Fix bugs in toc and improve related codes

This commit is contained in:
SiriusXT 2023-06-22 15:38:36 +08:00
parent bbe3f436d3
commit afb893c157
7 changed files with 90 additions and 88 deletions

View File

@ -10,20 +10,20 @@ import RightPanelWidget from "./right_panel_widget.js";
import options from "../services/options.js"; import options from "../services/options.js";
import OnClickButtonWidget from "./buttons/onclick_button.js"; import OnClickButtonWidget from "./buttons/onclick_button.js";
const TPL = `<div class="highlists-list-widget"> const TPL = `<div class="highlights-list-widget">
<style> <style>
.highlists-list-widget { .highlights-list-widget {
padding: 10px; padding: 10px;
contain: none; contain: none;
overflow: auto; overflow: auto;
position: relative; position: relative;
} }
.highlists-list > ol { .highlights-list > ol {
padding-left: 20px; padding-left: 20px;
} }
.highlists-list li { .highlights-list li {
cursor: pointer; cursor: pointer;
margin-bottom: 3px; margin-bottom: 3px;
text-align: justify; text-align: justify;
@ -32,18 +32,18 @@ const TPL = `<div class="highlists-list-widget">
hyphens: auto; hyphens: auto;
} }
.highlists-list li:hover { .highlights-list li:hover {
font-weight: bold; font-weight: bold;
} }
.close-highlists-list { .close-highlights-list {
position: absolute; position: absolute;
top: 2px; top: 2px;
right: 2px; right: 2px;
} }
</style> </style>
<span class="highlists-list"></span> <span class="highlights-list"></span>
</div>`; </div>`;
export default class HighlightsListWidget extends RightPanelWidget { export default class HighlightsListWidget extends RightPanelWidget {
@ -55,61 +55,61 @@ export default class HighlightsListWidget extends RightPanelWidget {
} }
get widgetTitle() { get widgetTitle() {
return "Highlighted Text"; return "Highlights List";
} }
isEnabled() { isEnabled() {
return super.isEnabled() return super.isEnabled()
&& this.note.type === 'text' && this.note.type === 'text'
&& !this.noteContext.viewScope.highlightedTextTemporarilyHidden && !this.noteContext.viewScope.highlightsListTemporarilyHidden
&& this.noteContext.viewScope.viewMode === 'default'; && this.noteContext.viewScope.viewMode === 'default';
} }
async doRenderBody() { async doRenderBody() {
this.$body.empty().append($(TPL)); this.$body.empty().append($(TPL));
this.$highlightsList = this.$body.find('.highlists-list'); this.$highlightsList = this.$body.find('.highlights-list');
this.$body.find('.highlists-list-widget').append(this.closeHltButton.render()); this.$body.find('.highlights-list-widget').append(this.closeHltButton.render());
} }
async refreshWithNote(note) { async refreshWithNote(note) {
/* The reason for adding highlightedTextPreviousVisible is to record whether the previous state /* The reason for adding highlightsListPreviousVisible is to record whether the previous state
of the highlightedText is hidden or displayed, and then let it be displayed/hidden at the initial time. of the highlightsList is hidden or displayed, and then let it be displayed/hidden at the initial time.
If there is no such value, when the right panel needs to display toc but not highlighttext, If there is no such value, when the right panel needs to display toc but not highlighttext,
every time the note content is changed, highlighttext Widget will appear and then close immediately, every time the note content is changed, highlighttext Widget will appear and then close immediately,
because getHlt function will consume time */ because getHlt function will consume time */
if (this.noteContext.viewScope.highlightedTextPreviousVisible) { if (this.noteContext.viewScope.highlightsListPreviousVisible) {
this.toggleInt(true); this.toggleInt(true);
} else { } else {
this.toggleInt(false); this.toggleInt(false);
} }
const optionsHlt = JSON.parse(options.get('highlightedText')); const optionsHighlightsList = JSON.parse(options.get('highlightsList'));
if (note.isLabelTruthy('hideHighlightWidget') || !optionsHlt) { if (note.isLabelTruthy('hideHighlightWidget') || !optionsHighlightsList) {
this.toggleInt(false); this.toggleInt(false);
this.triggerCommand("reEvaluateRightPaneVisibility"); this.triggerCommand("reEvaluateRightPaneVisibility");
return; return;
} }
let $highlightsList = "", hltLiCount = -1; let $highlightsList = "", hlLiCount = -1;
// Check for type text unconditionally in case alwaysShowWidget is set // Check for type text unconditionally in case alwaysShowWidget is set
if (this.note.type === 'text') { if (this.note.type === 'text') {
const {content} = await note.getNoteComplement(); const {content} = await note.getNoteComplement();
({$highlightsList, hltLiCount} = this.getHighlightList(content, optionsHlt)); ({$highlightsList, hlLiCount} = this.getHighlightList(content, optionsHighlightsList));
} }
this.$highlightsList.empty().append($highlightsList); this.$highlightsList.empty().append($highlightsList);
if (hltLiCount > 0) { if (hlLiCount > 0) {
this.toggleInt(true); this.toggleInt(true);
this.noteContext.viewScope.highlightedTextPreviousVisible = true; this.noteContext.viewScope.highlightsListPreviousVisible = true;
} else { } else {
this.toggleInt(false); this.toggleInt(false);
this.noteContext.viewScope.highlightedTextPreviousVisible = false; this.noteContext.viewScope.highlightsListPreviousVisible = false;
} }
this.triggerCommand("reEvaluateRightPaneVisibility"); this.triggerCommand("reEvaluateRightPaneVisibility");
} }
getHighlightList(content, optionsHlt) { getHighlightList(content, optionsHighlightsList) {
// matches a span containing background-color // matches a span containing background-color
const regex1 = /<span[^>]*style\s*=\s*[^>]*background-color:[^>]*?>[\s\S]*?<\/span>/gi; const regex1 = /<span[^>]*style\s*=\s*[^>]*background-color:[^>]*?>[\s\S]*?<\/span>/gi;
// matches a span containing color // matches a span containing color
@ -120,27 +120,27 @@ export default class HighlightsListWidget extends RightPanelWidget {
const regex4 = /<strong>[\s\S]*?<\/strong>/gi; const regex4 = /<strong>[\s\S]*?<\/strong>/gi;
// match underline // match underline
const regex5 = /<u>[\s\S]*?<\/u>/g; const regex5 = /<u>[\s\S]*?<\/u>/g;
// Possible values in optionsHlt '["bold","italic","underline","color","bgColor"]' // Possible values in optionsHighlightsList '["bold","italic","underline","color","bgColor"]'
// element priority span>i>strong>u // element priority span>i>strong>u
let findSubStr = "", combinedRegexStr = ""; let findSubStr = "", combinedRegexStr = "";
if (optionsHlt.includes("bgColor")) { if (optionsHighlightsList.includes("bgColor")) {
findSubStr += `,span[style*="background-color"]`; findSubStr += `,span[style*="background-color"]:not(section.include-note span[style*="background-color"])`;
combinedRegexStr += `|${regex1.source}`; combinedRegexStr += `|${regex1.source}`;
} }
if (optionsHlt.includes("color")) { if (optionsHighlightsList.includes("color")) {
findSubStr += `,span[style*="color"]`; findSubStr += `,span[style*="color"]:not(section.include-note span[style*="color"])`;
combinedRegexStr += `|${regex2.source}`; combinedRegexStr += `|${regex2.source}`;
} }
if (optionsHlt.includes("italic")) { if (optionsHighlightsList.includes("italic")) {
findSubStr += `,i`; findSubStr += `,i:not(section.include-note i)`;
combinedRegexStr += `|${regex3.source}`; combinedRegexStr += `|${regex3.source}`;
} }
if (optionsHlt.indexOf("bold")) { if (optionsHighlightsList.includes("bold")) {
findSubStr += `,strong`; findSubStr += `,strong:not(section.include-note strong)`;
combinedRegexStr += `|${regex4.source}`; combinedRegexStr += `|${regex4.source}`;
} }
if (optionsHlt.includes("underline")) { if (optionsHighlightsList.includes("underline")) {
findSubStr += `,u`; findSubStr += `,u:not(section.include-note u)`;
combinedRegexStr += `|${regex5.source}`; combinedRegexStr += `|${regex5.source}`;
} }
@ -148,7 +148,7 @@ export default class HighlightsListWidget extends RightPanelWidget {
combinedRegexStr = `(` + combinedRegexStr.substring(1) + `)`; combinedRegexStr = `(` + combinedRegexStr.substring(1) + `)`;
const combinedRegex = new RegExp(combinedRegexStr, 'gi'); const combinedRegex = new RegExp(combinedRegexStr, 'gi');
const $highlightsList = $("<ol>"); const $highlightsList = $("<ol>");
let prevEndIndex = -1, hltLiCount = 0; let prevEndIndex = -1, hlLiCount = 0;
for (let match = null, hltIndex = 0; ((match = combinedRegex.exec(content)) !== null); hltIndex++) { for (let match = null, hltIndex = 0; ((match = combinedRegex.exec(content)) !== null); hltIndex++) {
const subHtml = match[0]; const subHtml = match[0];
const startIndex = match.index; const startIndex = match.index;
@ -158,16 +158,18 @@ export default class HighlightsListWidget extends RightPanelWidget {
$highlightsList.children().last().append(subHtml); $highlightsList.children().last().append(subHtml);
} else { } else {
// TODO: can't be done with $(subHtml).text()? // TODO: can't be done with $(subHtml).text()?
const hasText = [...subHtml.matchAll(/(?<=^|>)[^><]+?(?=<|$)/g)].map(matchTmp => matchTmp[0]).join('').trim(); //Cant remember why regular expressions are used here, but modified to $(subHtml).text() works as expected
//const hasText = [...subHtml.matchAll(/(?<=^|>)[^><]+?(?=<|$)/g)].map(matchTmp => matchTmp[0]).join('').trim();
const hasText = $(subHtml).text().trim();
if (hasText) { if (hasText) {
$highlightsList.append( $highlightsList.append(
$('<li>') $('<li>')
.html(subHtml) .html(subHtml)
.on("click", () => this.jumpToHighlightedText(findSubStr, hltIndex)) .on("click", () => this.jumpToHighlightsList(findSubStr, hltIndex))
); );
hltLiCount++; hlLiCount++;
} else { } else {
// hide li if its text content is empty // hide li if its text content is empty
continue; continue;
@ -177,11 +179,11 @@ export default class HighlightsListWidget extends RightPanelWidget {
} }
return { return {
$highlightsList, $highlightsList,
hltLiCount hlLiCount
}; };
} }
async jumpToHighlightedText(findSubStr, itemIndex) { async jumpToHighlightsList(findSubStr, itemIndex) {
const isReadOnly = await this.noteContext.isReadOnly(); const isReadOnly = await this.noteContext.isReadOnly();
let targetElement; let targetElement;
if (isReadOnly) { if (isReadOnly) {
@ -224,7 +226,7 @@ export default class HighlightsListWidget extends RightPanelWidget {
} }
async closeHltCommand() { async closeHltCommand() {
this.noteContext.viewScope.highlightedTextTemporarilyHidden = true; this.noteContext.viewScope.highlightsListTemporarilyHidden = true;
await this.refresh(); await this.refresh();
this.triggerCommand('reEvaluateRightPaneVisibility'); this.triggerCommand('reEvaluateRightPaneVisibility');
} }
@ -245,13 +247,13 @@ class CloseHltButton extends OnClickButtonWidget {
super(); super();
this.icon("bx-x") this.icon("bx-x")
.title("Close HighlightedTextWidget") .title("Close HighlightsListWidget")
.titlePlacement("bottom") .titlePlacement("bottom")
.onClick((widget, e) => { .onClick((widget, e) => {
e.stopPropagation(); e.stopPropagation();
widget.triggerCommand("closeHlt"); widget.triggerCommand("closeHlt");
}) })
.class("icon-action close-highlists-list"); .class("icon-action close-highlights-list");
} }
} }

View File

@ -191,7 +191,7 @@ export default class TocWidget extends RightPanelWidget {
if (isReadOnly) { if (isReadOnly) {
const $container = await this.noteContext.getContentElement(); const $container = await this.noteContext.getContentElement();
const headingElement = $container.find(":header")[headingIndex]; const headingElement = $container.find(":header:not(section.include-note :header)")[headingIndex];
if (headingElement != null) { if (headingElement != null) {
headingElement.scrollIntoView({ behavior: "smooth" }); headingElement.scrollIntoView({ behavior: "smooth" });
@ -210,7 +210,7 @@ export default class TocWidget extends RightPanelWidget {
// navigate (note that the TOC rendering and other TOC // navigate (note that the TOC rendering and other TOC
// entries' navigation could be wrong too) // entries' navigation could be wrong too)
if (headingNode != null) { if (headingNode != null) {
$(textEditor.editing.view.domRoots.values().next().value).find(':header')[headingIndex].scrollIntoView({ $(textEditor.editing.view.domRoots.values().next().value).find(':header:not(section.include-note :header)')[headingIndex].scrollIntoView({
behavior: 'smooth' behavior: 'smooth'
}); });
} }

View File

@ -7,7 +7,7 @@ import MaxContentWidthOptions from "./options/appearance/max_content_width.js";
import KeyboardShortcutsOptions from "./options/shortcuts.js"; import KeyboardShortcutsOptions from "./options/shortcuts.js";
import HeadingStyleOptions from "./options/text_notes/heading_style.js"; import HeadingStyleOptions from "./options/text_notes/heading_style.js";
import TableOfContentsOptions from "./options/text_notes/table_of_contents.js"; import TableOfContentsOptions from "./options/text_notes/table_of_contents.js";
import HighlightedTextOptions from "./options/text_notes/highlighted_text.js"; import HighlightsListOptions from "./options/text_notes/highlights_list.js";
import TextAutoReadOnlySizeOptions from "./options/text_notes/text_auto_read_only_size.js"; import TextAutoReadOnlySizeOptions from "./options/text_notes/text_auto_read_only_size.js";
import VimKeyBindingsOptions from "./options/code_notes/vim_key_bindings.js"; import VimKeyBindingsOptions from "./options/code_notes/vim_key_bindings.js";
import WrapLinesOptions from "./options/code_notes/wrap_lines.js"; import WrapLinesOptions from "./options/code_notes/wrap_lines.js";
@ -62,7 +62,7 @@ const CONTENT_WIDGETS = {
_optionsTextNotes: [ _optionsTextNotes: [
HeadingStyleOptions, HeadingStyleOptions,
TableOfContentsOptions, TableOfContentsOptions,
HighlightedTextOptions, HighlightsListOptions,
TextAutoReadOnlySizeOptions TextAutoReadOnlySizeOptions
], ],
_optionsCodeNotes: [ _optionsCodeNotes: [

View File

@ -1,40 +0,0 @@
import OptionsWidget from "../options_widget.js";
const TPL = `
<div class="options-section">
<h4>Highlighted Text</h4>
<p>You can customize the highlighted text displayed in the right panel:</p>
</div>
<label><input type="checkbox" class="highlighted-text-check" value="bold"> Bold font &nbsp;</label>
<label><input type="checkbox" class="highlighted-text-check" value="italic"> Italic font &nbsp;</label>
<label><input type="checkbox" class="highlighted-text-check" value="underline"> Underlined font &nbsp;</label>
<label><input type="checkbox" class="highlighted-text-check" value="color"> Font with color &nbsp;</label>
<label><input type="checkbox" class="highlighted-text-check" value="bgColor"> Font with background color &nbsp;</label>
</div>
</div>`;
export default class HighlightedTextOptions extends OptionsWidget {
doRender() {
this.$widget = $(TPL);
this.$hlt = this.$widget.find("input.highlighted-text-check");
this.$hlt.on('change', () => {
const hltVals = this.$widget.find('input.highlighted-text-check[type="checkbox"]:checked').map(function () {
return this.value;
}).get();
this.updateOption('highlightedText', JSON.stringify(hltVals));
});
}
async optionsLoaded(options) {
const hltVals = JSON.parse(options.highlightedText);
this.$widget.find('input.highlighted-text-check[type="checkbox"]').each(function () {
if ($.inArray($(this).val(), hltVals) !== -1) {
$(this).prop("checked", true);
} else {
$(this).prop("checked", false);
}
});
}
}

View File

@ -0,0 +1,40 @@
import OptionsWidget from "../options_widget.js";
const TPL = `
<div class="options-section">
<h4>Highlights List</h4>
<p>You can customize the highlights list displayed in the right panel:</p>
</div>
<label><input type="checkbox" class="highlights-list-check" value="bold"> Bold font &nbsp;</label>
<label><input type="checkbox" class="highlights-list-check" value="italic"> Italic font &nbsp;</label>
<label><input type="checkbox" class="highlights-list-check" value="underline"> Underlined font &nbsp;</label>
<label><input type="checkbox" class="highlights-list-check" value="color"> Font with color &nbsp;</label>
<label><input type="checkbox" class="highlights-list-check" value="bgColor"> Font with background color &nbsp;</label>
</div>
</div>`;
export default class HighlightsListOptions extends OptionsWidget {
doRender() {
this.$widget = $(TPL);
this.$hlt = this.$widget.find("input.highlights-list-check");
this.$hlt.on('change', () => {
const hltVals = this.$widget.find('input.highlights-list-check[type="checkbox"]:checked').map(function () {
return this.value;
}).get();
this.updateOption('highlightsList', JSON.stringify(hltVals));
});
}
async optionsLoaded(options) {
const hltVals = JSON.parse(options.highlightsList);
this.$widget.find('input.highlights-list-check[type="checkbox"]').each(function () {
if ($.inArray($(this).val(), hltVals) !== -1) {
$(this).prop("checked", true);
} else {
$(this).prop("checked", false);
}
});
}
}

View File

@ -60,7 +60,7 @@ const ALLOWED_OPTIONS = new Set([
'compressImages', 'compressImages',
'downloadImagesAutomatically', 'downloadImagesAutomatically',
'minTocHeadings', 'minTocHeadings',
'highlightedText', 'highlightsList',
'checkForUpdates', 'checkForUpdates',
'disableTray', 'disableTray',
'customSearchEngineName', 'customSearchEngineName',

View File

@ -87,7 +87,7 @@ const defaultOptions = [
{ name: 'compressImages', value: 'true', isSynced: true }, { name: 'compressImages', value: 'true', isSynced: true },
{ name: 'downloadImagesAutomatically', value: 'true', isSynced: true }, { name: 'downloadImagesAutomatically', value: 'true', isSynced: true },
{ name: 'minTocHeadings', value: '5', isSynced: true }, { name: 'minTocHeadings', value: '5', isSynced: true },
{ name: 'highlightedText', value: '["bold","italic","underline","color","bgColor"]', isSynced: true }, { name: 'highlightsList', value: '["bold","italic","underline","color","bgColor"]', isSynced: true },
{ name: 'checkForUpdates', value: 'true', isSynced: true }, { name: 'checkForUpdates', value: 'true', isSynced: true },
{ name: 'disableTray', value: 'false', isSynced: false }, { name: 'disableTray', value: 'false', isSynced: false },
{ name: 'customSearchEngineName', value: 'Duckduckgo', isSynced: false }, { name: 'customSearchEngineName', value: 'Duckduckgo', isSynced: false },