Skip to content

Commit

Permalink
Move to webext-options-sync
Browse files Browse the repository at this point in the history
  • Loading branch information
fregante committed Sep 18, 2018
1 parent cbcf711 commit 624f215
Show file tree
Hide file tree
Showing 8 changed files with 184 additions and 34 deletions.
8 changes: 8 additions & 0 deletions extension/background.js
@@ -0,0 +1,8 @@
/* globals OptionsSync */
new OptionsSync().define({
defaults: {
previewCount: true,
compactUI: true,
participating: false
}
});
8 changes: 2 additions & 6 deletions extension/index.js
@@ -1,4 +1,4 @@
/* globals select, empty, domify, parseHTML, getOptions, setTimeoutUntilVisible, elementReady */
/* globals select, empty, domify, parseHTML, OptionsSync, setTimeoutUntilVisible, elementReady */

let options;
let notifications;
Expand Down Expand Up @@ -124,11 +124,7 @@ async function updateLoop() {
}

async function init() {
options = await getOptions({
previewCount: true,
compactUI: true,
participating: false
});
options = await new OptionsSync().getAll();
await elementReady('.notification-indicator');
updateLoop();

Expand Down
7 changes: 7 additions & 0 deletions extension/manifest.json
Expand Up @@ -26,10 +26,17 @@
],
"js": [
"utils.js",
"webext-options-sync.js",
"index.js"
],
"run_at": "document_start"
}],
"background": {
"scripts": [
"webext-options-sync.js",
"background.js"
]
},
"options_ui": {
"chrome_style": true,
"page": "options.html"
Expand Down
1 change: 1 addition & 0 deletions extension/options.html
Expand Up @@ -31,4 +31,5 @@
</label>
</p>
</form>
<script src="webext-options-sync.js"></script>
<script src="options.js"></script>
22 changes: 2 additions & 20 deletions extension/options.js
@@ -1,20 +1,2 @@
// Default values
const options = {
previewCount: true,
compactUI: true,
participating: false
};

chrome.storage.sync.get({options}, ({options}) => {
for (const [option, value] of Object.entries(options)) {
const field = document.querySelector(`[name=${option}]`);
field.checked = value;
field.addEventListener('change', () => {
chrome.storage.sync.set({
options: Object.assign(options, {
[option]: field.checked
})
});
});
}
});
/* globals OptionsSync */
new OptionsSync().syncForm(document.querySelector('form'));
8 changes: 0 additions & 8 deletions extension/utils.js
Expand Up @@ -18,14 +18,6 @@ function elementReady(selector) {
});
}

function getOptions(defaults) {
return new Promise(resolve => {
chrome.storage.sync.get({options: defaults}, response => {
resolve(response.options);
});
});
}

function parseHTML(html) {
const dom = new DOMParser().parseFromString(html, 'text/html');
return sanitizeDOM(dom);
Expand Down
161 changes: 161 additions & 0 deletions extension/webext-options-sync.js
@@ -0,0 +1,161 @@
// eslint-disable
// https://github.com/bfred-it/webext-options-sync

class OptionsSync {
constructor(storageName = 'options') {
this.storageName = storageName;
}

define(defs) {
defs = Object.assign({
defaults: {},
migrations: [],
}, defs);

if (chrome.runtime.onInstalled) { // In background script
chrome.runtime.onInstalled.addListener(() => this._applyDefinition(defs));
} else { // In content script, discouraged
this._applyDefinition(defs);
}
}

async _applyDefinition(defs) {
const options = Object.assign({}, defs.defaults, await this.getAll());

console.group('Appling definitions');
console.info('Current options:', options);
if (defs.migrations.length > 0) {
console.info('Running', defs.migrations.length, 'migrations');
defs.migrations.forEach(migrate => migrate(options, defs.defaults));
}
console.groupEnd();

this.setAll(options);
}

_parseNumbers(options) {
for (const name of Object.keys(options)) {
if (options[name] === String(Number(options[name]))) {
options[name] = Number(options[name]);
}
}
return options;
}

getAll() {
return new Promise(resolve => {
chrome.storage.sync.get(this.storageName,
keys => resolve(keys[this.storageName] || {})
);
}).then(this._parseNumbers);
}

setAll(newOptions) {
return new Promise(resolve => {
chrome.storage.sync.set({
[this.storageName]: newOptions,
}, resolve);
});
}

async set(newOptions) {
const options = await this.getAll();
this.setAll(Object.assign(options, newOptions));
}

syncForm(form) {
if (typeof form === 'string') {
form = document.querySelector(form);
}
this.getAll().then(options => OptionsSync._applyToForm(options, form));
form.addEventListener('input', e => this._handleFormUpdates(e));
form.addEventListener('change', e => this._handleFormUpdates(e));
chrome.storage.onChanged.addListener((changes, namespace) => {
if (namespace === 'sync') {
for (const key of Object.keys(changes)) {
const {newValue} = changes[key];
if (key === this.storageName) {
OptionsSync._applyToForm(newValue, form);
return;
}
}
}
});
}

static _applyToForm(options, form) {
console.group('Updating form');
for (const name of Object.keys(options)) {
const els = form.querySelectorAll(`[name="${name}"]`);
const [field] = els;
if (field) {
console.info(name, ':', options[name]);
switch (field.type) {
case 'checkbox':
field.checked = options[name];
break;
case 'radio': {
const [selected] = [...els].filter(el => el.value === options[name]);
if (selected) {
selected.checked = true;
}
break;
}
default:
field.value = options[name];
break;
}
} else {
console.warn('Stored option {', name, ':', options[name], '} was not found on the page');
}
}
console.groupEnd();
}

_handleFormUpdates({target: el}) {
const {name} = el;
let {value} = el;
if (!name || !el.validity.valid) {
return;
}
switch (el.type) {
case 'select-one':
value = el.options[el.selectedIndex].value; // eslint-disable-line prefer-destructuring
break;
case 'checkbox':
value = el.checked;
break;
default: break;
}
console.info('Saving option', el.name, 'to', value);
this.set({
[name]: value,
});
}
}

OptionsSync.migrations = {
removeUnused(options, defaults) {
for (const key of Object.keys(options)) {
if (!(key in defaults)) {
delete options[key];
}
}
}
};

if (typeof HTMLElement !== 'undefined') {
class OptionsSyncElement extends HTMLElement {
constructor() {
super();
new OptionsSync(this.getAttribute('storageName') || undefined).syncForm(this);
}
}
try {
customElements.define('options-sync', OptionsSyncElement);
} catch (err) {/* */}
}

if (typeof module === 'object') {
module.exports = OptionsSync;
}
3 changes: 3 additions & 0 deletions package.json
Expand Up @@ -16,6 +16,9 @@
"xo": "^0.18.2"
},
"xo": {
"ignores": [
"extension/webext-options-sync.js"
],
"envs": [
"browser",
"webextensions"
Expand Down

0 comments on commit 624f215

Please sign in to comment.