diff --git a/edit.html b/edit.html index a0fde99d..5d7d0c00 100644 --- a/edit.html +++ b/edit.html @@ -23,11 +23,11 @@ + - diff --git a/js/msg.js b/js/msg.js index 26dba45d..53f17bfa 100644 --- a/js/msg.js +++ b/js/msg.js @@ -33,7 +33,8 @@ self.msg = self.INJECTED === 1 ? self.msg : (() => { onExtension, off, RX_NO_RECEIVER, - RX_PORT_CLOSED + RX_PORT_CLOSED, + isBg, }; function getBg() { diff --git a/js/prefs.js b/js/prefs.js index bb3ba650..c7fb981c 100644 --- a/js/prefs.js +++ b/js/prefs.js @@ -1,6 +1,8 @@ -/* global promisifyChrome */ +/* global promisifyChrome msg API */ 'use strict'; +// Needs msg.js loaded first + self.prefs = self.INJECTED === 1 ? self.prefs : (() => { const defaults = { 'openEditInWindow': false, // new editor opens in a own browser window @@ -112,12 +114,11 @@ self.prefs = self.INJECTED === 1 ? self.prefs : (() => { 'storage.sync': ['get', 'set'], }); - const initializing = browser.storage.sync.get('settings') - .then(result => { - if (result.settings) { - setAll(result.settings, true); - } - }); + const initializing = ( + msg.isBg + ? browser.storage.sync.get('settings').then(res => res.settings) + : API.getPrefs() + ).then(res => res && setAll(res, true)); chrome.storage.onChanged.addListener((changes, area) => { if (area !== 'sync' || !changes.settings || !changes.settings.newValue) { diff --git a/js/script-loader.js b/js/script-loader.js index ada07824..2517951f 100644 --- a/js/script-loader.js +++ b/js/script-loader.js @@ -48,73 +48,3 @@ const loadScript = (() => { )); }; })(); - - -(() => { - let subscribers, observer; - // natively declared - + diff --git a/manage/filters.js b/manage/filters.js index 11e11feb..3d670dfe 100644 --- a/manage/filters.js +++ b/manage/filters.js @@ -14,7 +14,7 @@ let initialized = false; router.watch({search: ['search']}, ([search]) => { $('#search').value = search || ''; if (!initialized) { - init(); + initFilters(); initialized = true; } else { searchStyles(); @@ -36,7 +36,7 @@ HTMLSelectElement.prototype.adjustWidth = function () { parent.replaceChild(this, singleSelect); }; -function init() { +function initFilters() { $('#search').oninput = e => { router.updateSearch('search', e.target.value); }; diff --git a/manage/import-export.js b/manage/import-export.js index 7d08fc89..15261baf 100644 --- a/manage/import-export.js +++ b/manage/import-export.js @@ -1,15 +1,11 @@ /* global messageBox styleSectionsEqual API onDOMready tryJSONparse scrollElementIntoView $ $$ API $create t animateElement - styleJSONseemsValid */ -/* exported bulkChangeQueue bulkChangeTime */ + styleJSONseemsValid bulkChangeQueue */ 'use strict'; const STYLISH_DUMP_FILE_EXT = '.txt'; const STYLUS_BACKUP_FILE_EXT = '.json'; -let bulkChangeQueue = []; -let bulkChangeTime = 0; - onDOMready().then(() => { $('#file-all-styles').onclick = () => exportToFile(); $('#unfile-all-styles').onclick = () => importFromFile({fileTypeFilter: STYLUS_BACKUP_FILE_EXT}); @@ -135,7 +131,7 @@ function importFromString(jsonString) { } }); bulkChangeQueue.length = 0; - bulkChangeTime = performance.now(); + bulkChangeQueue.time = performance.now(); return API.importManyStyles(items.map(i => i.item)) .then(styles => { for (let i = 0; i < styles.length; i++) { diff --git a/manage/manage.js b/manage/manage.js index 1d1d28a1..888da2e8 100644 --- a/manage/manage.js +++ b/manage/manage.js @@ -4,11 +4,10 @@ global messageBox getStyleWithNoCode checkUpdate handleUpdateInstalled objectDiff configDialog - sorter msg prefs API onDOMready $ $$ $create template setupLivePrefs + sorter msg prefs API $ $$ $create template setupLivePrefs t tWordBreak formatDate getOwnTab getActiveTab openURL animateElement sessionStorageHash debounce scrollElementIntoView CHROME VIVALDI router - bulkChangeTime:true bulkChangeQueue */ 'use strict'; @@ -18,6 +17,8 @@ const ENTRY_ID_PREFIX_RAW = 'style-'; const ENTRY_ID_PREFIX = '#' + ENTRY_ID_PREFIX_RAW; const BULK_THROTTLE_MS = 100; +const bulkChangeQueue = []; +bulkChangeQueue.time = 0; // define pref-mapped ids separately const newUI = { @@ -49,46 +50,10 @@ Promise.all([ API.getAllStyles(true), // FIXME: integrate this into filter.js router.getSearch('search') && API.searchDB({query: router.getSearch('search')}), - Promise.all([ - onDOMready(), - prefs.initializing, - ]) - .then(() => { - initGlobalEvents(); - if (!VIVALDI) { - $$('#header select').forEach(el => el.adjustWidth()); - } - }), -]).then(args => { - showStyles(...args); -}); - -msg.onExtension(onRuntimeMessage); - -function onRuntimeMessage(msg) { - switch (msg.method) { - case 'styleUpdated': - case 'styleAdded': - case 'styleDeleted': - bulkChangeQueue.push(msg); - if (performance.now() - bulkChangeTime < BULK_THROTTLE_MS) { - debounce(handleBulkChange, BULK_THROTTLE_MS); - } else { - handleBulkChange(); - } - break; - case 'styleApply': - case 'styleReplaceAll': - break; - default: - return; - } - setTimeout(sorter.updateStripes, 0, {onlyWhenColumnsChanged: true}); -} - - -function initGlobalEvents() { - installed = $('#installed'); + waitForSelector('#installed'), // needed to avoid flicker due to an extra frame and layout shift + prefs.initializing +]).then(([styles, ids, el]) => { + installed = el; installed.onclick = handleEvent.entryClicked; $('#manage-options-button').onclick = () => router.updateHash('#stylus-options'); $('#sync-styles').onclick = () => router.updateHash('#stylus-options'); @@ -96,31 +61,12 @@ function initGlobalEvents() { // show date installed & last update on hover installed.addEventListener('mouseover', handleEvent.lazyAddEntryTitle); installed.addEventListener('mouseout', handleEvent.lazyAddEntryTitle); - document.addEventListener('visibilitychange', onVisibilityChange); - - $$('[data-toggle-on-click]').forEach(el => { - // dataset on SVG doesn't work in Chrome 49-??, works in 57+ - const target = $(el.getAttribute('data-toggle-on-click')); - el.onclick = event => { - event.preventDefault(); - target.classList.toggle('hidden'); - if (target.classList.contains('hidden')) { - el.removeAttribute('open'); - } else { - el.setAttribute('open', ''); - } - }; - }); - // N.B. triggers existing onchange listeners setupLivePrefs(); sorter.init(); - prefs.subscribe(newUI.ids.map(newUI.prefKeyForId), () => switchUI()); - switchUI({styleOnly: true}); - // translate CSS manually document.head.appendChild($create('style', ` .disabled h2::after { @@ -133,6 +79,39 @@ function initGlobalEvents() { content: "${t('filteredStylesAllHidden')}"; } `)); + if (!VIVALDI) { + $$('#header select').forEach(el => el.adjustWidth()); + } + if (CHROME >= 80 && CHROME <= 88) { + // Wrong checkboxes are randomly checked after going back in history, https://crbug.com/1138598 + addEventListener('pagehide', () => { + $$('input[type=checkbox]').forEach((el, i) => (el.name = `bug${i}`)); + }); + } + showStyles(styles, ids); +}); + +msg.onExtension(onRuntimeMessage); + +function onRuntimeMessage(msg) { + switch (msg.method) { + case 'styleUpdated': + case 'styleAdded': + case 'styleDeleted': + bulkChangeQueue.push(msg); + if (performance.now() - bulkChangeQueue.time < BULK_THROTTLE_MS) { + debounce(handleBulkChange, BULK_THROTTLE_MS); + } else { + handleBulkChange(); + } + break; + case 'styleApply': + case 'styleReplaceAll': + break; + default: + return; + } + setTimeout(sorter.updateStripes, 0, {onlyWhenColumnsChanged: true}); } function showStyles(styles = [], matchUrlIds) { @@ -538,7 +517,7 @@ function handleBulkChange() { const {id} = msg.style; if (msg.method === 'styleDeleted') { handleDelete(id); - bulkChangeTime = performance.now(); + bulkChangeQueue.time = performance.now(); } else { handleUpdateForId(id, msg); } @@ -549,7 +528,7 @@ function handleBulkChange() { function handleUpdateForId(id, opts) { return API.getStyle(id, true).then(style => { handleUpdate(style, opts); - bulkChangeTime = performance.now(); + bulkChangeQueue.time = performance.now(); }); } @@ -723,6 +702,20 @@ function highlightEditedStyle() { } } +function waitForSelector(selector) { + // TODO: if used in other places, move to dom.js + // TODO: if used concurrently, rework to use just one observer internally + return new Promise(resolve => { + const mo = new MutationObserver(() => { + const el = $(selector); + if (el) { + mo.disconnect(); + resolve(el); + } + }); + mo.observe(document, {childList: true, subtree: true}); + }); +} function embedOptions() { let options = $('#stylus-embedded-options'); diff --git a/popup.html b/popup.html index 677edb81..1a39b3a4 100644 --- a/popup.html +++ b/popup.html @@ -180,8 +180,8 @@ - +