diff --git a/js/dom.js b/js/dom.js index c956d729..7ffb9ce6 100644 --- a/js/dom.js +++ b/js/dom.js @@ -392,3 +392,55 @@ function moveFocus(rootElement, step) { } } } + +// Accepts an array of pref names (values are fetched via prefs.get) +// and establishes a two-way connection between the document elements and the actual prefs +function setupLivePrefs( + IDs = Object.getOwnPropertyNames(prefs.defaults) + .filter(id => $('#' + id)) +) { + for (const id of IDs) { + const element = $('#' + id); + updateElement({id, element, force: true}); + element.addEventListener('change', onChange); + } + prefs.subscribe(IDs, (id, value) => updateElement({id, value})); + + function onChange() { + const value = getInputValue(this); + if (prefs.get(this.id) !== value) { + prefs.set(this.id, value); + } + } + function updateElement({ + id, + value = prefs.get(id), + element = $('#' + id), + force, + }) { + if (!element) { + prefs.unsubscribe(IDs, updateElement); + return; + } + setInputValue(element, value, force); + } + function getInputValue(input) { + if (input.type === 'checkbox') { + return input.checked; + } + if (input.type === 'number') { + return Number(input.value); + } + return input.value; + } + function setInputValue(input, value, force = false) { + if (force || getInputValue(input) !== value) { + if (input.type === 'checkbox') { + input.checked = value; + } else { + input.value = value; + } + input.dispatchEvent(new Event('change', {bubbles: true, cancelable: true})); + } + } +} diff --git a/js/prefs.js b/js/prefs.js index 5703e370..262eabf8 100644 --- a/js/prefs.js +++ b/js/prefs.js @@ -113,6 +113,8 @@ var prefs = (() => { initializing.then(() => setAll(changes.settings.newValue, true)); }); + let timer; + // coalesce multiple pref changes in broadcast // let changes = {}; @@ -217,11 +219,10 @@ var prefs = (() => { } values[key] = value; emitChange(key, value); - if (synced) { + if (synced || timer) { return; } - // changes[key] = value; - debounce(syncPrefs); + timer = setTimeout(syncPrefs); } function emitChange(key, value) { @@ -241,6 +242,7 @@ var prefs = (() => { function syncPrefs() { // FIXME: we always set the entire object? Ideally, this should only use `changes`. chrome.storage.sync.set({settings: values}); + timer = null; } function equal(a, b) { @@ -263,7 +265,7 @@ var prefs = (() => { } function contextDeleteMissing() { - return CHROME && ( + return /Chrome\/\d+/.test(navigator.userAgent) && ( // detect browsers without Delete by looking at the end of UA string /Vivaldi\/[\d.]+$/.test(navigator.userAgent) || // Chrome and co. @@ -272,57 +274,17 @@ var prefs = (() => { !Array.from(navigator.plugins).some(p => p.name === 'Shockwave Flash') ); } + + function deepCopy(obj) { + if (!obj || typeof obj !== 'object') { + return obj; + } + if (Array.isArray(obj)) { + return obj.map(deepCopy); + } + return Object.keys(obj).reduce((output, key) => { + output[key] = deepCopy(obj[key]); + return output; + }, {}); + } })(); - - -// Accepts an array of pref names (values are fetched via prefs.get) -// and establishes a two-way connection between the document elements and the actual prefs -function setupLivePrefs( - IDs = Object.getOwnPropertyNames(prefs.defaults) - .filter(id => $('#' + id)) -) { - for (const id of IDs) { - const element = $('#' + id); - updateElement({id, element, force: true}); - element.addEventListener('change', onChange); - } - prefs.subscribe(IDs, (id, value) => updateElement({id, value})); - - function onChange() { - const value = getInputValue(this); - if (prefs.get(this.id) !== value) { - prefs.set(this.id, value); - } - } - function updateElement({ - id, - value = prefs.get(id), - element = $('#' + id), - force, - }) { - if (!element) { - prefs.unsubscribe(IDs, updateElement); - return; - } - setInputValue(element, value, force); - } - function getInputValue(input) { - if (input.type === 'checkbox') { - return input.checked; - } - if (input.type === 'number') { - return Number(input.value); - } - return input.value; - } - function setInputValue(input, value, force = false) { - if (force || getInputValue(input) !== value) { - if (input.type === 'checkbox') { - input.checked = value; - } else { - input.value = value; - } - input.dispatchEvent(new Event('change', {bubbles: true, cancelable: true})); - } - } -}