diff --git a/src/public/app/widgets/highlighted_text.js b/src/public/app/widgets/highlighted_text.js index b6ced3124..f13bdc6b8 100644 --- a/src/public/app/widgets/highlighted_text.js +++ b/src/public/app/widgets/highlighted_text.js @@ -81,25 +81,20 @@ export default class HighlightTextWidget extends RightPanelWidget { } const hltLabel = note.getLabel('hideHighlightWidget'); - let optionsHltColors = JSON.parse(options.get('highlightedTextColors')); - let optionsHltBgColors = JSON.parse(options.get('highlightedTextBgColors')); + const optionsHlt = JSON.parse(options.get('highlightedText')); - if (hltLabel?.value == "" || hltLabel?.value === "true" || (optionsHltColors=="" && optionsHltBgColors=="")) { + if (hltLabel?.value == "" || hltLabel?.value === "true" || optionsHlt == "") { this.toggleInt(false); this.triggerCommand("reEvaluateRightPaneVisibility"); return; } let $hlt = "", hltLiCount = -1; - //Obtained by `textEditor.config.get('fontColor.colors'), but this command can only be used in edit mode, so it is directly saved here - const colorToValDic = { "Black": "hsl(0,0%,0%)", "Dim grey": "hsl(0,0%,30%)", "Grey": "hsl(0,0%,60%)", "Light grey": "hsl(0,0%,90%)", "White": "hsl(0,0%,100%)", "Red": "hsl(0,75%,60%)", "Orange": "hsl(30,75%,60%)", "Yellow": "hsl(60,75%,60%)", "Light green": "hsl(90,75%,60%)", "Green": "hsl(120,75%,60%)", "Aquamarine": "hsl(150,75%,60%)", "Turquoise": "hsl(180,75%,60%)", "Light blue": "hsl(210,75%,60%)", "Blue": "hsl(240,75%,60%)", "Purple": "hsl(270,75%,60%)" } - const optionsHltColorsVal = optionsHltColors.map(color => colorToValDic[color]); - const optionsHltBgColorsVal = optionsHltBgColors.map(color => colorToValDic[color]); // Check for type text unconditionally in case alwaysShowWidget is set if (this.note.type === 'text') { const { content } = await note.getNoteComplement(); //hltColors/hltBgColors are the colors/background-color that appear in notes and in options - ({ $hlt, hltLiCount } = await this.getHlt(content, optionsHltColorsVal, optionsHltBgColorsVal)); + ({ $hlt, hltLiCount } = await this.getHlt(content, optionsHlt)); } this.$hlt.html($hlt); if ([undefined, "false"].includes(hltLabel?.value) && hltLiCount > 0) { @@ -110,90 +105,115 @@ export default class HighlightTextWidget extends RightPanelWidget { this.noteContext.viewScope.highlightedTextTemporarilyHiddenPrevious = false; } - this.triggerCommand("reEvaluateRightPaneVisibility"); } /** * Builds a jquery table of helight text. */ - getHlt(html, optionsHltColorsVal, optionsHltBgColorsVal) { - const hltTagsRegex = /]*(?:background-color|color):[^;>]+;[^>]*>(.*?)<\/span>/gi; - let prevEndIndex = -1; - let prevLiDisplay = false; - const $hlt = $("
    "); - let hltLiCount = 0; - for (let match = null, hltIndex = 0; ((match = hltTagsRegex.exec(html)) !== null); hltIndex++) { - var spanHtml = match[0]; - const styleString = match[0].match(/style="(.*?)"/)[1]; - const text = match[1]; - const startIndex = match.index; - const endIndex = hltTagsRegex.lastIndex - 1; - var $li = $('
  1. '); - - const styles = styleString - .split(';') - .filter(item => item.includes('background-color') || item.includes('color')) - .map(item => item.trim()); - - for (let stylesIndex = 0; stylesIndex < styles.length; stylesIndex++) { - var [color, colorVal] = styles[stylesIndex].split(':'); - colorVal = colorVal.replace(/\s+/g, ''); - if (color == "color" && optionsHltColorsVal.indexOf(colorVal) >= 0) { - $li.html(spanHtml) - hltLiCount++; - - } - else if (color == "background-color" && optionsHltBgColorsVal.indexOf(colorVal) >= 0) { - - //When you need to add a background color, in order to make the display more comfortable, change the background color to Translucent - const spanHtmlRegex = /background-color:\s*(hsl|rgb)\((\d{1,3}),(\d{1,3}%?),(\d{1,3}%?)\)/i; - let spanHtmlMatch = spanHtml.match(spanHtmlRegex); - if (spanHtmlMatch && spanHtmlMatch.length > 4) { - let newColorValue = `${spanHtmlMatch[1]}a(${spanHtmlMatch[2]},${spanHtmlMatch[3]},${spanHtmlMatch[4]},0.5)`; - spanHtml = spanHtml.replace(spanHtmlRegex, `background-color: ${newColorValue}`); - } - $li.html(spanHtml) - hltLiCount++; - - } else { - $li.css("display", "none"); - } - } - if ($li.css("display")!="none"){ - if (prevEndIndex != -1 && startIndex === prevEndIndex + 1 && prevLiDisplay == true) { - $hlt.children().last().append($li.html()); - } else { - if ($li.text().trim() == "") { $li.css("display", "none"); } - $li.on("click", () => this.jumpToHlt(hltIndex)); - $hlt.append($li); - } - } - - prevEndIndex = endIndex; - prevLiDisplay = $li.css("display")!="none"; + getHlt(html, optionsHlt) { + // element priority: span>i>strong>u + // matches a span containing background-color + const regex1 = /]*style\s*=\s*[^>]*background-color:[^>]*?>[\s\S]*?<\/span>/gi; + // matches a span containing color + const regex2 = /]*style\s*=\s*[^>]*[^-]color:[^>]*?>[\s\S]*?<\/span>/gi; + // match italics + const regex3 = /[\s\S]*?<\/i>/gi; + // match bold + const regex4 = /[\s\S]*?<\/strong>/gi; + // match underline + const regex5 = /[\s\S]*?<\/u>/g; + // Possible values in optionsHlt: '["bold","italic","underline","color","bgColor"]' + let findSubStr="", combinedRegexStr = ""; + if (optionsHlt.indexOf("bgColor") >= 0){ + findSubStr+=`,span[style*="background-color"]`; + combinedRegexStr+=`|${regex1.source}`; } + if (optionsHlt.indexOf("color") >= 0){ + findSubStr+=`,span[style*="color"]`; + combinedRegexStr+=`|${regex2.source}`; + } + if (optionsHlt.indexOf("italic") >= 0){ + findSubStr+=`,i`; + combinedRegexStr+=`|${regex3.source}`; + } + if (optionsHlt.indexOf("bold") >= 0){ + findSubStr+=`,strong`; + combinedRegexStr+=`|${regex4.source}`; + } + if (optionsHlt.indexOf("underline") >= 0){ + findSubStr+=`,u`; + combinedRegexStr+=`|${regex5.source}`; + } + + findSubStr = findSubStr.substring(1) + combinedRegexStr = `(` + combinedRegexStr.substring(1) + `)`; + const combinedRegex = new RegExp(combinedRegexStr, 'gi'); + var $hlt = $("
      "); + var hltLiCount = 0; + let prevEndIndex = -1; + for (let match = null, hltIndex = 0; ((match = combinedRegex.exec(html)) !== null); hltIndex++) { + var subHtml = match[0]; + const startIndex = match.index; + const endIndex = combinedRegex.lastIndex; + hltLiCount++; + if (prevEndIndex != -1 && startIndex === prevEndIndex) { + $hlt.children().last().append(subHtml); + } else { + var $li = $('
    1. '); + $li.html(subHtml); + if ($li.text().trim() == "") { $li.css("display", "none"); } + $li.on("click", () => this.jumpToHlt(findSubStr,hltIndex)); + $hlt.append($li); + } + prevEndIndex = endIndex; + } + return { $hlt, hltLiCount }; } - - async jumpToHlt(hltIndex) { + async jumpToHlt(findSubStr,hltIndex) { const isReadOnly = await this.noteContext.isReadOnly(); + let targetElement; if (isReadOnly) { const $container = await this.noteContext.getContentElement(); - const hltElement = $container.find(`span[style*="background-color"],span[style*="color"]`)[hltIndex]; - - if (hltElement != null) { - hltElement.scrollIntoView({ behavior: "smooth", block: "center" }); - } + targetElement=$container.find(findSubStr).filter(function() { + if (findSubStr.indexOf("color")>=0 && findSubStr.indexOf("background-color")<0){ + let color = this.style.color; + return $(this).prop('tagName')=="SPAN" && color==""?false:true; + }else{ + return true; + } + }).filter(function() { + return $(this).parent(findSubStr).length === 0 + && $(this).parent().parent(findSubStr).length === 0 + && $(this).parent().parent().parent(findSubStr).length === 0 + && $(this).parent().parent().parent().parent(findSubStr).length === 0; + }) } else { const textEditor = await this.noteContext.getTextEditor(); - $(textEditor.editing.view.domRoots.values().next().value).find(`span[style*="background-color"],span[style*="color"]`)[hltIndex].scrollIntoView({ - behavior: "smooth", block: "center" - }); + targetElement=$(textEditor.editing.view.domRoots.values().next().value).find(findSubStr).filter(function() { + // When finding span[style*="color"] but not looking for span[style*="background-color"], + // the background-color error will be regarded as color, so it needs to be filtered + if (findSubStr.indexOf("color")>=0 && findSubStr.indexOf("background-color")<0){ + let color = this.style.color; + return $(this).prop('tagName')=="SPAN" && color==""?false:true; + }else{ + return true; + } + }).filter(function() { + //Need to filter out the child elements of the element that has been found + return $(this).parent(findSubStr).length === 0 + && $(this).parent().parent(findSubStr).length === 0 + && $(this).parent().parent().parent(findSubStr).length === 0 + && $(this).parent().parent().parent().parent(findSubStr).length === 0; + }) } + targetElement[hltIndex].scrollIntoView({ + behavior: "smooth", block: "center" + }); } async closeHltCommand() { 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 index 8af3503f8..90a47b616 100644 --- 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 @@ -1,83 +1,34 @@ import OptionsWidget from "../options_widget.js"; const TPL = ` -
      - +

      Highlighted Text

      - - You can customize the highlighted text displayed in the right panel: -
      Text color:
      - - - - - - - - - - - - - - - -
      Background color:
      - - - - - - - - - - - - - - - + + You can customize the highlighted text displayed in the right panel:
      + + + + + +
      `; export default class HighlightedTextOptions extends OptionsWidget { doRender() { this.$widget = $(TPL); - this.$hltColors = this.$widget.find(".highlighted-text-color"); - this.$hltColors.on('change', () => { - const hltColorVals=this.$widget.find('input.highlighted-text-color[type="checkbox"]:checked').map(function() { + this.$hlt = this.$widget.find(".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('highlightedTextColors', JSON.stringify(hltColorVals)); - - }); - this.$hltBgColors = this.$widget.find(".highlighted-text-background-color"); - this.$hltBgColors.on('change', () =>{ - const hltBgColorVals=this.$widget.find('input.highlighted-text-background-color[type="checkbox"]:checked').map(function() { - return this.value; - }).get(); - this.updateOption('highlightedTextBgColors', JSON.stringify(hltBgColorVals)); - }); - + this.updateOption('highlightedText', JSON.stringify(hltVals)); + }); } async optionsLoaded(options) { - const hltColorVals=JSON.parse(options.highlightedTextColors); - const hltBgColorVals=JSON.parse(options.highlightedTextBgColors); - this.$widget.find('input.highlighted-text-color[type="checkbox"]').each(function () { - if ($.inArray($(this).val(), hltColorVals) !== -1) { - $(this).prop("checked", true); - } else { - $(this).prop("checked", false); - } - }); - this.$widget.find('input.highlighted-text-background-color[type="checkbox"]').each(function () { - if ($.inArray($(this).val(), hltBgColorVals) !== -1) { + 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/routes/api/options.js b/src/routes/api/options.js index 48f2590b9..4d9ee77a2 100644 --- a/src/routes/api/options.js +++ b/src/routes/api/options.js @@ -60,8 +60,7 @@ const ALLOWED_OPTIONS = new Set([ 'compressImages', 'downloadImagesAutomatically', 'minTocHeadings', - 'highlightedTextColors', - 'highlightedTextBgColors', + 'highlightedText', 'checkForUpdates', 'disableTray', 'customSearchEngineName', diff --git a/src/services/options_init.js b/src/services/options_init.js index 53ca56932..358403214 100644 --- a/src/services/options_init.js +++ b/src/services/options_init.js @@ -87,8 +87,7 @@ const defaultOptions = [ { name: 'compressImages', value: 'true', isSynced: true }, { name: 'downloadImagesAutomatically', value: 'true', isSynced: true }, { name: 'minTocHeadings', value: '5', isSynced: true }, - { name: 'highlightedTextColors', value: '["Red","Orange","Yellow","Light green","Green","Aquamarine","Turquoise","Light blue","Blue","Purple"]', isSynced: true }, - { name: 'highlightedTextBgColors', value: '["Red","Orange","Yellow","Light green","Green","Aquamarine","Turquoise","Light blue","Blue","Purple"]', isSynced: true }, + { name: 'highlightedText', 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 },