Deprecate localStorage, refactor prefs
This commit is contained in:
parent
69a085c116
commit
1f961b0993
|
@ -102,7 +102,6 @@ function disableAllStylesToggle(newState) {
|
|||
newState = !prefs.getPref("disableAll");
|
||||
}
|
||||
prefs.setPref("disableAll", newState);
|
||||
notifyAllTabs({method: "styleDisableAll", disableAll: newState});
|
||||
}
|
||||
|
||||
function getStyles(options, callback) {
|
||||
|
|
30
edit.js
30
edit.js
|
@ -11,7 +11,7 @@ var propertyToCss = {urls: "url", urlPrefixes: "url-prefix", domains: "domain",
|
|||
var CssToProperty = {"url": "urls", "url-prefix": "urlPrefixes", "domain": "domains", "regexp": "regexps"};
|
||||
|
||||
// make querySelectorAll enumeration code readable
|
||||
["forEach", "some", "indexOf"].forEach(function(method) {
|
||||
["forEach", "some", "indexOf", "map"].forEach(function(method) {
|
||||
NodeList.prototype[method]= Array.prototype[method];
|
||||
});
|
||||
|
||||
|
@ -125,8 +125,7 @@ function initCodeMirror() {
|
|||
var isWindowsOS = navigator.appVersion.indexOf("Windows") > 0;
|
||||
|
||||
// default option values
|
||||
var userOptions = prefs.getPref("editor.options");
|
||||
var stylishOptions = {
|
||||
shallowMerge(CM.defaults, {
|
||||
mode: 'css',
|
||||
lineNumbers: true,
|
||||
lineWrapping: true,
|
||||
|
@ -142,9 +141,7 @@ function initCodeMirror() {
|
|||
"Alt-PageDown": "nextEditor",
|
||||
"Alt-PageUp": "prevEditor"
|
||||
}
|
||||
}
|
||||
shallowMerge(stylishOptions, CM.defaults);
|
||||
shallowMerge(userOptions, CM.defaults);
|
||||
}, prefs.getPref("editor.options"));
|
||||
|
||||
// additional commands
|
||||
CM.commands.jumpToLine = jumpToLine;
|
||||
|
@ -158,11 +155,9 @@ function initCodeMirror() {
|
|||
// "basic" keymap only has basic keys by design, so we skip it
|
||||
|
||||
var extraKeysCommands = {};
|
||||
if (userOptions && typeof userOptions.extraKeys == "object") {
|
||||
Object.keys(userOptions.extraKeys).forEach(function(key) {
|
||||
extraKeysCommands[userOptions.extraKeys[key]] = true;
|
||||
});
|
||||
}
|
||||
Object.keys(CM.defaults.extraKeys).forEach(function(key) {
|
||||
extraKeysCommands[CM.defaults.extraKeys[key]] = true;
|
||||
});
|
||||
if (!extraKeysCommands.jumpToLine) {
|
||||
CM.keyMap.sublime["Ctrl-G"] = "jumpToLine";
|
||||
CM.keyMap.emacsy["Ctrl-G"] = "jumpToLine";
|
||||
|
@ -246,12 +241,11 @@ function initCodeMirror() {
|
|||
});
|
||||
}
|
||||
document.getElementById("editor.keyMap").innerHTML = optionsHtmlFromArray(Object.keys(CM.keyMap).sort());
|
||||
var controlPrefs = {};
|
||||
document.querySelectorAll("#options *[data-option][id^='editor.']").forEach(function(option) {
|
||||
controlPrefs[option.id] = CM.defaults[option.dataset.option];
|
||||
});
|
||||
document.getElementById("options").addEventListener("change", acmeEventListener, false);
|
||||
loadPrefs(controlPrefs);
|
||||
loadPrefs(
|
||||
document.querySelectorAll("#options *[data-option][id^='editor.']")
|
||||
.map(function(option) { return option.id })
|
||||
);
|
||||
});
|
||||
|
||||
hotkeyRerouter.setState(true);
|
||||
|
@ -1561,7 +1555,7 @@ function showCodeMirrorPopup(title, html, options) {
|
|||
var popup = showHelp(title, html);
|
||||
popup.classList.add("big");
|
||||
|
||||
popup.codebox = CodeMirror(popup.querySelector(".contents"), shallowMerge(options, {
|
||||
popup.codebox = CodeMirror(popup.querySelector(".contents"), shallowMerge({
|
||||
mode: "css",
|
||||
lineNumbers: true,
|
||||
lineWrapping: true,
|
||||
|
@ -1572,7 +1566,7 @@ function showCodeMirrorPopup(title, html, options) {
|
|||
styleActiveLine: true,
|
||||
theme: prefs.getPref("editor.theme"),
|
||||
keyMap: prefs.getPref("editor.keyMap")
|
||||
}));
|
||||
}, options));
|
||||
popup.codebox.focus();
|
||||
popup.codebox.on("focus", function() { hotkeyRerouter.setState(false) });
|
||||
popup.codebox.on("blur", function() { hotkeyRerouter.setState(true) });
|
||||
|
|
14
manage.js
14
manage.js
|
@ -110,7 +110,7 @@ function createStyleElement(style) {
|
|||
event.stopPropagation();
|
||||
if (openWindow || openBackgroundTab || openForegroundTab) {
|
||||
if (openWindow) {
|
||||
var options = prefs.getPref('windowPosition', {});
|
||||
var options = prefs.getPref("windowPosition");
|
||||
options.url = url;
|
||||
chrome.windows.create(options);
|
||||
} else {
|
||||
|
@ -475,12 +475,12 @@ document.addEventListener("DOMContentLoaded", function() {
|
|||
document.getElementById("search").addEventListener("input", searchStyles);
|
||||
searchStyles(true); // re-apply filtering on history Back
|
||||
|
||||
loadPrefs({
|
||||
"manage.onlyEnabled": false,
|
||||
"manage.onlyEdited": false,
|
||||
"show-badge": true,
|
||||
"popup.stylesFirst": true
|
||||
});
|
||||
loadPrefs([
|
||||
"manage.onlyEnabled",
|
||||
"manage.onlyEdited",
|
||||
"show-badge",
|
||||
"popup.stylesFirst"
|
||||
]);
|
||||
initFilter("enabled-only", document.getElementById("manage.onlyEnabled"));
|
||||
initFilter("edited-only", document.getElementById("manage.onlyEdited"));
|
||||
});
|
||||
|
|
|
@ -8,11 +8,7 @@ function notifyAllTabs(request) {
|
|||
});
|
||||
});
|
||||
// notify all open popups
|
||||
// use a shallow copy since the original `request` is still being processed
|
||||
var reqPopup = {};
|
||||
for (var k in request) reqPopup[k] = request[k];
|
||||
reqPopup.reason = request.method;
|
||||
reqPopup.method = "updatePopup";
|
||||
var reqPopup = shallowMerge({}, request, {method: "updatePopup", reason: request.method});
|
||||
chrome.extension.sendMessage(reqPopup);
|
||||
}
|
||||
|
||||
|
|
16
popup.js
16
popup.js
|
@ -185,10 +185,6 @@ function handleDelete(id) {
|
|||
}
|
||||
}
|
||||
|
||||
function handleDisableAll(disableAll) {
|
||||
installed.classList.toggle("disabled", disableAll);
|
||||
}
|
||||
|
||||
chrome.extension.onMessage.addListener(function(request, sender, sendResponse) {
|
||||
if (request.method == "updatePopup") {
|
||||
switch (request.reason) {
|
||||
|
@ -199,12 +195,6 @@ chrome.extension.onMessage.addListener(function(request, sender, sendResponse) {
|
|||
case "styleDeleted":
|
||||
handleDelete(request.id);
|
||||
break;
|
||||
case "prefChanged":
|
||||
if (request.prefName == "disableAll") {
|
||||
document.getElementById("disableAll").checked = request.value;
|
||||
handleDisableAll(request.value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -213,9 +203,7 @@ chrome.extension.onMessage.addListener(function(request, sender, sendResponse) {
|
|||
document.getElementById(id).addEventListener("click", openLink, false);
|
||||
});
|
||||
|
||||
loadPrefs({"disableAll": false});
|
||||
handleDisableAll(prefs.getPref("disableAll"));
|
||||
document.getElementById("disableAll").addEventListener("change", function(event) {
|
||||
notifyAllTabs({method: "styleDisableAll", disableAll: event.target.checked});
|
||||
handleDisableAll(event.target.checked);
|
||||
installed.classList.toggle("disabled", prefs.getPref("disableAll"));
|
||||
});
|
||||
loadPrefs(["disableAll"]);
|
||||
|
|
231
storage.js
231
storage.js
|
@ -136,28 +136,32 @@ function isCheckbox(el) {
|
|||
return el.nodeName.toLowerCase() == "input" && "checkbox" == el.type.toLowerCase();
|
||||
}
|
||||
|
||||
function changePref(event) {
|
||||
var el = event.target;
|
||||
prefs.setPref(el.id, isCheckbox(el) ? el.checked : el.value);
|
||||
}
|
||||
|
||||
// Accepts a hash of pref name to default value
|
||||
function loadPrefs(prefs) {
|
||||
for (var id in prefs) {
|
||||
var value = this.prefs.getPref(id, prefs[id]);
|
||||
var el = document.getElementById(id);
|
||||
if (isCheckbox(el)) {
|
||||
el.checked = value;
|
||||
} else {
|
||||
el.value = value;
|
||||
// Accepts an array of pref names (values are fetched via prefs.getPref)
|
||||
function loadPrefs(IDs) {
|
||||
var localIDs = {};
|
||||
IDs.forEach(function(id) {
|
||||
localIDs[id] = true;
|
||||
updateElement(id).addEventListener("change", function() {
|
||||
prefs.setPref(this.id, isCheckbox(this) ? this.checked : this.value);
|
||||
});
|
||||
});
|
||||
chrome.extension.onMessage.addListener(function(request) {
|
||||
if (request.prefName in localIDs) {
|
||||
updateElement(request.prefName);
|
||||
}
|
||||
});
|
||||
function updateElement(id) {
|
||||
var el = document.getElementById(id);
|
||||
el[isCheckbox(el) ? "checked" : "value"] = prefs.getPref(id);
|
||||
el.dispatchEvent(new Event("change", {bubbles: true, cancelable: true}));
|
||||
el.addEventListener("change", changePref);
|
||||
return el;
|
||||
}
|
||||
}
|
||||
|
||||
var prefs = {
|
||||
defaults: {
|
||||
var prefs = chrome.extension.getBackgroundPage().prefs || new function Prefs() {
|
||||
var me = this;
|
||||
|
||||
var defaults = {
|
||||
"openEditInWindow": false, // new editor opens in a own browser window
|
||||
"windowPosition": {}, // detached window position
|
||||
"show-badge": true, // display text on popup menu icon
|
||||
|
@ -189,83 +193,113 @@ var prefs = {
|
|||
},
|
||||
"editor.lintDelay": 500, // lint gutter marker update delay, ms
|
||||
"editor.lintReportDelay": 4500, // lint report update delay, ms
|
||||
},
|
||||
};
|
||||
var values = deepCopy(defaults);
|
||||
|
||||
NO_DEFAULT_PREFERENCE: "No default preference for '%s'",
|
||||
UNHANDLED_DATA_TYPE: "Default '%s' is of type '%s' - what should be done with it?",
|
||||
var syncTimeout; // see broadcast() function below
|
||||
|
||||
getPref: function(key, defaultValue) {
|
||||
// Returns localStorage[key], defaultValue, this.defaults[key], or undefined
|
||||
// as type of defaultValue, this.defaults[key], or localStorage[key]
|
||||
var value = localStorage[key];
|
||||
// NB: localStorage["not_key"] is undefined, localStorage.getItem("not_key") is null
|
||||
if (value === undefined) {
|
||||
return defaultValue === undefined ? shallowCopy(this.defaults[key]) : defaultValue;
|
||||
Object.defineProperty(this, "readOnlyValues", {value: {}});
|
||||
|
||||
Prefs.prototype.getPref = function(key, defaultValue) {
|
||||
if (key in values) {
|
||||
return values[key];
|
||||
}
|
||||
switch (typeof (defaultValue === undefined ? this.defaults[key] : defaultValue)) {
|
||||
case "boolean": return value.toLowerCase() === "true";
|
||||
case "number": return Number(value);
|
||||
case "object": return JSON.parse(value);
|
||||
case "string": break;
|
||||
case "undefined": console.warn(this.NO_DEFAULT_PREFERENCE, key); break;
|
||||
default: console.error(UNHANDLED_DATA_TYPE, key, typeof defaultValue);
|
||||
if (defaultValue !== undefined) {
|
||||
return defaultValue;
|
||||
}
|
||||
return value;
|
||||
},
|
||||
getAllPrefs: function() {
|
||||
var all = {}, me = this;
|
||||
Object.keys(this.defaults).forEach(function(key) { all[key] = me.getPref(key) });
|
||||
return all;
|
||||
},
|
||||
setPref: function(key, value, options) {
|
||||
var oldValue = localStorage[key];
|
||||
if (value === undefined || equal(value, this.defaults[key])) {
|
||||
delete localStorage[key];
|
||||
} else {
|
||||
localStorage[key] = typeof value == "string" ? value : JSON.stringify(value);
|
||||
if (key in defaults) {
|
||||
return defaults[key];
|
||||
}
|
||||
if (!equal(value, oldValue === undefined ? this.defaults[key] : oldValue)) {
|
||||
this.broadcast(key, value, options);
|
||||
console.warn("No default preference for '%s'", key);
|
||||
};
|
||||
|
||||
Prefs.prototype.getAllPrefs = function(key) {
|
||||
return deepCopy(values);
|
||||
};
|
||||
|
||||
Prefs.prototype.setPref = function(key, value, options) {
|
||||
var oldValue = deepCopy(values[key]);
|
||||
values[key] = value;
|
||||
defineReadonlyProperty(this.readOnlyValues, key, value);
|
||||
if ((!options || !options.noBroadcast) && !equal(value, oldValue)) {
|
||||
me.broadcast(key, value, options);
|
||||
}
|
||||
},
|
||||
broadcast: function(key, value, options) {
|
||||
};
|
||||
|
||||
Prefs.prototype.removePref = function(key) { me.setPref(key, undefined) };
|
||||
|
||||
Prefs.prototype.broadcast = function(key, value, options) {
|
||||
var message = {method: "prefChanged", prefName: key, value: value};
|
||||
notifyAllTabs(message);
|
||||
chrome.extension.sendMessage(message);
|
||||
if (key == "disableAll") {
|
||||
notifyAllTabs({method: "styleDisableAll", disableAll: value});
|
||||
}
|
||||
if (!options || !options.noSync) {
|
||||
clearTimeout(this.syncTimeout);
|
||||
this.syncTimeout = setTimeout((function() {
|
||||
chrome.storage.sync.set({"settings": this.getAllPrefs()});
|
||||
}).bind(this), 0);
|
||||
clearTimeout(syncTimeout);
|
||||
syncTimeout = setTimeout(function() {
|
||||
chrome.storage.sync.set({"settings": values});
|
||||
}, 0);
|
||||
}
|
||||
},
|
||||
removePref: function(key) { setPref(key, undefined) }
|
||||
};
|
||||
};
|
||||
|
||||
chrome.storage.sync.get({settings: prefs.getAllPrefs()}, function(result) {
|
||||
Object.keys(prefs.defaults).forEach(function(key) {
|
||||
if (key in result.settings) {
|
||||
prefs.setPref(key, result.settings[key], {noSync: true});
|
||||
}
|
||||
Object.keys(defaults).forEach(function(key) {
|
||||
me.setPref(key, defaults[key], {noBroadcast: true});
|
||||
});
|
||||
});
|
||||
|
||||
chrome.storage.onChanged.addListener(function(changes, area) {
|
||||
if (area == "sync" && "settings" in changes) {
|
||||
var newSettings = changes.settings.newValue;
|
||||
for (key in prefs.defaults) {
|
||||
if (key in newSettings) {
|
||||
prefs.setPref(key, newSettings[key], {noSync: true});
|
||||
chrome.storage.sync.get("settings", function(result) {
|
||||
var synced = result.settings;
|
||||
for (var key in defaults) {
|
||||
if (synced && (key in synced)) {
|
||||
me.setPref(key, synced[key], {noSync: true});
|
||||
} else {
|
||||
var value = tryMigrating(key);
|
||||
if (value !== undefined) {
|
||||
me.setPref(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
window.addEventListener("storage", function(event) {
|
||||
if (event.storageArea == localStorage && event.key in prefs.defaults) {
|
||||
prefs.broadcast(event.key, prefs.getPref(event.key));
|
||||
chrome.storage.onChanged.addListener(function(changes, area) {
|
||||
if (area == "sync" && "settings" in changes) {
|
||||
var synced = changes.settings.newValue;
|
||||
if (synced) {
|
||||
for (key in defaults) {
|
||||
if (key in synced) {
|
||||
me.setPref(key, synced[key], {noSync: true});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// user manually deleted our settings, we'll recreate them
|
||||
chrome.storage.sync.set({"settings": values});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function tryMigrating(key) {
|
||||
if (!(key in localStorage)) {
|
||||
return undefined;
|
||||
}
|
||||
var value = localStorage[key];
|
||||
delete localStorage[key];
|
||||
localStorage["DEPRECATED: " + key] = value;
|
||||
switch (typeof defaults[key]) {
|
||||
case "boolean":
|
||||
return value.toLowerCase() === "true";
|
||||
case "number":
|
||||
return Number(value);
|
||||
case "object":
|
||||
try {
|
||||
return JSON.parse(value);
|
||||
} catch(e) {
|
||||
console.log("Cannot migrate from localStorage %s = '%s': %o", key, value, e);
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
function getCodeMirrorThemes(callback) {
|
||||
chrome.runtime.getPackageDirectoryEntry(function(rootDir) {
|
||||
|
@ -300,17 +334,42 @@ function sessionStorageHash(name) {
|
|||
return hash;
|
||||
}
|
||||
|
||||
function shallowCopy(obj) {
|
||||
return typeof obj == "object" ? shallowMerge(obj, {}) : obj;
|
||||
function deepCopy(obj) {
|
||||
if (!obj || typeof obj != "object") {
|
||||
return obj;
|
||||
} else {
|
||||
var emptyCopy = Object.create(Object.getPrototypeOf(obj));
|
||||
return deepMerge(emptyCopy, obj);
|
||||
}
|
||||
}
|
||||
|
||||
function shallowMerge(from, to) {
|
||||
if (typeof from == "object" && typeof to == "object") {
|
||||
for (var k in from) {
|
||||
to[k] = from[k];
|
||||
function deepMerge(target, obj1 /* plus any number of object arguments */) {
|
||||
for (var i = 1; i < arguments.length; i++) {
|
||||
var obj = arguments[i];
|
||||
for (var k in obj) {
|
||||
// hasOwnProperty checking is not needed for our non-OOP stuff
|
||||
var value = obj[k];
|
||||
if (!value || typeof value != "object") {
|
||||
target[k] = value;
|
||||
} else if (k in target) {
|
||||
deepMerge(target[k], value);
|
||||
} else {
|
||||
target[k] = deepCopy(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return to;
|
||||
return target;
|
||||
}
|
||||
|
||||
function shallowMerge(target, obj1 /* plus any number of object arguments */) {
|
||||
for (var i = 1; i < arguments.length; i++) {
|
||||
var obj = arguments[i];
|
||||
for (var k in obj) {
|
||||
target[k] = obj[k];
|
||||
// hasOwnProperty checking is not needed for our non-OOP stuff
|
||||
}
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
function equal(a, b) {
|
||||
|
@ -327,3 +386,9 @@ function equal(a, b) {
|
|||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function defineReadonlyProperty(obj, key, value) {
|
||||
var copy = deepCopy(value);
|
||||
Object.freeze(copy);
|
||||
Object.defineProperty(obj, key, {value: copy, configurable: true})
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user