From 23c870ceb9679a30b818e3182cb30ddf155dd634 Mon Sep 17 00:00:00 2001 From: tophf Date: Thu, 20 Jul 2017 10:27:24 +0300 Subject: [PATCH 01/17] fixup b50c19a8: "errors" is now an array --- edit/edit.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/edit/edit.js b/edit/edit.js index dbd873fd..bbad11d0 100644 --- a/edit/edit.js +++ b/edit/edit.js @@ -1610,7 +1610,7 @@ function fromMozillaFormat() { makeSectionVisible(firstAddedCM); firstAddedCM.focus(); - if (errors) { + if (errors.length) { showHelp(t('issues'), $element({ tag: 'pre', textContent: errors.join('\n'), From c963a1393283882bc3711f8600f70d2f508946a9 Mon Sep 17 00:00:00 2001 From: tophf Date: Thu, 20 Jul 2017 12:16:36 +0300 Subject: [PATCH 02/17] code cosmetics Reorganize showRegExpTester()'s render stats code to better reflect the displayed structure/order of data --- edit/edit.js | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/edit/edit.js b/edit/edit.js index bbad11d0..f037bf89 100644 --- a/edit/edit.js +++ b/edit/edit.js @@ -1897,30 +1897,31 @@ function showRegExpTester(event, section = getSectionForChild(this)) { if (!data.length) { continue; } - // 2nd level: regexp text - const summary = $element({tag: 'summary', appendChild: label}); - const block = [summary]; - for (const {text, urls} of data) { - if (!urls) { - block.push(text, br.cloneNode()); - continue; - } - block.push($element({ - tag: 'details', - open: true, - appendChild: [ - $element({tag: 'summary', textContent: text}), - // 3rd level: tab urls - ...urls, - ], - })); - } - report.appendChild($element({ + const block = report.appendChild($element({ tag: 'details', open: true, dataset: {type}, - appendChild: block, + appendChild: $element({tag: 'summary', appendChild: label}), })); + // 2nd level: regexp text + for (const {text, urls} of data) { + if (urls) { + // type is partial or full + block.appendChild($element({ + tag: 'details', + open: true, + appendChild: [ + $element({tag: 'summary', textContent: text}), + // 3rd level: tab urls + ...urls, + ], + })); + } else { + // type is none or invalid + block.appendChild(document.createTextNode(text)); + block.appendChild(br.cloneNode()); + } + } } showHelp(t('styleRegexpTestTitle'), report); From eeb826ee97060c0fa0f28b0ffee0524b697d005a Mon Sep 17 00:00:00 2001 From: tophf Date: Thu, 20 Jul 2017 12:21:32 +0300 Subject: [PATCH 03/17] showRegExpTester: don't blink favicons on typing --- edit/edit.css | 2 -- 1 file changed, 2 deletions(-) diff --git a/edit/edit.css b/edit/edit.css index f41e93cb..bdf53116 100644 --- a/edit/edit.css +++ b/edit/edit.css @@ -330,8 +330,6 @@ body[data-match-highlight="selection"] .CodeMirror-selection-highlight-scrollbar position: absolute; margin-left: -20px; margin-top: -1px; - animation: fadein 1s cubic-bezier(.03, .67, .08, .94); - animation-fill-mode: both; } /************ help popup ************/ #help-popup { From 7c8dbfc3a43b3744e8af474bf2fbc8f919c2c7c2 Mon Sep 17 00:00:00 2001 From: Jeremy Schomery Date: Wed, 19 Jul 2017 12:49:15 +0430 Subject: [PATCH 04/17] empty elements with textContent --- edit/edit.js | 2 +- manage/manage.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/edit/edit.js b/edit/edit.js index f037bf89..104aba5e 100644 --- a/edit/edit.js +++ b/edit/edit.js @@ -1963,7 +1963,7 @@ function showHelp(title, text) { ((e.keyCode || e.which) === 27 && !e.altKey && !e.ctrlKey && !e.shiftKey && !e.metaKey) ) { div.style.display = ''; - document.querySelector('.contents').innerHTML = ''; + document.querySelector('.contents').textContent = ''; document.removeEventListener('keydown', closeHelp); } } diff --git a/manage/manage.js b/manage/manage.js index 3d11308a..08e8feec 100644 --- a/manage/manage.js +++ b/manage/manage.js @@ -519,7 +519,7 @@ function switchUI({styleOnly} = {}) { const missingFavicons = newUI.enabled && newUI.favicons && !$('.applies-to img'); if (changed.enabled || (missingFavicons && !createStyleElement.parts)) { - installed.innerHTML = ''; + installed.textContent = ''; getStylesSafe().then(showStyles); return; } From f5c125d61cf1ae422bb94d936e9a5a5a9c3cc11d Mon Sep 17 00:00:00 2001 From: Jeremy Schomery Date: Wed, 19 Jul 2017 12:52:16 +0430 Subject: [PATCH 05/17] no more innerHTML in popup.js --- popup/popup.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/popup/popup.js b/popup/popup.js index 52accdf1..d8d6e331 100644 --- a/popup/popup.js +++ b/popup/popup.js @@ -176,7 +176,8 @@ function showStyles(styles) { return; } if (!styles.length) { - installed.innerHTML = template.noStyles.outerHTML; + installed.textContent = ''; + installed.appendChild(template.noStyles.cloneNode(true)); return; } From 0e9c8f290c885fd8531994b43acf2cca55fdad40 Mon Sep 17 00:00:00 2001 From: Jeremy Schomery Date: Wed, 19 Jul 2017 13:01:09 +0430 Subject: [PATCH 06/17] optionsHtmlFromArray to optionsFromArray --- edit/edit.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/edit/edit.js b/edit/edit.js index 104aba5e..4efb2860 100644 --- a/edit/edit.js +++ b/edit/edit.js @@ -260,24 +260,26 @@ function initCodeMirror() { }; // initialize global editor controls - function optionsHtmlFromArray(options) { - return options.map(opt => '').join(''); + function optionsFromArray(parent, options) { + console.log(parent, options); + options.map(opt => $element({tag: 'option', textContent: opt})) + .forEach(opt => parent.appendChild(opt)); } const themeControl = document.getElementById('editor.theme'); const themeList = localStorage.codeMirrorThemes; if (themeList) { - themeControl.innerHTML = optionsHtmlFromArray(themeList.split(/\s+/)); + optionsFromArray(themeControl, themeList.split(/\s+/)); } else { // Chrome is starting up and shows our edit.html, but the background page isn't loaded yet const theme = prefs.get('editor.theme'); - themeControl.innerHTML = optionsHtmlFromArray([theme === 'default' ? t('defaultTheme') : theme]); + optionsFromArray(themeControl, [theme === 'default' ? t('defaultTheme') : theme]); getCodeMirrorThemes().then(() => { const themes = (localStorage.codeMirrorThemes || '').split(/\s+/); - themeControl.innerHTML = optionsHtmlFromArray(themes); + optionsFromArray(themeControl, themes); themeControl.selectedIndex = Math.max(0, themes.indexOf(theme)); }); } - document.getElementById('editor.keyMap').innerHTML = optionsHtmlFromArray(Object.keys(CM.keyMap).sort()); + optionsFromArray($('#editor.keyMap'), Object.keys(CM.keyMap).sort()); document.getElementById('options').addEventListener('change', acmeEventListener, false); setupLivePrefs(); From 5d46dcc33ec9a08de683f87aa16f9a9bfcac1356 Mon Sep 17 00:00:00 2001 From: Jeremy Schomery Date: Wed, 19 Jul 2017 13:43:21 +0430 Subject: [PATCH 07/17] customizeOpenDialog, openDialog, and originalOpenConfirm now use cloned template instead of innerHTML --- edit/edit.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/edit/edit.js b/edit/edit.js index 4efb2860..26f83a08 100644 --- a/edit/edit.js +++ b/edit/edit.js @@ -261,7 +261,6 @@ function initCodeMirror() { // initialize global editor controls function optionsFromArray(parent, options) { - console.log(parent, options); options.map(opt => $element({tag: 'option', textContent: opt})) .forEach(opt => parent.appendChild(opt)); } @@ -690,11 +689,11 @@ function setupGlobalSearch() { return cm.state.search; } - // temporarily overrides the original openDialog with the provided template's innerHTML + // overrides the original openDialog with a clone of the provided template function customizeOpenDialog(cm, template, callback) { cm.openDialog = (tmpl, cb, opt) => { // invoke 'callback' and bind 'this' to the original callback - originalOpenDialog.call(cm, template.innerHTML, callback.bind(cb), opt); + originalOpenDialog.call(cm, template.cloneNode(true), callback.bind(cb), opt); }; setTimeout(() => { cm.openDialog = originalOpenDialog; }, 0); refocusMinidialog(cm); @@ -873,7 +872,7 @@ function setupGlobalSearch() { doReplace(); } }); - originalOpenConfirm.call(cm, template.replaceConfirm.innerHTML, ovrCallbacks, opt); + originalOpenConfirm.call(cm, template.replaceConfirm.cloneNode(true), ovrCallbacks, opt); }; } } @@ -892,7 +891,7 @@ function setupGlobalSearch() { function jumpToLine(cm) { const cur = cm.getCursor(); refocusMinidialog(cm); - cm.openDialog(template.jumpToLine.innerHTML, str => { + cm.openDialog(template.jumpToLine.cloneNode(true), str => { const m = str.match(/^\s*(\d+)(?:\s*:\s*(\d+))?\s*$/); if (m) { cm.setCursor(m[1] - 1, m[2] ? m[2] - 1 : cur.ch); From 61971b97c892565787405ab34d402044a374d036 Mon Sep 17 00:00:00 2001 From: Jeremy Schomery Date: Wed, 19 Jul 2017 14:36:02 +0430 Subject: [PATCH 08/17] tHTML uses parseFromString instead of innerHTML. showHelp now uses tHTML --- edit/edit.js | 11 ++--------- js/localization.js | 22 ++++++++++++++++------ 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/edit/edit.js b/edit/edit.js index 26f83a08..48634791 100644 --- a/edit/edit.js +++ b/edit/edit.js @@ -1936,17 +1936,10 @@ function showRegExpTester(event, section = getSectionForChild(this)) { }); } -function showHelp(title, text) { +function showHelp(title, body) { const div = $('#help-popup'); div.classList.remove('big'); - - const contents = $('.contents', div); - if (text instanceof HTMLElement) { - contents.textContent = ''; - contents.appendChild(text); - } else { - contents.innerHTML = text; - } + $('.contents', div).appendChild(tHTML(body)); $('.title', div).textContent = title; if (getComputedStyle(div).display === 'none') { diff --git a/js/localization.js b/js/localization.js index 1db7577b..643caf3d 100644 --- a/js/localization.js +++ b/js/localization.js @@ -28,13 +28,23 @@ function tE(id, key, attr, esc) { } -function tHTML(html) { - const node = document.createElement('div'); - node.innerHTML = html.replace(/>\s+<'); // spaces are removed; use   for an explicit space - if (html.includes('i18n-')) { - tNodeList(node.getElementsByTagName('*')); +function tHTML(html, tag) { + // body is a text node without HTML tags + if (typeof html === 'string' && /<\w+/.test(html) === false) { + return document.createTextNode(html); } - return node.firstElementChild; + if (typeof html === 'string') { + html = html.replace(/>\s+<'); // spaces are removed; use   for an explicit space + if (tag) { + html = `<${tag}>${html}`; + } + const node = (new DOMParser()).parseFromString(html, 'text/html').querySelector('body').firstElementChild; + if (html.includes('i18n-')) { + tNodeList(node.getElementsByTagName('*')); + } + return node; + } + return html; } From 12d67fda6c6a2b98d86530f3a60a6bd0f91586fd Mon Sep 17 00:00:00 2001 From: tophf Date: Sat, 22 Jul 2017 07:37:13 +0300 Subject: [PATCH 09/17] fixup 312424ff: process multiple top-level elements in tHTML --- js/localization.js | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/js/localization.js b/js/localization.js index 643caf3d..0df6086c 100644 --- a/js/localization.js +++ b/js/localization.js @@ -38,11 +38,19 @@ function tHTML(html, tag) { if (tag) { html = `<${tag}>${html}`; } - const node = (new DOMParser()).parseFromString(html, 'text/html').querySelector('body').firstElementChild; + const body = t.DOMParser.parseFromString(html, 'text/html').body; if (html.includes('i18n-')) { - tNodeList(node.getElementsByTagName('*')); + tNodeList(body.getElementsByTagName('*')); } - return node; + // the html string may contain more than one top-level elements + if (body.childElementCount <= 1) { + return body.firstElementChild; + } + const fragment = document.createDocumentFragment(); + while (body.childElementCount) { + fragment.appendChild(body.firstElementChild); + } + return fragment; } return html; } @@ -100,6 +108,7 @@ function tNodeList(nodes) { function tDocLoader() { + t.DOMParser = new DOMParser(); t.cache = tryJSONparse(localStorage.L10N) || {}; // reset L10N cache on UI language change From 0955fc852ce2f1ea079757eb8bfe1159f435840e Mon Sep 17 00:00:00 2001 From: Jeremy Schomery Date: Wed, 19 Jul 2017 16:04:31 +0430 Subject: [PATCH 10/17] removing a few more innerHTMLs --- edit/edit.js | 21 +++++++++++++-------- js/localization.js | 2 +- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/edit/edit.js b/edit/edit.js index 48634791..bc34ee73 100644 --- a/edit/edit.js +++ b/edit/edit.js @@ -1088,9 +1088,9 @@ function renderLintReport(someBlockChanged) { let issueCount = 0; editors.forEach((cm, index) => { if (cm.state.lint && cm.state.lint.html) { - const newBlock = newContent.appendChild(document.createElement('table')); const html = '' + label + ' ' + (index + 1) + '' + cm.state.lint.html; - newBlock.innerHTML = html; + const newBlock = newContent.appendChild(tHTML(html, 'table')); + newBlock.cm = cm; issueCount += newBlock.rows.length; @@ -1535,7 +1535,7 @@ function fromMozillaFormat() { ` - ).innerHTML); + )); const contents = popup.querySelector('.contents'); contents.insertBefore(popup.codebox.display.wrapper, contents.firstElementChild); @@ -1753,12 +1753,17 @@ function showKeyMapHelp() { const col = input.parentNode.cellIndex; inputs[1 - col].value = ''; table.tBodies[0].childNodes.forEach(row => { - let cell = row.children[col]; - cell.innerHTML = cell.textContent.replace(query, '$&'); + const cell = row.children[col]; + const html = cell.textContent.replace(query, '$&'); + cell.textContent = ''; + const div = tHTML(html, 'div'); + while (div.childNodes.length > 0) { + cell.appendChild(div.childNodes[0]); + } row.style.display = query.test(cell.textContent) ? '' : 'none'; // clear highlight from the other column - cell = row.children[1 - col]; - cell.innerHTML = cell.textContent; + // cell = row.children[1 - col]; + // cell.innerHTML = cell.textContent; }); } function mergeKeyMaps(merged, ...more) { @@ -1939,7 +1944,7 @@ function showRegExpTester(event, section = getSectionForChild(this)) { function showHelp(title, body) { const div = $('#help-popup'); div.classList.remove('big'); - $('.contents', div).appendChild(tHTML(body)); + $('.contents', div).appendChild(typeof body === 'string' ? tHTML(body) : body); $('.title', div).textContent = title; if (getComputedStyle(div).display === 'none') { diff --git a/js/localization.js b/js/localization.js index 0df6086c..20c0be7d 100644 --- a/js/localization.js +++ b/js/localization.js @@ -30,7 +30,7 @@ function tE(id, key, attr, esc) { function tHTML(html, tag) { // body is a text node without HTML tags - if (typeof html === 'string' && /<\w+/.test(html) === false) { + if (typeof html === 'string' && !tag && /<\w+/.test(html) === false) { return document.createTextNode(html); } if (typeof html === 'string') { From 332f95e1ffbac0c392d18bdf878502af5403606a Mon Sep 17 00:00:00 2001 From: Jeremy Schomery Date: Wed, 19 Jul 2017 15:13:24 +0300 Subject: [PATCH 11/17] i18n-html to i18n-text if possible. tNodeList now parses HTML content using tHTML --- edit.html | 2 +- js/localization.js | 6 +++++- manage.html | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/edit.html b/edit.html index 5df54d54..4684b534 100644 --- a/edit.html +++ b/edit.html @@ -61,7 +61,7 @@ diff --git a/js/localization.js b/js/localization.js index 20c0be7d..d976ab62 100644 --- a/js/localization.js +++ b/js/localization.js @@ -96,7 +96,11 @@ function tNodeList(nodes) { node.appendChild(document.createTextNode(value)); break; case 'html': - node.insertAdjacentHTML('afterbegin', value); + // localized strings only allow having text nodes and links + node.textContent = ''; + [...tHTML(value, 'div').childNodes] + .filter(a => a.nodeType === a.TEXT_NODE || a.tagName === 'A') + .forEach(n => node.appendChild(n)); break; default: node.setAttribute(type, value); diff --git a/manage.html b/manage.html index b3bb0545..1a5cae15 100644 --- a/manage.html +++ b/manage.html @@ -117,7 +117,7 @@ From 7affeedd3bd8f7d06da08ae8cfb4d7e8c4126b08 Mon Sep 17 00:00:00 2001 From: Jeremy Schomery Date: Wed, 19 Jul 2017 17:08:27 +0430 Subject: [PATCH 12/17] removing tE() --- edit/edit.js | 6 +++--- js/localization.js | 11 ----------- 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/edit/edit.js b/edit/edit.js index bc34ee73..7d1e382e 100644 --- a/edit/edit.js +++ b/edit/edit.js @@ -1234,7 +1234,7 @@ function init() { const params = getParams(); if (!params.id) { // match should be 2 - one for the whole thing, one for the parentheses // This is an add - tE('heading', 'addStyleTitle'); + $('#heading').textContent = t('addStyleTitle'); const section = {code: ''}; for (const i in CssToProperty) { if (params[i]) { @@ -1252,7 +1252,7 @@ function init() { return; } // This is an edit - tE('heading', 'editStyleHeading', null, false); + $('#heading').textContent = t('editStyleHeading'); getStylesSafe({id: params.id}).then(styles => { let style = styles[0]; if (!style) { @@ -1505,7 +1505,7 @@ function saveComplete(style) { // Go from new style URL to edit style URL if (location.href.indexOf('id=') === -1) { history.replaceState({}, document.title, 'edit.html?id=' + style.id); - tE('heading', 'editStyleHeading', null, false); + $('#heading').textContent = t('editStyleHeading'); } updateTitle(); } diff --git a/js/localization.js b/js/localization.js index d976ab62..f7f572cf 100644 --- a/js/localization.js +++ b/js/localization.js @@ -17,17 +17,6 @@ function t(key, params) { } -function tE(id, key, attr, esc) { - if (attr) { - document.getElementById(id).setAttribute(attr, t(key)); - } else if (typeof esc === 'undefined' || esc) { - document.getElementById(id).appendChild(document.createTextNode(t(key))); - } else { - document.getElementById(id).innerHTML = t(key); - } -} - - function tHTML(html, tag) { // body is a text node without HTML tags if (typeof html === 'string' && !tag && /<\w+/.test(html) === false) { From 53683dcbbd7d7a4db6fe1722f50fcac67eede694 Mon Sep 17 00:00:00 2001 From: Jeremy Schomery Date: Wed, 19 Jul 2017 17:17:55 +0430 Subject: [PATCH 13/17] empty contents of showHelp() before appending childs; based on https://github.com/openstyles/stylus/pull/119#issuecomment-316372767 --- edit/edit.js | 1 + 1 file changed, 1 insertion(+) diff --git a/edit/edit.js b/edit/edit.js index 7d1e382e..c33dcb36 100644 --- a/edit/edit.js +++ b/edit/edit.js @@ -1944,6 +1944,7 @@ function showRegExpTester(event, section = getSectionForChild(this)) { function showHelp(title, body) { const div = $('#help-popup'); div.classList.remove('big'); + $('.contents', div).textContent = ''; $('.contents', div).appendChild(typeof body === 'string' ? tHTML(body) : body); $('.title', div).textContent = title; From 9870a8041c0de635fd905af8028fc4f9eaa23d4b Mon Sep 17 00:00:00 2001 From: Jeremy Schomery Date: Wed, 19 Jul 2017 17:26:32 +0430 Subject: [PATCH 14/17] no innerHTML on messageBox() --- msgbox/msgbox.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/msgbox/msgbox.js b/msgbox/msgbox.js index 8faf8d60..df0be6ae 100644 --- a/msgbox/msgbox.js +++ b/msgbox/msgbox.js @@ -1,8 +1,8 @@ 'use strict'; function messageBox({ - title, // [mandatory] the title string for innerHTML - contents, // [mandatory] 1) DOM element 2) string for innerHTML + title, // [mandatory] string + contents, // [mandatory] 1) DOM element 2) string className = '', // string, CSS class name of the message box element buttons = [], // array of strings used as labels onshow, // function(messageboxElement) invoked after the messagebox is shown @@ -52,10 +52,9 @@ function messageBox({ unbindAndRemoveSelf(); } const id = 'message-box'; - const putAs = typeof contents === 'string' ? 'innerHTML' : 'appendChild'; messageBox.element = $element({id, className, appendChild: [ $element({appendChild: [ - $element({id: `${id}-title`, innerHTML: title}), + $element({id: `${id}-title`, textContent: title}), $element({id: `${id}-close-icon`, appendChild: $element({tag: 'SVG#svg', class: 'svg-icon', viewBox: '0 0 20 20', appendChild: $element({tag: 'SVG#path', d: 'M11.69,10l4.55,4.55-1.69,1.69L10,11.69,' + @@ -63,7 +62,7 @@ function messageBox({ }) }), onclick: messageBox.listeners.closeIcon}), - $element({id: `${id}-contents`, [putAs]: contents}), + $element({id: `${id}-contents`, appendChild: tHTML(contents)}), $element({id: `${id}-buttons`, appendChild: buttons.map((textContent, buttonIndex) => textContent && $element({ From 3ccdb555da27cdd5a28bddb9f6dc4db440a6ff05 Mon Sep 17 00:00:00 2001 From: Jeremy Schomery Date: Wed, 19 Jul 2017 17:37:32 +0430 Subject: [PATCH 15/17] no insertAdjacentHTML usage on CM theme switching --- edit/edit.js | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/edit/edit.js b/edit/edit.js index c33dcb36..b5da9147 100644 --- a/edit/edit.js +++ b/edit/edit.js @@ -315,15 +315,17 @@ function acmeEventListener(event) { break; } // avoid flicker: wait for the second stylesheet to load, then apply the theme - document.head.insertAdjacentHTML('beforeend', - ''); - (() => { - setTimeout(() => { - CodeMirror.setOption(option, value); - themeLink.remove(); - document.getElementById('cm-theme2').id = 'cm-theme'; - }, 100); - })(); + document.head.appendChild($element({ + tag: 'link', + id: 'cm-theme2', + rel: 'stylesheet', + href: url + })); + setTimeout(() => { + CodeMirror.setOption(option, value); + themeLink.remove(); + document.getElementById('cm-theme2').id = 'cm-theme'; + }, 100); return; } case 'autocompleteOnTyping': From 3c298995f130da19b0da765893e61196e48d079a Mon Sep 17 00:00:00 2001 From: Jeremy Schomery Date: Wed, 19 Jul 2017 20:48:34 +0430 Subject: [PATCH 16/17] a few fixes --- edit/edit.js | 43 +++++++++++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/edit/edit.js b/edit/edit.js index b5da9147..316b5ea5 100644 --- a/edit/edit.js +++ b/edit/edit.js @@ -261,8 +261,11 @@ function initCodeMirror() { // initialize global editor controls function optionsFromArray(parent, options) { - options.map(opt => $element({tag: 'option', textContent: opt})) - .forEach(opt => parent.appendChild(opt)); + const fragment = document.createDocumentFragment(); + for (const opt of options) { + fragment.appendChild($element({tag: 'option', textContent: opt})); + } + parent.appendChild(fragment); } const themeControl = document.getElementById('editor.theme'); const themeList = localStorage.codeMirrorThemes; @@ -324,7 +327,7 @@ function acmeEventListener(event) { setTimeout(() => { CodeMirror.setOption(option, value); themeLink.remove(); - document.getElementById('cm-theme2').id = 'cm-theme'; + $('#cm-theme2').id = 'cm-theme'; }, 100); return; } @@ -1751,21 +1754,37 @@ function showKeyMapHelp() { function filterTable(event) { const input = event.target; - const query = stringAsRegExp(input.value, 'gi'); const col = input.parentNode.cellIndex; inputs[1 - col].value = ''; table.tBodies[0].childNodes.forEach(row => { const cell = row.children[col]; - const html = cell.textContent.replace(query, '$&'); - cell.textContent = ''; - const div = tHTML(html, 'div'); - while (div.childNodes.length > 0) { - cell.appendChild(div.childNodes[0]); + const text = cell.textContent; + const query = stringAsRegExp(input.value, 'gi'); + const test = query.test(text); + row.style.display = input.value && test === false ? 'none' : ''; + if (input.value && test) { + cell.textContent = ''; + let offset = 0; + text.replace(query, (match, index) => { + if (index > offset) { + cell.appendChild(document.createTextNode(text.substring(offset, index))); + } + cell.appendChild($element({tag: 'mark', textContent: match})); + offset = index + match.length; + }); + if (offset + 1 !== text.length) { + cell.appendChild(document.createTextNode(text.substring(offset))); + } + } + else { + cell.textContent = text; } - row.style.display = query.test(cell.textContent) ? '' : 'none'; // clear highlight from the other column - // cell = row.children[1 - col]; - // cell.innerHTML = cell.textContent; + const otherCell = row.children[1 - col]; + if (otherCell.children.length) { + const text = otherCell.textContent; + otherCell.textContent = text; + } }); } function mergeKeyMaps(merged, ...more) { From e463ca7ece2fc2deb6b6fe3df6de4d0da4cf8dbd Mon Sep 17 00:00:00 2001 From: tophf Date: Sat, 22 Jul 2017 07:39:43 +0300 Subject: [PATCH 17/17] setBadgeText: skip pre-rendered tabs --- background/background.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/background/background.js b/background/background.js index 13d12e2f..643cba2c 100644 --- a/background/background.js +++ b/background/background.js @@ -281,7 +281,10 @@ function updateIcon(tab, styles) { // Vivaldi bug workaround: setBadgeText must follow setBadgeBackgroundColor chrome.browserAction.setBadgeBackgroundColor({color}); getTab(tab.id).then(() => { - chrome.browserAction.setBadgeText({text, tabId: tab.id}); + // skip pre-rendered tabs + if (tab.index >= 0) { + chrome.browserAction.setBadgeText({text, tabId: tab.id}); + } }); }); }