From afb893c1574d341e955571b96e3a2d981061b6bf Mon Sep 17 00:00:00 2001 From: SiriusXT <1160925501@qq.com> Date: Thu, 22 Jun 2023 15:38:36 +0800 Subject: [PATCH] Fix bugs in toc and improve related codes --- src/public/app/widgets/highlights_list.js | 86 ++++++++++--------- src/public/app/widgets/toc.js | 4 +- .../widgets/type_widgets/content_widget.js | 4 +- .../options/text_notes/highlighted_text.js | 40 --------- .../options/text_notes/highlights_list.js | 40 +++++++++ src/routes/api/options.js | 2 +- src/services/options_init.js | 2 +- 7 files changed, 90 insertions(+), 88 deletions(-) delete mode 100644 src/public/app/widgets/type_widgets/options/text_notes/highlighted_text.js create mode 100644 src/public/app/widgets/type_widgets/options/text_notes/highlights_list.js diff --git a/src/public/app/widgets/highlights_list.js b/src/public/app/widgets/highlights_list.js index 4e88e9e45..8d32e7b36 100644 --- a/src/public/app/widgets/highlights_list.js +++ b/src/public/app/widgets/highlights_list.js @@ -10,20 +10,20 @@ import RightPanelWidget from "./right_panel_widget.js"; import options from "../services/options.js"; import OnClickButtonWidget from "./buttons/onclick_button.js"; -const TPL = `
+const TPL = `
- +
`; export default class HighlightsListWidget extends RightPanelWidget { @@ -55,61 +55,61 @@ export default class HighlightsListWidget extends RightPanelWidget { } get widgetTitle() { - return "Highlighted Text"; + return "Highlights List"; } isEnabled() { return super.isEnabled() && this.note.type === 'text' - && !this.noteContext.viewScope.highlightedTextTemporarilyHidden + && !this.noteContext.viewScope.highlightsListTemporarilyHidden && this.noteContext.viewScope.viewMode === 'default'; } async doRenderBody() { this.$body.empty().append($(TPL)); - this.$highlightsList = this.$body.find('.highlists-list'); - this.$body.find('.highlists-list-widget').append(this.closeHltButton.render()); + this.$highlightsList = this.$body.find('.highlights-list'); + this.$body.find('.highlights-list-widget').append(this.closeHltButton.render()); } async refreshWithNote(note) { - /* The reason for adding highlightedTextPreviousVisible 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. + /* The reason for adding highlightsListPreviousVisible is to record whether the previous state + 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, every time the note content is changed, highlighttext Widget will appear and then close immediately, because getHlt function will consume time */ - if (this.noteContext.viewScope.highlightedTextPreviousVisible) { + if (this.noteContext.viewScope.highlightsListPreviousVisible) { this.toggleInt(true); } else { 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.triggerCommand("reEvaluateRightPaneVisibility"); return; } - let $highlightsList = "", hltLiCount = -1; + let $highlightsList = "", hlLiCount = -1; // Check for type text unconditionally in case alwaysShowWidget is set if (this.note.type === 'text') { const {content} = await note.getNoteComplement(); - ({$highlightsList, hltLiCount} = this.getHighlightList(content, optionsHlt)); + ({$highlightsList, hlLiCount} = this.getHighlightList(content, optionsHighlightsList)); } this.$highlightsList.empty().append($highlightsList); - if (hltLiCount > 0) { + if (hlLiCount > 0) { this.toggleInt(true); - this.noteContext.viewScope.highlightedTextPreviousVisible = true; + this.noteContext.viewScope.highlightsListPreviousVisible = true; } else { this.toggleInt(false); - this.noteContext.viewScope.highlightedTextPreviousVisible = false; + this.noteContext.viewScope.highlightsListPreviousVisible = false; } this.triggerCommand("reEvaluateRightPaneVisibility"); } - getHighlightList(content, optionsHlt) { + getHighlightList(content, optionsHighlightsList) { // matches a span containing background-color const regex1 = /]*style\s*=\s*[^>]*background-color:[^>]*?>[\s\S]*?<\/span>/gi; // matches a span containing color @@ -120,27 +120,27 @@ export default class HighlightsListWidget extends RightPanelWidget { const regex4 = /[\s\S]*?<\/strong>/gi; // match underline const regex5 = /[\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 let findSubStr = "", combinedRegexStr = ""; - if (optionsHlt.includes("bgColor")) { - findSubStr += `,span[style*="background-color"]`; + if (optionsHighlightsList.includes("bgColor")) { + findSubStr += `,span[style*="background-color"]:not(section.include-note span[style*="background-color"])`; combinedRegexStr += `|${regex1.source}`; } - if (optionsHlt.includes("color")) { - findSubStr += `,span[style*="color"]`; + if (optionsHighlightsList.includes("color")) { + findSubStr += `,span[style*="color"]:not(section.include-note span[style*="color"])`; combinedRegexStr += `|${regex2.source}`; } - if (optionsHlt.includes("italic")) { - findSubStr += `,i`; + if (optionsHighlightsList.includes("italic")) { + findSubStr += `,i:not(section.include-note i)`; combinedRegexStr += `|${regex3.source}`; } - if (optionsHlt.indexOf("bold")) { - findSubStr += `,strong`; + if (optionsHighlightsList.includes("bold")) { + findSubStr += `,strong:not(section.include-note strong)`; combinedRegexStr += `|${regex4.source}`; } - if (optionsHlt.includes("underline")) { - findSubStr += `,u`; + if (optionsHighlightsList.includes("underline")) { + findSubStr += `,u:not(section.include-note u)`; combinedRegexStr += `|${regex5.source}`; } @@ -148,7 +148,7 @@ export default class HighlightsListWidget extends RightPanelWidget { combinedRegexStr = `(` + combinedRegexStr.substring(1) + `)`; const combinedRegex = new RegExp(combinedRegexStr, 'gi'); const $highlightsList = $("
    "); - let prevEndIndex = -1, hltLiCount = 0; + let prevEndIndex = -1, hlLiCount = 0; for (let match = null, hltIndex = 0; ((match = combinedRegex.exec(content)) !== null); hltIndex++) { const subHtml = match[0]; const startIndex = match.index; @@ -158,16 +158,18 @@ export default class HighlightsListWidget extends RightPanelWidget { $highlightsList.children().last().append(subHtml); } else { // TODO: can't be done with $(subHtml).text()? - const hasText = [...subHtml.matchAll(/(?<=^|>)[^><]+?(?=<|$)/g)].map(matchTmp => matchTmp[0]).join('').trim(); + //Can’t 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) { $highlightsList.append( $('
  1. ') .html(subHtml) - .on("click", () => this.jumpToHighlightedText(findSubStr, hltIndex)) + .on("click", () => this.jumpToHighlightsList(findSubStr, hltIndex)) ); - hltLiCount++; + hlLiCount++; } else { // hide li if its text content is empty continue; @@ -177,11 +179,11 @@ export default class HighlightsListWidget extends RightPanelWidget { } return { $highlightsList, - hltLiCount + hlLiCount }; } - async jumpToHighlightedText(findSubStr, itemIndex) { + async jumpToHighlightsList(findSubStr, itemIndex) { const isReadOnly = await this.noteContext.isReadOnly(); let targetElement; if (isReadOnly) { @@ -224,7 +226,7 @@ export default class HighlightsListWidget extends RightPanelWidget { } async closeHltCommand() { - this.noteContext.viewScope.highlightedTextTemporarilyHidden = true; + this.noteContext.viewScope.highlightsListTemporarilyHidden = true; await this.refresh(); this.triggerCommand('reEvaluateRightPaneVisibility'); } @@ -245,13 +247,13 @@ class CloseHltButton extends OnClickButtonWidget { super(); this.icon("bx-x") - .title("Close HighlightedTextWidget") + .title("Close HighlightsListWidget") .titlePlacement("bottom") .onClick((widget, e) => { e.stopPropagation(); widget.triggerCommand("closeHlt"); }) - .class("icon-action close-highlists-list"); + .class("icon-action close-highlights-list"); } } diff --git a/src/public/app/widgets/toc.js b/src/public/app/widgets/toc.js index 8d66b3294..5253dc112 100644 --- a/src/public/app/widgets/toc.js +++ b/src/public/app/widgets/toc.js @@ -191,7 +191,7 @@ export default class TocWidget extends RightPanelWidget { if (isReadOnly) { 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) { headingElement.scrollIntoView({ behavior: "smooth" }); @@ -210,7 +210,7 @@ export default class TocWidget extends RightPanelWidget { // navigate (note that the TOC rendering and other TOC // entries' navigation could be wrong too) 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' }); } diff --git a/src/public/app/widgets/type_widgets/content_widget.js b/src/public/app/widgets/type_widgets/content_widget.js index f7a4846bd..477e0571e 100644 --- a/src/public/app/widgets/type_widgets/content_widget.js +++ b/src/public/app/widgets/type_widgets/content_widget.js @@ -7,7 +7,7 @@ import MaxContentWidthOptions from "./options/appearance/max_content_width.js"; import KeyboardShortcutsOptions from "./options/shortcuts.js"; import HeadingStyleOptions from "./options/text_notes/heading_style.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 VimKeyBindingsOptions from "./options/code_notes/vim_key_bindings.js"; import WrapLinesOptions from "./options/code_notes/wrap_lines.js"; @@ -62,7 +62,7 @@ const CONTENT_WIDGETS = { _optionsTextNotes: [ HeadingStyleOptions, TableOfContentsOptions, - HighlightedTextOptions, + HighlightsListOptions, TextAutoReadOnlySizeOptions ], _optionsCodeNotes: [ diff --git a/src/public/app/widgets/type_widgets/options/text_notes/highlighted_text.js b/src/public/app/widgets/type_widgets/options/text_notes/highlighted_text.js deleted file mode 100644 index 72d0ccd49..000000000 --- a/src/public/app/widgets/type_widgets/options/text_notes/highlighted_text.js +++ /dev/null @@ -1,40 +0,0 @@ -import OptionsWidget from "../options_widget.js"; - -const TPL = ` -
    -

    Highlighted Text

    - -

    You can customize the highlighted text displayed in the right panel:

    - -
    - - - - - -
-`; - -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); - } - }); - } -} diff --git a/src/public/app/widgets/type_widgets/options/text_notes/highlights_list.js b/src/public/app/widgets/type_widgets/options/text_notes/highlights_list.js new file mode 100644 index 000000000..5e2a2595d --- /dev/null +++ b/src/public/app/widgets/type_widgets/options/text_notes/highlights_list.js @@ -0,0 +1,40 @@ +import OptionsWidget from "../options_widget.js"; + +const TPL = ` +
+

Highlights List

+ +

You can customize the highlights list displayed in the right panel:

+ +
+ + + + + + +`; + +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); + } + }); + } +} diff --git a/src/routes/api/options.js b/src/routes/api/options.js index 4d9ee77a2..ba8bc7943 100644 --- a/src/routes/api/options.js +++ b/src/routes/api/options.js @@ -60,7 +60,7 @@ const ALLOWED_OPTIONS = new Set([ 'compressImages', 'downloadImagesAutomatically', 'minTocHeadings', - 'highlightedText', + 'highlightsList', 'checkForUpdates', 'disableTray', 'customSearchEngineName', diff --git a/src/services/options_init.js b/src/services/options_init.js index 358403214..b8026d371 100644 --- a/src/services/options_init.js +++ b/src/services/options_init.js @@ -87,7 +87,7 @@ const defaultOptions = [ { name: 'compressImages', value: 'true', isSynced: true }, { name: 'downloadImagesAutomatically', value: 'true', 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: 'disableTray', value: 'false', isSynced: false }, { name: 'customSearchEngineName', value: 'Duckduckgo', isSynced: false },