This commit is contained in:
DecentM 2017-01-30 19:06:33 +00:00 committed by GitHub
commit 92590f0326
11 changed files with 3855 additions and 3442 deletions

View File

@ -1,3 +1,4 @@
/*jshint undef:false*/
var g_disableAll = false;
var g_styleElements = {};
var iframeObserver;
@ -11,8 +12,13 @@ function requestStyles() {
// we'll request the styles directly to minimize delay and flicker,
// unless Chrome still starts up and the background page isn't fully loaded.
// (Note: in this case the function may be invoked again from applyStyles.)
var request = {method: "getStyles", matchUrl: location.href, enabled: true, asHash: true};
if (location.href.indexOf(chrome.extension.getURL("")) == 0) {
var request = {
method: "getStyles",
matchUrl: location.href,
enabled: true,
asHash: true
};
if (location.href.indexOf(chrome.extension.getURL("")) === 0) {
var bg = chrome.extension.getBackgroundPage();
if (bg && bg.getStyles) {
// apply styles immediately, then proceed with a normal request that will update the icon
@ -24,6 +30,18 @@ function requestStyles() {
chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
// Also handle special request just for the pop-up
function styleAdded() {
if (request.style.enabled) {
chrome.runtime.sendMessage({
method: "getStyles",
matchUrl: location.href,
enabled: true,
id: request.style.id,
asHash: true
}, applyStyles);
}
}
switch (request.method == "updatePopup" ? request.reason : request.method) {
case "styleDeleted":
removeStyle(request.id, document);
@ -31,15 +49,13 @@ chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
case "styleUpdated":
if (request.style.enabled) {
retireStyle(request.style.id);
// fallthrough to "styleAdded"
styleAdded();
} else {
removeStyle(request.style.id, document);
}
break;
}
case "styleAdded":
if (request.style.enabled) {
chrome.runtime.sendMessage({method: "getStyles", matchUrl: location.href, enabled: true, id: request.style.id, asHash: true}, applyStyles);
}
styleAdded();
break;
case "styleApply":
applyStyles(request.styles);
@ -90,7 +106,7 @@ function removeStyle(id, doc) {
if (e) {
e.remove();
}
if (doc == document && Object.keys(g_styleElements).length == 0) {
if (doc == document && Object.keys(g_styleElements).length === 0) {
iframeObserver.disconnect();
}
getDynamicIFrames(doc).forEach(function (iframe) {
@ -224,7 +240,7 @@ function iframeIsDynamic(f) {
// Cross-origin, so it's not a dynamic iframe
return false;
}
return href == document.location.href || href.indexOf("about:") == 0;
return href == document.location.href || href.indexOf("about:") === 0;
}
function iframeIsLoadingSrcDoc(f) {
@ -244,7 +260,9 @@ function addStyleToIFrameSrcDoc(iframe, styleElement) {
function replaceAll(newStyles, doc, pass2) {
var oldStyles = [].slice.call(doc.querySelectorAll("STYLE.stylish" + (pass2 ? "[id$='-ghost']" : "")));
if (!pass2) {
oldStyles.forEach(function(style) { style.id += "-ghost"; });
oldStyles.forEach(function (style) {
style.id += "-ghost";
});
}
getDynamicIFrames(doc).forEach(function (iframe) {
replaceAll(newStyles, iframe.contentDocument, pass2);
@ -255,7 +273,9 @@ function replaceAll(newStyles, doc, pass2) {
replaceAll(newStyles, doc, true);
}
if (pass2) {
oldStyles.forEach(function(style) { style.remove(); });
oldStyles.forEach(function (style) {
style.remove();
});
}
}
@ -290,6 +310,9 @@ function initObserver() {
iframeObserver.start = function () {
// will be ignored by browser if already observing
iframeObserver.observe(document, {childList: true, subtree: true});
}
iframeObserver.observe(document, {
childList: true,
subtree: true
});
};
}

View File

@ -1,6 +1,9 @@
/*jshint undef:false*/
var frameIdMessageable;
runTryCatch(function () {
chrome.tabs.sendMessage(0, {}, {frameId: 0}, function() {
chrome.tabs.sendMessage(0, {}, {
frameId: 0
}, function () {
var clearError = chrome.runtime.lastError;
frameIdMessageable = true;
});
@ -14,21 +17,34 @@ if ("onHistoryStateUpdated" in chrome.webNavigation) {
chrome.webNavigation.onHistoryStateUpdated.addListener(webNavigationListener.bind(this, "styleReplaceAll"));
}
chrome.webNavigation.onBeforeNavigate.addListener(webNavigationListener.bind(this, null));
function webNavigationListener(method, data) {
// Until Chrome 41, we can't target a frame with a message
// (https://developer.chrome.com/extensions/tabs#method-sendMessage)
// so a style affecting a page with an iframe will affect the main page as well.
// Skip doing this for frames in pre-41 to prevent page flicker.
if (data.frameId != 0 && !frameIdMessageable) {
if (data.frameId !== 0 && !frameIdMessageable) {
return;
}
getStyles({matchUrl: data.url, enabled: true, asHash: true}, function(styleHash) {
getStyles({
matchUrl: data.url,
enabled: true,
asHash: true
}, function (styleHash) {
if (method) {
chrome.tabs.sendMessage(data.tabId, {method: method, styles: styleHash},
frameIdMessageable ? {frameId: data.frameId} : undefined);
chrome.tabs.sendMessage(data.tabId, {
method: method,
styles: styleHash
},
frameIdMessageable ? {
frameId: data.frameId
} : undefined);
}
if (data.frameId == 0) {
updateIcon({id: data.tabId, url: data.url}, styleHash);
if (data.frameId === 0) {
updateIcon({
id: data.tabId,
url: data.url
}, styleHash);
}
});
}
@ -45,7 +61,11 @@ chrome.tabs.onUpdated.addListener(function(tabId, info, tab) {
// do nothing since the tab neither had # before nor has # now
return;
}
webNavigationListener("styleReplaceAll", {tabId: tabId, frameId: 0, url: info.url});
webNavigationListener("styleReplaceAll", {
tabId: tabId,
frameId: 0,
url: info.url
});
}
});
chrome.tabs.onRemoved.addListener(function (tabId, info) {
@ -57,9 +77,7 @@ chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
case "getStyles":
var styles = getStyles(request, sendResponse);
// check if this is a main content frame style enumeration
if (request.matchUrl && !request.id
&& sender && sender.tab && sender.frameId == 0
&& sender.tab.url == request.matchUrl) {
if (request.matchUrl && !request.id && sender && sender.tab && sender.frameId === 0 && sender.tab.url == request.matchUrl) {
updateIcon(sender.tab, styles);
}
return true;
@ -72,17 +90,25 @@ chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
}
break;
case "healthCheck":
getDatabase(function() { sendResponse(true); }, function() { sendResponse(false); });
getDatabase(function () {
sendResponse(true);
}, function () {
sendResponse(false);
});
return true;
case "openURL":
openURL(request);
break;
case "styleDisableAll":
chrome.contextMenus.update("disableAll", {checked: request.disableAll});
chrome.contextMenus.update("disableAll", {
checked: request.disableAll
});
break;
case "prefChanged":
if (request.prefName == "show-badge") {
chrome.contextMenus.update("show-badge", {checked: request.value});
chrome.contextMenus.update("show-badge", {
checked: request.value
});
}
break;
}
@ -94,11 +120,15 @@ if ("commands" in chrome) {
chrome.commands.onCommand.addListener(function (command) {
switch (command) {
case "openManage":
openURL({url: chrome.extension.getURL("manage.html")});
openURL({
url: chrome.extension.getURL("manage.html")
});
break;
case "styleDisableAll":
disableAllStylesToggle();
chrome.contextMenus.update("disableAll", {checked: prefs.get("disableAll")});
chrome.contextMenus.update("disableAll", {
checked: prefs.get("disableAll")
});
break;
}
});
@ -108,13 +138,23 @@ if ("commands" in chrome) {
// upon encountering the unsupported parameter value "browser_action", so we have to catch it.
runTryCatch(function () {
chrome.contextMenus.create({
id: "show-badge", title: chrome.i18n.getMessage("menuShowBadge"),
type: "checkbox", contexts: ["browser_action"], checked: prefs.get("show-badge")
}, function() { var clearError = chrome.runtime.lastError });
id: "show-badge",
title: chrome.i18n.getMessage("menuShowBadge"),
type: "checkbox",
contexts: ["browser_action"],
checked: prefs.get("show-badge")
}, function () {
var clearError = chrome.runtime.lastError;
});
chrome.contextMenus.create({
id: "disableAll", title: chrome.i18n.getMessage("disableAllStyles"),
type: "checkbox", contexts: ["browser_action"], checked: prefs.get("disableAll")
}, function() { var clearError = chrome.runtime.lastError });
id: "disableAll",
title: chrome.i18n.getMessage("disableAllStyles"),
type: "checkbox",
contexts: ["browser_action"],
checked: prefs.get("disableAll")
}, function () {
var clearError = chrome.runtime.lastError;
});
});
chrome.contextMenus.onClicked.addListener(function (info, tab) {
@ -139,8 +179,10 @@ getDatabase(function() {}, reportError);
var editFullUrl = chrome.extension.getURL("edit.html");
chrome.tabs.onAttached.addListener(function (tabId, data) {
chrome.tabs.get(tabId, function (tabData) {
if (tabData.url.indexOf(editFullUrl) == 0) {
chrome.windows.get(tabData.windowId, {populate: true}, function(win) {
if (tabData.url.indexOf(editFullUrl) === 0) {
chrome.windows.get(tabData.windowId, {
populate: true
}, function (win) {
// If there's only one tab in this window, it's been dragged to new window
prefs.set("openEditInWindow", win.tabs.length == 1);
});
@ -149,10 +191,16 @@ chrome.tabs.onAttached.addListener(function(tabId, data) {
});
function openURL(options) {
chrome.tabs.query({currentWindow: true, url: options.url}, function(tabs) {
chrome.tabs.query({
currentWindow: true,
url: options.url
}, function (tabs) {
// switch to an existing tab with the requested url
if (tabs.length) {
chrome.tabs.highlight({windowId: tabs[0].windowId, tabs: tabs[0].index}, function (window) {});
chrome.tabs.highlight({
windowId: tabs[0].windowId,
tabs: tabs[0].index
}, function (window) {});
} else {
delete options.method;
getActiveTab(function (tab) {

310
edit.js
View File

@ -1,5 +1,5 @@
"use strict";
/*jshint undef:false*/
(function () {
var styleId = null;
var dirty = {}; // only the actually dirty items here
var editors = []; // array of all CodeMirror instances
@ -7,8 +7,18 @@ var saveSizeOnClose;
var useHistoryBack; // use browser history back when "back to manage" is clicked
// direct & reverse mapping of @-moz-document keywords and internal property names
var propertyToCss = {urls: "url", urlPrefixes: "url-prefix", domains: "domain", regexps: "regexp"};
var CssToProperty = {"url": "urls", "url-prefix": "urlPrefixes", "domain": "domains", "regexp": "regexps"};
var propertyToCss = {
urls: "url",
urlPrefixes: "url-prefix",
domains: "domain",
regexps: "regexp"
};
var CssToProperty = {
"url": "urls",
"url-prefix": "urlPrefixes",
"domain": "domains",
"regexp": "regexps"
};
// make querySelectorAll enumeration code readable
["forEach", "some", "indexOf", "map"].forEach(function (method) {
@ -28,15 +38,26 @@ Array.prototype.rotate = function(amount) { // negative amount == rotate left
var r = this.slice(-amount, this.length);
Array.prototype.push.apply(r, this.slice(0, this.length - r.length));
return r;
}
};
Object.defineProperty(Array.prototype, "last", {get: function() { return this[this.length - 1]; }});
Object.defineProperty(Array.prototype, "last", {
get: function () {
return this[this.length - 1];
}
});
// reroute handling to nearest editor when keypress resolves to one of these commands
var hotkeyRerouter = {
commands: {
save: true, jumpToLine: true, nextEditor: true, prevEditor: true,
find: true, findNext: true, findPrev: true, replace: true, replaceAll: true
save: true,
jumpToLine: true,
nextEditor: true,
prevEditor: true,
find: true,
findNext: true,
findPrev: true,
replace: true,
replaceAll: true
},
setState: function (enable) {
setTimeout(function () {
@ -45,11 +66,11 @@ var hotkeyRerouter = {
},
eventHandler: function (event) {
var keyName = CodeMirror.keyName(event);
if ("handled" == CodeMirror.lookupKey(keyName, CodeMirror.getOption("keyMap"), handleCommand)
|| "handled" == CodeMirror.lookupKey(keyName, CodeMirror.defaults.extraKeys, handleCommand)) {
if ("handled" == CodeMirror.lookupKey(keyName, CodeMirror.getOption("keyMap"), handleCommand) || "handled" == CodeMirror.lookupKey(keyName, CodeMirror.defaults.extraKeys, handleCommand)) {
event.preventDefault();
event.stopPropagation();
}
function handleCommand(command) {
if (hotkeyRerouter.commands[command] === true) {
CodeMirror.commands[command](getEditorInSight(event.target));
@ -98,7 +119,7 @@ function setCleanItem(node, isClean) {
}
function isCleanGlobal() {
var clean = Object.keys(dirty).length == 0;
var clean = Object.keys(dirty).length === 0;
setDirtyClass(document.body, !clean);
document.getElementById("save-button").disabled = clean;
return clean;
@ -110,7 +131,9 @@ function setCleanGlobal() {
}
function setCleanSection(section) {
section.querySelectorAll(".style-contributor").forEach(function(node) { setCleanItem(node, true) });
section.querySelectorAll(".style-contributor").forEach(function (node) {
setCleanItem(node, true);
});
// #header section has no codemirror
var cm = section.CodeMirror;
@ -132,7 +155,10 @@ function initCodeMirror() {
foldGutter: true,
gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter", "CodeMirror-lint-markers"],
matchBrackets: true,
lint: {getAnnotations: CodeMirror.lint.css, delay: prefs.get("editor.lintDelay")},
lint: {
getAnnotations: CodeMirror.lint.css,
delay: prefs.get("editor.lintDelay")
},
lintReportDelay: prefs.get("editor.lintReportDelay"),
styleActiveLine: true,
theme: "default",
@ -145,11 +171,17 @@ function initCodeMirror() {
// additional commands
CM.commands.jumpToLine = jumpToLine;
CM.commands.nextEditor = function(cm) { nextPrevEditor(cm, 1) };
CM.commands.prevEditor = function(cm) { nextPrevEditor(cm, -1) };
CM.commands.nextEditor = function (cm) {
nextPrevEditor(cm, 1);
};
CM.commands.prevEditor = function (cm) {
nextPrevEditor(cm, -1);
};
CM.commands.save = save;
CM.commands.blockComment = function (cm) {
cm.blockComment(cm.getCursor("from"), cm.getCursor("to"), {fullLines: false});
cm.blockComment(cm.getCursor("from"), cm.getCursor("to"), {
fullLines: false
});
};
// "basic" keymap only has basic keys by design, so we skip it
@ -177,7 +209,7 @@ function initCodeMirror() {
if (isWindowsOS) {
// "pcDefault" keymap on Windows should have F3/Shift-F3
if (!extraKeysCommands.findNext) {
CM.keyMap.pcDefault["F3"] = "findNext";
CM.keyMap.pcDefault.F3 = "findNext";
}
if (!extraKeysCommands.findPrev) {
CM.keyMap.pcDefault["Shift-F3"] = "findPrev";
@ -185,8 +217,14 @@ function initCodeMirror() {
// try to remap non-interceptable Ctrl-(Shift-)N/T/W hotkeys
["N", "T", "W"].forEach(function (char) {
[{from: "Ctrl-", to: ["Alt-", "Ctrl-Alt-"]},
{from: "Shift-Ctrl-", to: ["Ctrl-Alt-", "Shift-Ctrl-Alt-"]} // Note: modifier order in CM is S-C-A
[{
from: "Ctrl-",
to: ["Alt-", "Ctrl-Alt-"]
},
{
from: "Shift-Ctrl-",
to: ["Ctrl-Alt-", "Shift-Ctrl-Alt-"]
} // Note: modifier order in CM is S-C-A
].forEach(function (remap) {
var oldKey = remap.from + char;
Object.keys(CM.keyMap).forEach(function (keyMapName) {
@ -211,17 +249,17 @@ function initCodeMirror() {
// user option values
CM.getOption = function (o) {
return CodeMirror.defaults[o];
}
};
CM.setOption = function (o, v) {
CodeMirror.defaults[o] = v;
editors.forEach(function (editor) {
editor.setOption(o, v);
});
}
};
CM.prototype.getSection = function () {
return this.display.wrapper.parentNode;
}
};
// preload the theme so that CodeMirror can calculate its metrics in DOMContentLoaded->setupLivePrefs()
var theme = prefs.get("editor.theme");
@ -230,7 +268,9 @@ function initCodeMirror() {
// initialize global editor controls
document.addEventListener("DOMContentLoaded", function () {
function optionsHtmlFromArray(options) {
return options.map(function(opt) { return "<option>" + opt + "</option>"; }).join("");
return options.map(function (opt) {
return "<option>" + opt + "</option>";
}).join("");
}
var themeControl = document.getElementById("editor.theme");
var bg = chrome.extension.getBackgroundPage();
@ -248,7 +288,9 @@ function initCodeMirror() {
document.getElementById("options").addEventListener("change", acmeEventListener, false);
setupLivePrefs(
document.querySelectorAll("#options *[data-option][id^='editor.']")
.map(function(option) { return option.id })
.map(function (option) {
return option.id;
})
);
});
@ -324,9 +366,8 @@ function setupCodeMirror(textarea, index) {
resizeGrip.addEventListener("mousedown", function (e) {
e.preventDefault();
var cm = e.target.parentNode.CodeMirror;
var minHeight = cm.defaultTextHeight()
+ cm.display.lineDiv.offsetParent.offsetTop /* .CodeMirror-lines padding */
+ cm.display.wrapper.offsetHeight - cm.display.wrapper.scrollHeight /* borders */;
var minHeight = cm.defaultTextHeight() + cm.display.lineDiv.offsetParent.offsetTop /* .CodeMirror-lines padding */ + cm.display.wrapper.offsetHeight - cm.display.wrapper.scrollHeight /* borders */ ;
function resize(e) {
cm.setSize(null, Math.max(minHeight, cm.display.wrapper.scrollHeight + e.movementY));
}
@ -337,13 +378,13 @@ function setupCodeMirror(textarea, index) {
});
});
// resizeGrip has enough space when scrollbars.horiz is visible
if (cm.display.scrollbars.horiz.style.display != "") {
if (cm.display.scrollbars.horiz.style.display !== "") {
cm.display.scrollbars.vert.style.marginBottom = "0";
}
// resizeGrip space adjustment in case a long line was entered/deleted by a user
new MutationObserver(function (mutations) {
var hScrollbar = mutations[0].target;
var hScrollbarVisible = hScrollbar.style.display != "";
var hScrollbarVisible = hScrollbar.style.display !== "";
var vScrollbar = hScrollbar.parentNode.CodeMirror.display.scrollbars.vert;
vScrollbar.style.marginBottom = hScrollbarVisible ? "0" : "";
}).observe(cm.display.scrollbars.horiz, {
@ -386,7 +427,9 @@ document.addEventListener("wheel", function(event) {
}
});
chrome.tabs.query({currentWindow: true}, function(tabs) {
chrome.tabs.query({
currentWindow: true
}, function (tabs) {
var windowId = tabs[0].windowId;
if (prefs.get("openEditInWindow")) {
if (tabs.length == 1 && window.history.length == 1) {
@ -435,10 +478,10 @@ window.onbeforeunload = function() {
}
updateLintReport(null, 0);
return confirm(t('styleChangesNotSaved'));
}
};
function addAppliesTo(list, name, value) {
var showingEverything = list.querySelector(".applies-to-everything") != null;
var showingEverything = list.querySelector(".applies-to-everything") !== null;
// blow away "Everything" if it's there
if (showingEverything) {
list.removeChild(list.firstChild);
@ -458,7 +501,9 @@ function addAppliesTo(list, name, value) {
} else {
e = template.appliesToEverything.cloneNode(true);
}
e.querySelector(".add-applies-to").addEventListener("click", function() {addAppliesTo(this.parentNode.parentNode)}, false);
e.querySelector(".add-applies-to").addEventListener("click", function () {
addAppliesTo(this.parentNode.parentNode);
}, false);
list.appendChild(e);
}
@ -491,18 +536,19 @@ function addSection(event, section) {
appliesTo.addEventListener("change", onChange);
appliesTo.addEventListener("input", onChange);
var sections = document.getElementById("sections");
var sections = document.getElementById("sections"),
cm;
if (event) {
var clickedSection = getSectionForChild(event.target);
sections.insertBefore(div, clickedSection.nextElementSibling);
var newIndex = getSections().indexOf(clickedSection) + 1;
var cm = setupCodeMirror(codeElement, newIndex);
cm = setupCodeMirror(codeElement, newIndex);
makeSectionVisible(cm);
cm.focus()
cm.focus();
renderLintReport();
} else {
sections.appendChild(div);
var cm = setupCodeMirror(codeElement);
cm = setupCodeMirror(codeElement);
}
div.CodeMirror = cm;
@ -561,7 +607,7 @@ function setupGlobalSearch() {
findNext: CodeMirror.commands.findNext,
findPrev: CodeMirror.commands.findPrev,
replace: CodeMirror.commands.replace
}
};
var originalOpenDialog = CodeMirror.prototype.openDialog;
var originalOpenConfirm = CodeMirror.prototype.openConfirm;
@ -585,7 +631,7 @@ function setupGlobalSearch() {
query: newState.query,
overlay: newState.overlay,
annotate: cm.showMatchesOnScrollbar(newState.query, shouldIgnoreCase(newState.query))
}
};
cm.addOverlay(newState.overlay);
return cm.state.search;
}
@ -596,7 +642,9 @@ function setupGlobalSearch() {
// invoke 'callback' and bind 'this' to the original callback
originalOpenDialog.call(cm, template.innerHTML, callback.bind(cb), opt);
};
setTimeout(function() { cm.openDialog = originalOpenDialog; }, 0);
setTimeout(function () {
cm.openDialog = originalOpenDialog;
}, 0);
refocusMinidialog(cm);
}
@ -624,7 +672,7 @@ function setupGlobalSearch() {
updateState(cm, curState);
}
});
if (CodeMirror.cmpPos(curState.posFrom, curState.posTo) == 0) {
if (CodeMirror.cmpPos(curState.posFrom, curState.posTo) === 0) {
findNext(activeCM);
}
});
@ -640,11 +688,9 @@ function setupGlobalSearch() {
var pos = activeCM.getCursor(reverse ? "from" : "to");
activeCM.setSelection(activeCM.getCursor()); // clear the selection, don't move the cursor
var rxQuery = typeof state.query == "object"
? state.query : stringAsRegExp(state.query, shouldIgnoreCase(state.query) ? "i" : "");
var rxQuery = typeof state.query == "object" ? state.query : stringAsRegExp(state.query, shouldIgnoreCase(state.query) ? "i" : "");
if (document.activeElement && document.activeElement.name == "applies-value"
&& searchAppliesTo(activeCM)) {
if (document.activeElement && document.activeElement.name == "applies-value" && searchAppliesTo(activeCM)) {
return;
}
for (var i = 0, cm = activeCM; i < editors.length; i++) {
@ -689,7 +735,7 @@ function setupGlobalSearch() {
// works only outside of current event handlers chain, hence timeout=0
setTimeout(function () {
input.setSelectionRange(end, end);
input.setSelectionRange(match.index, end)
input.setSelectionRange(match.index, end);
}, 0);
return true;
}
@ -742,6 +788,7 @@ function setupGlobalSearch() {
};
originalCommand.replace(cm, all);
}
function doConfirm(cm) {
var wrapAround = false;
var origPos = cm.getCursor();
@ -750,7 +797,9 @@ function setupGlobalSearch() {
return function () {
makeSectionVisible(cm);
cm.openConfirm = overrideConfirm;
setTimeout(function() { cm.openConfirm = originalOpenConfirm; }, 0);
setTimeout(function () {
cm.openConfirm = originalOpenConfirm;
}, 0);
var pos = cm.getCursor();
callback();
@ -758,13 +807,13 @@ function setupGlobalSearch() {
wrapAround |= cmp <= 0;
var dlg = cm.getWrapperElement().querySelector(".CodeMirror-dialog");
if (!dlg || cmp == 0 || wrapAround && CodeMirror.cmpPos(cm.getCursor(), origPos) >= 0) {
if (!dlg || cmp === 0 || wrapAround && CodeMirror.cmpPos(cm.getCursor(), origPos) >= 0) {
if (dlg) {
dlg.remove();
}
doReplace();
}
}
};
});
originalOpenConfirm.call(cm, template.replaceConfirm.innerHTML, ovrCallbacks, opt);
};
@ -790,7 +839,9 @@ function jumpToLine(cm) {
if (m) {
cm.setCursor(m[1] - 1, m[2] ? m[2] - 1 : cur.ch);
}
}, {value: cur.line+1});
}, {
value: cur.line + 1
});
}
function refocusMinidialog(cm) {
@ -822,11 +873,19 @@ function getEditorInSight(nearbyElement) {
}
if (!cm || offscreenDistance(cm) > 0) {
var sorted = editors
.map(function(cm, index) { return {cm: cm, distance: offscreenDistance(cm), index: index} })
.sort(function(a, b) { return a.distance - b.distance || a.index - b.index });
.map(function (cm, index) {
return {
cm: cm,
distance: offscreenDistance(cm),
index: index
};
})
.sort(function (a, b) {
return a.distance - b.distance || a.index - b.index;
});
cm = sorted[0].cm;
if (sorted[0].distance > 0) {
makeSectionVisible(cm)
makeSectionVisible(cm);
}
}
return cm;
@ -845,7 +904,7 @@ function getEditorInSight(nearbyElement) {
}
function updateLintReport(cm, delay) {
if (delay == 0) {
if (delay === 0) {
// immediately show pending csslint messages in onbeforeunload and save
update.call(cm);
return;
@ -866,7 +925,7 @@ function updateLintReport(cm, delay) {
var state = cm.state.lint;
clearTimeout(state.reportTimeout);
state.reportTimeout = setTimeout(update.bind(cm), state.options.delay + 100);
state.postponeNewIssues = delay == undefined || delay == null;
state.postponeNewIssues = delay === undefined || delay === null;
function update() { // this == cm
var scope = this ? [this] : editors;
@ -876,7 +935,7 @@ function updateLintReport(cm, delay) {
var state = cm.state.lint;
var oldMarkers = state.markedLast || {};
var newMarkers = {};
var html = state.marked.length == 0 ? "" : "<tbody>" +
var html = state.marked.length === 0 ? "" : "<tbody>" +
state.marked.map(function (mark) {
var info = mark.__annotation;
var isActiveLine = info.from.line == cm.getCursor().line;
@ -915,9 +974,19 @@ function updateLintReport(cm, delay) {
}
}
}
function escapeHtml(html) {
var chars = {"&": "&amp;", "<": "&lt;", ">": "&gt;", '"': '&quot;', "'": '&#39;', "/": '&#x2F;'};
return html.replace(/[&<>"'\/]/g, function(char) { return chars[char] });
var chars = {
"&": "&amp;",
"<": "&lt;",
">": "&gt;",
'"': '&quot;',
"'": '&#39;',
"/": '&#x2F;'
};
return html.replace(/[&<>"'\/]/g, function (char) {
return chars[char];
});
}
}
@ -986,6 +1055,7 @@ function beautify(event) {
script.src = "beautify/beautify-css.js";
script.onload = doBeautify;
}
function doBeautify() {
var tabs = prefs.get("editor.indentWithTabs");
var options = prefs.get("editor.beautify");
@ -1061,7 +1131,9 @@ function init() {
var params = getParams();
if (!params.id) { // match should be 2 - one for the whole thing, one for the parentheses
// This is an add
var section = {code: ""}
var section = {
code: ""
};
for (var i in CssToProperty) {
if (params[i]) {
section[CssToProperty[i]] = [params[i]];
@ -1069,7 +1141,7 @@ function init() {
}
addSection(null, section);
// default to enabled
document.getElementById("enabled").checked = true
document.getElementById("enabled").checked = true;
tE("heading", "addStyleTitle");
initHooks();
return;
@ -1077,8 +1149,12 @@ function init() {
// This is an edit
tE("heading", "editStyleHeading", null, false);
requestStyle();
function requestStyle() {
chrome.runtime.sendMessage({method: "getStyles", id: params.id}, function callback(styles) {
chrome.runtime.sendMessage({
method: "getStyles",
id: params.id
}, function callback(styles) {
if (!styles) { // Chrome is starting up and shows edit.html
requestStyle();
return;
@ -1095,8 +1171,12 @@ function initWithStyle(style) {
document.getElementById("enabled").checked = style.enabled;
document.getElementById("url").href = style.url;
// if this was done in response to an update, we need to clear existing sections
getSections().forEach(function(div) { div.remove(); });
var queue = style.sections.length ? style.sections : [{code: ""}];
getSections().forEach(function (div) {
div.remove();
});
var queue = style.sections.length ? style.sections : [{
code: ""
}];
var queueStart = new Date().getTime();
// after 100ms the sections will be added asynchronously
while (new Date().getTime() - queueStart <= 100 && queue.length) {
@ -1146,7 +1226,10 @@ function initHooks() {
function maximizeCodeHeight(sectionDiv, isLast) {
var cm = sectionDiv.CodeMirror;
var stats = maximizeCodeHeight.stats = maximizeCodeHeight.stats || {totalHeight: 0, deltas: []};
var stats = maximizeCodeHeight.stats = maximizeCodeHeight.stats || {
totalHeight: 0,
deltas: []
};
if (!stats.cmActualHeight) {
stats.cmActualHeight = getComputedHeight(cm.display.wrapper);
}
@ -1180,7 +1263,9 @@ function maximizeCodeHeight(sectionDiv, isLast) {
if (available <= 0) {
return;
}
var totalDelta = stats.deltas.reduce(function(sum, d) { return sum + d; }, 0);
var totalDelta = stats.deltas.reduce(function (sum, d) {
return sum + d;
}, 0);
var q = available / totalDelta;
var baseHeight = stats.cmActualHeight - stats.sectionMarginTop;
stats.deltas.forEach(function (delta, index) {
@ -1199,7 +1284,7 @@ function updateTitle() {
function validate() {
var name = document.getElementById("name").value;
if (name == "") {
if (name === "") {
return t("styleMissingName");
}
// validate the regexps
@ -1259,7 +1344,7 @@ function getSectionsHashes() {
getSections().forEach(function (div) {
var meta = getMeta(div);
var code = div.CodeMirror.getValue();
if (/^\s*$/.test(code) && Object.keys(meta).length == 0) {
if (/^\s*$/.test(code) && Object.keys(meta).length === 0) {
return;
}
meta.code = code;
@ -1269,7 +1354,12 @@ function getSectionsHashes() {
}
function getMeta(e) {
var meta = {urls: [], urlPrefixes: [], domains: [], regexps: []};
var meta = {
urls: [],
urlPrefixes: [],
domains: [],
regexps: []
};
e.querySelector(".applies-to-list").childNodes.forEach(function (li) {
if (li.className == template.appliesToEverything.className) {
return;
@ -1297,7 +1387,9 @@ function saveComplete(style) {
}
function showMozillaFormat() {
var popup = showCodeMirrorPopup(t("styleToMozillaFormatTitle"), "", {readOnly: true});
var popup = showCodeMirrorPopup(t("styleToMozillaFormatTitle"), "", {
readOnly: true
});
popup.codebox.setValue(toMozillaFormat());
popup.codebox.execCommand("selectAll");
}
@ -1317,10 +1409,10 @@ function toMozillaFormat() {
}
function fromMozillaFormat() {
var popup = showCodeMirrorPopup(t("styleFromMozillaFormatPrompt"), tHTML("<div>\
<button name='import-append' i18n-text='importAppendLabel' i18n-title='importAppendTooltip'></button>\
<button name='import-replace' i18n-text='importReplaceLabel' i18n-title='importReplaceTooltip'></button>\
</div>").innerHTML);
var popup = showCodeMirrorPopup(t("styleFromMozillaFormatPrompt"), tHTML("<div>" +
"<button name='import-append' i18n-text='importAppendLabel' i18n-title='importAppendTooltip'></button>" +
"<button name='import-replace' i18n-text='importReplaceLabel' i18n-title='importReplaceTooltip'></button>" +
"</div>").innerHTML);
var contents = popup.querySelector(".contents");
contents.insertBefore(popup.codebox.display.wrapper, contents.firstElementChild);
@ -1340,15 +1432,26 @@ function fromMozillaFormat() {
var replaceOldStyle = this.name == "import-replace";
popup.querySelector(".close-icon").click();
var mozStyle = trimNewLines(popup.codebox.getValue());
var parser = new exports.css.Parser(), lines = mozStyle.split("\n");
var sectionStack = [{code: "", start: {line: 1, col: 1}}];
var errors = "", oldSectionCount = editors.length;
var parser = new exports.css.Parser(),
lines = mozStyle.split("\n");
var sectionStack = [{
code: "",
start: {
line: 1,
col: 1
}
}];
var errors = "",
oldSectionCount = editors.length;
var firstAddedCM;
parser.addListener("startdocument", function (e) {
var outerText = getRange(sectionStack.last.start, (--e.col, e));
var gapComment = outerText.match(/(\/\*[\s\S]*?\*\/)[\s\n]*$/);
var section = {code: "", start: backtrackTo(this, exports.css.Tokens.LBRACE, "end")};
var section = {
code: "",
start: backtrackTo(this, exports.css.Tokens.LBRACE, "end")
};
// move last comment before @-moz-document inside the section
if (gapComment && !gapComment[1].match(/\/\*\s*AGENT_SHEET\s*\*\//)) {
section.code = gapComment[1] + "\n";
@ -1378,7 +1481,10 @@ function fromMozillaFormat() {
parser.addListener("endstylesheet", function () {
// add nonclosed outer sections (either broken or the last global one)
var endOfText = {line: lines.length, col: lines.last.length + 1};
var endOfText = {
line: lines.length,
col: lines.last.length + 1
};
sectionStack.last.code += getRange(sectionStack.last.start, endOfText);
sectionStack.forEach(doAddSection);
@ -1410,6 +1516,7 @@ function fromMozillaFormat() {
"\n" + lines[end.line - 1].substring(0, end.col - 1));
}
}
function doAddSection(section) {
if (!firstAddedCM) {
if (!initFirstSection(section)) {
@ -1432,25 +1539,34 @@ function fromMozillaFormat() {
}
if (replaceOldStyle) {
editors.slice(0).reverse().forEach(function (cm) {
removeSection({target: cm.getSection().firstElementChild});
removeSection({
target: cm.getSection().firstElementChild
});
});
} else if (!editors.last.getValue()) {
// nuke the last blank section
if (editors.last.getSection().querySelector(".applies-to-everything")) {
removeSection({target: editors.last.getSection()});
removeSection({
target: editors.last.getSection()
});
}
}
return true;
}
}
function backtrackTo(parser, tokenType, startEnd) {
var tokens = parser._tokenStream._lt;
for (var i = tokens.length - 2; i >= 0; --i) {
if (tokens[i].type == tokenType) {
return {line: tokens[i][startEnd+"Line"], col: tokens[i][startEnd+"Col"]};
return {
line: tokens[i][startEnd + "Line"],
col: tokens[i][startEnd + "Col"]
};
}
}
}
function trimNewLines(s) {
return s.replace(/^[\s\n]+/, "").replace(/[\s\n]+$/, "");
}
@ -1471,9 +1587,19 @@ function showToMozillaHelp() {
function showKeyMapHelp() {
var keyMap = mergeKeyMaps({}, prefs.get("editor.keyMap"), CodeMirror.defaults.extraKeys);
var keyMapSorted = Object.keys(keyMap)
.map(function(key) { return {key: key, cmd: keyMap[key]} })
.concat([{key: "Shift-Ctrl-Wheel", cmd: "scrollWindow"}])
.sort(function(a, b) { return a.cmd < b.cmd || (a.cmd == b.cmd && a.key < b.key) ? -1 : 1 });
.map(function (key) {
return {
key: key,
cmd: keyMap[key]
};
})
.concat([{
key: "Shift-Ctrl-Wheel",
cmd: "scrollWindow"
}])
.sort(function (a, b) {
return a.cmd < b.cmd || (a.cmd == b.cmd && a.key < b.key) ? -1 : 1;
});
showHelp(t("cm_keyMap") + ": " + prefs.get("editor.keyMap"),
'<table class="keymap-list">' +
'<thead><tr><th><input placeholder="' + t("helpKeyMapHotkey") + '" type="search"></th>' +
@ -1506,6 +1632,7 @@ function showKeyMapHelp() {
this.value = normalizedKey.replace("-dummy", "");
filterTable(event);
}
function filterTable(event) {
var input = event.target;
var query = stringAsRegExp(input.value, "gi");
@ -1520,6 +1647,7 @@ function showKeyMapHelp() {
cell.innerHTML = cell.textContent;
});
}
function mergeKeyMaps(merged) {
[].slice.call(arguments, 1).forEach(function (keyMap) {
if (typeof keyMap == "string") {
@ -1589,14 +1717,21 @@ function showCodeMirrorPopup(title, html, options) {
foldGutter: true,
gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter", "CodeMirror-lint-markers"],
matchBrackets: true,
lint: {getAnnotations: CodeMirror.lint.css, delay: 0},
lint: {
getAnnotations: CodeMirror.lint.css,
delay: 0
},
styleActiveLine: true,
theme: prefs.get("editor.theme"),
keyMap: prefs.get("editor.keyMap")
}, options));
popup.codebox.focus();
popup.codebox.on("focus", function() { hotkeyRerouter.setState(false) });
popup.codebox.on("blur", function() { hotkeyRerouter.setState(true) });
popup.codebox.on("focus", function () {
hotkeyRerouter.setState(false);
});
popup.codebox.on("blur", function () {
hotkeyRerouter.setState(true);
});
return popup;
}
@ -1642,3 +1777,4 @@ function getComputedHeight(el) {
return el.getBoundingClientRect().height +
parseFloat(compStyle.marginTop) + parseFloat(compStyle.marginBottom);
}
}());

View File

@ -1,3 +1,4 @@
/*jshint undef:false*/
healthCheck();
function healthCheck() {

View File

@ -1,5 +1,9 @@
chrome.runtime.sendMessage({method: "getStyles", url: getMeta("stylish-id-url") || location.href}, function(response) {
if (response.length == 0) {
/*jshint undef:false*/
chrome.runtime.sendMessage({
method: "getStyles",
url: getMeta("stylish-id-url") || location.href
}, function (response) {
if (response.length === 0) {
sendEvent("styleCanBeInstalledChrome");
} else {
var installedStyle = response[0];
@ -9,16 +13,22 @@ chrome.runtime.sendMessage({method: "getStyles", url: getMeta("stylish-id-url")
if (md5Url && installedStyle.md5Url && installedStyle.originalMd5) {
getResource(md5Url, function (md5) {
if (md5 == installedStyle.originalMd5) {
sendEvent("styleAlreadyInstalledChrome", {updateUrl: installedStyle.updateUrl});
sendEvent("styleAlreadyInstalledChrome", {
updateUrl: installedStyle.updateUrl
});
} else {
sendEvent("styleCanBeUpdatedChrome", {updateUrl: installedStyle.updateUrl});
sendEvent("styleCanBeUpdatedChrome", {
updateUrl: installedStyle.updateUrl
});
}
});
} else {
getResource(getMeta("stylish-code-chrome"), function (code) {
// this would indicate a failure (a style with settings?).
if (code == null) {
sendEvent("styleCanBeUpdatedChrome", {updateUrl: installedStyle.updateUrl});
if (code === null) {
sendEvent("styleCanBeUpdatedChrome", {
updateUrl: installedStyle.updateUrl
});
}
var json = JSON.parse(code);
if (json.sections.length == installedStyle.sections.length) {
@ -28,11 +38,15 @@ chrome.runtime.sendMessage({method: "getStyles", url: getMeta("stylish-id-url")
});
})) {
// everything's the same
sendEvent("styleAlreadyInstalledChrome", {updateUrl: installedStyle.updateUrl});
sendEvent("styleAlreadyInstalledChrome", {
updateUrl: installedStyle.updateUrl
});
return;
};
}
sendEvent("styleCanBeUpdatedChrome", {updateUrl: installedStyle.updateUrl});
}
sendEvent("styleCanBeUpdatedChrome", {
updateUrl: installedStyle.updateUrl
});
});
}
}
@ -50,9 +64,9 @@ function sectionsAreEqual(a, b) {
function arraysAreEqual(a, b) {
// treat empty array and undefined as equivalent
if (typeof a == "undefined")
return (typeof b == "undefined") || (b.length == 0);
return (typeof b == "undefined") || (b.length === 0);
if (typeof b == "undefined")
return (typeof a == "undefined") || (a.length == 0);
return (typeof a == "undefined") || (a.length === 0);
if (a.length != b.length) {
return false;
}
@ -65,7 +79,9 @@ function sendEvent(type, data) {
if (typeof data == "undefined") {
data = null;
}
var stylishEvent = new CustomEvent(type, {detail: data});
var stylishEvent = new CustomEvent(type, {
detail: data
});
document.dispatchEvent(stylishEvent);
}
@ -86,7 +102,10 @@ document.addEventListener("stylishInstallChrome", function() {
}, false);
document.addEventListener("stylishUpdateChrome", function () {
chrome.runtime.sendMessage({method: "getStyles", url: getMeta("stylish-id-url") || location.href}, function(response) {
chrome.runtime.sendMessage({
method: "getStyles",
url: getMeta("stylish-id-url") || location.href
}, function (response) {
var style = response[0];
if (confirm(chrome.i18n.getMessage('styleUpdate', [style.name]))) {
getResource(getMeta("stylish-code-chrome"), function (code) {
@ -108,7 +127,7 @@ function getMeta(name) {
}
function getResource(url, callback) {
if (url.indexOf("#") == 0) {
if (url.indexOf("#") === 0) {
if (callback) {
callback(document.getElementById(url.substring(1)).innerText);
}
@ -123,7 +142,7 @@ function getResource(url, callback) {
callback(xhr.responseText);
}
}
}
};
if (url.length > 2000) {
var parts = url.split("?");
xhr.open("POST", parts[0], true);

View File

@ -1,9 +1,10 @@
/*jshint undef:false*/
var template = {};
tDocLoader();
function t(key, params) {
var s = chrome.i18n.getMessage(key, params)
if (s == "") {
var s = chrome.i18n.getMessage(key, params);
if (s === "") {
throw "Missing string '" + key + "'.";
}
return s;
@ -44,7 +45,7 @@ function tNodeList(nodes) {
for (var a = node.attributes.length - 1; a >= 0; a--) {
var attr = node.attributes[a];
var name = attr.nodeName;
if (name.indexOf("i18n-") != 0) {
if (name.indexOf("i18n-") !== 0) {
continue;
}
name = name.substr(5); // "i18n-".length

101
manage.js
View File

@ -1,3 +1,4 @@
/*jshint undef:false*/
var lastUpdatedStyleId = null;
var installed;
@ -5,11 +6,15 @@ var appliesToExtraTemplate = document.createElement("span");
appliesToExtraTemplate.className = "applies-to-extra";
appliesToExtraTemplate.innerHTML = " " + t('appliesDisplayTruncatedSuffix');
chrome.runtime.sendMessage({method: "getStyles"}, showStyles);
chrome.runtime.sendMessage({
method: "getStyles"
}, showStyles);
function showStyles(styles) {
if (!styles) { // Chrome is starting up
chrome.runtime.sendMessage({method: "getStyles"}, showStyles);
chrome.runtime.sendMessage({
method: "getStyles"
}, showStyles);
return;
}
if (!installed) {
@ -18,7 +23,9 @@ function showStyles(styles) {
document.stylishStyles = styles;
return;
}
styles.sort(function(a, b) { return a.name.localeCompare(b.name)});
styles.sort(function (a, b) {
return a.name.localeCompare(b.name);
});
styles.map(createStyleElement).forEach(function (e) {
installed.appendChild(e);
});
@ -44,7 +51,7 @@ function createStyleElement(style) {
var styleName = e.querySelector(".style-name");
styleName.appendChild(document.createTextNode(style.name));
if (style.url) {
var homepage = template.styleHomepage.cloneNode(true)
var homepage = template.styleHomepage.cloneNode(true);
homepage.setAttribute("href", style.url);
styleName.appendChild(document.createTextNode(" "));
styleName.appendChild(homepage);
@ -53,6 +60,7 @@ function createStyleElement(style) {
var urls = [];
var urlPrefixes = [];
var regexps = [];
function add(array, property) {
style.sections.forEach(function (section) {
if (section[property]) {
@ -60,7 +68,7 @@ function createStyleElement(style) {
return array.indexOf(value) == -1;
}).forEach(function (value) {
array.push(value);
});;
});
}
});
}
@ -74,12 +82,16 @@ function createStyleElement(style) {
if (urls)
appliesToToShow = appliesToToShow.concat(urls);
if (urlPrefixes)
appliesToToShow = appliesToToShow.concat(urlPrefixes.map(function(u) { return u + "*"; }));
appliesToToShow = appliesToToShow.concat(urlPrefixes.map(function (u) {
return u + "*";
}));
if (regexps)
appliesToToShow = appliesToToShow.concat(regexps.map(function(u) { return "/" + u + "/"; }));
appliesToToShow = appliesToToShow.concat(regexps.map(function (u) {
return "/" + u + "/";
}));
var appliesToString = "";
var showAppliesToExtra = false;
if (appliesToToShow.length == "")
if (appliesToToShow.length === "")
appliesToString = t('appliesToEverything');
else if (appliesToToShow.length <= 10)
appliesToString = appliesToToShow.join(", ");
@ -95,8 +107,10 @@ function createStyleElement(style) {
editLink.setAttribute("href", editLink.getAttribute("href") + style.id);
editLink.addEventListener("click", function (event) {
if (!event.altKey) {
var left = event.button == 0, middle = event.button == 1,
shift = event.shiftKey, ctrl = event.ctrlKey;
var left = event.button === 0,
middle = event.button == 1,
shift = event.shiftKey,
ctrl = event.ctrlKey;
var openWindow = left && shift && !ctrl;
var openBackgroundTab = (middle && !shift) || (left && ctrl && !shift);
var openForegroundTab = (middle && shift) || (left && ctrl && shift);
@ -116,7 +130,9 @@ function createStyleElement(style) {
});
}
} else {
history.replaceState({scrollY: window.scrollY}, document.title);
history.replaceState({
scrollY: window.scrollY
}, document.title);
getActiveTab(function (tab) {
sessionStorageHash("manageStylesHistory").set(tab.id, url);
location.href = url;
@ -124,8 +140,12 @@ function createStyleElement(style) {
}
}
});
e.querySelector(".enable").addEventListener("click", function(event) { enable(event, true); }, false);
e.querySelector(".disable").addEventListener("click", function(event) { enable(event, false); }, false);
e.querySelector(".enable").addEventListener("click", function (event) {
enable(event, true);
}, false);
e.querySelector(".disable").addEventListener("click", function (event) {
enable(event, false);
}, false);
e.querySelector(".check-update").addEventListener("click", doCheckUpdate, false);
e.querySelector(".update").addEventListener("click", doUpdate, false);
e.querySelector(".delete").addEventListener("click", doDelete, false);
@ -181,7 +201,7 @@ function handleUpdate(style) {
lastUpdatedStyleId = null;
element.className = element.className += " update-done";
element.querySelector(".update-note").innerHTML = t('updateCompleted');
};
}
}
function handleDelete(id) {
@ -225,7 +245,7 @@ function checkUpdateAll() {
if (success) {
++updatableCount;
}
if (--toCheckCount == 0) {
if (--toCheckCount === 0) {
btnCheck.disabled = false;
if (updatableCount) {
btnApply.classList.remove("hidden");
@ -249,7 +269,10 @@ function checkUpdate(element, callback) {
var originalMd5 = element.getAttribute("style-original-md5");
function handleSuccess(forceUpdate, serverJson) {
chrome.runtime.sendMessage({method: "getStyles", id: id}, function(styles) {
chrome.runtime.sendMessage({
method: "getStyles",
id: id
}, function (styles) {
var style = styles[0];
var needsUpdate = false;
if (!forceUpdate && codeIsEqual(style.sections, serverJson.sections)) {
@ -265,7 +288,7 @@ function checkUpdate(element, callback) {
}
function handleFailure(status) {
if (status == 0) {
if (status === 0) {
handleNeedsUpdate(t('updateCheckFailServerUnreachable'), id, null);
} else {
handleNeedsUpdate(t('updateCheckFailBadResponseCode', [status]), id, null);
@ -276,7 +299,7 @@ function checkUpdate(element, callback) {
}
if (!md5Url || !originalMd5) {
checkUpdateFullCode(url, false, handleSuccess, handleFailure)
checkUpdateFullCode(url, false, handleSuccess, handleFailure);
} else {
checkUpdateMd5(originalMd5, md5Url, function (needsUpdate) {
if (needsUpdate) {
@ -313,12 +336,12 @@ function download(url, successCallback, failureCallback) {
xhr.onreadystatechange = function (aEvt) {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
successCallback(xhr.responseText)
successCallback(xhr.responseText);
} else {
failureCallback(xhr.status);
}
}
}
};
if (url.length > 2000) {
var parts = url.split("?");
xhr.open("POST", parts[0], true);
@ -387,11 +410,13 @@ function codeIsEqual(a, b) {
}
function jsonEquals(a, b, property) {
var aProp = a[property], typeA = getType(aProp);
var bProp = b[property], typeB = getType(bProp);
var aProp = a[property],
typeA = getType(aProp);
var bProp = b[property],
typeB = getType(bProp);
if (typeA != typeB) {
// consider empty arrays equivalent to lack of property
if ((typeA == "undefined" || (typeA == "array" && aProp.length == 0)) && (typeB == "undefined" || (typeB == "array" && bProp.length == 0))) {
if ((typeA == "undefined" || (typeA == "array" && aProp.length === 0)) && (typeB == "undefined" || (typeB == "array" && bProp.length === 0))) {
return true;
}
return false;
@ -427,8 +452,11 @@ function searchStyles(immediately) {
clearTimeout(searchStyles.timeout);
searchStyles.timeout = setTimeout(doSearch, 100);
}
function doSearch() {
chrome.runtime.sendMessage({method: "getStyles"}, function(styles) {
chrome.runtime.sendMessage({
method: "getStyles"
}, function (styles) {
styles.forEach(function (style) {
var el = document.querySelector("[style-id='" + style.id + "']");
if (el) {
@ -437,17 +465,21 @@ function searchStyles(immediately) {
});
});
}
function isMatchingStyle(style) {
return style.sections.some(function (section) {
return Object.keys(section).some(function (key) {
var value = section[key];
switch (typeof value) {
case "string": return isMatchingText(value);
case "object": return value.some(isMatchingText);
case "string":
return isMatchingText(value);
case "object":
return value.some(isMatchingText);
}
});
});
}
function isMatchingText(text) {
return text.toLocaleLowerCase().indexOf(query) >= 0;
}
@ -456,9 +488,12 @@ function searchStyles(immediately) {
function onFilterChange(className, event) {
installed.classList.toggle(className, event.target.checked);
}
function initFilter(className, node) {
node.addEventListener("change", onFilterChange.bind(undefined, className), false);
onFilterChange(className, {target: node});
onFilterChange(className, {
target: node
});
}
function importStyles(e) {
@ -471,9 +506,8 @@ function importStyles (e) {
if (style) {
delete style.id;
saveStyle(style, save);
}
else {
window.location.reload()
} else {
window.location.reload();
}
}
@ -481,15 +515,14 @@ function importStyles (e) {
try {
styles = JSON.parse(evt.target.result);
save();
}
catch (e) {
} catch (e) {
window.alert(e.message);
}
};
reader.onerror = function (e) {
window.alert(e.message);
}
reader.readAsText(file)
};
reader.readAsText(file);
}
function selectAll() {

View File

@ -1,5 +1,8 @@
/*jshint undef:false*/
function notifyAllTabs(request) {
chrome.windows.getAll({populate: true}, function(windows) {
chrome.windows.getAll({
populate: true
}, function (windows) {
windows.forEach(function (win) {
win.tabs.forEach(function (tab) {
chrome.tabs.sendMessage(tab.id, request);
@ -8,7 +11,10 @@ function notifyAllTabs(request) {
});
});
// notify all open popups
var reqPopup = shallowMerge({}, request, {method: "updatePopup", reason: request.method});
var reqPopup = shallowMerge({}, request, {
method: "updatePopup",
reason: request.method
});
chrome.runtime.sendMessage(reqPopup);
}
@ -37,21 +43,30 @@ function updateIcon(tab, styles) {
getTabRealURL(tab, function (url) {
// if we have access to this, call directly. a page sending a message to itself doesn't seem to work right.
if (typeof getStyles != "undefined") {
getStyles({matchUrl: url, enabled: true}, stylesReceived);
getStyles({
matchUrl: url,
enabled: true
}, stylesReceived);
} else {
chrome.runtime.sendMessage({method: "getStyles", matchUrl: url, enabled: true}, stylesReceived);
chrome.runtime.sendMessage({
method: "getStyles",
matchUrl: url,
enabled: true
}, stylesReceived);
}
});
function stylesReceived(styles) {
var disableAll = "disableAll" in styles ? styles.disableAll : prefs.get("disableAll");
var postfix = styles.length == 0 || disableAll ? "w" : "";
var postfix = styles.length === 0 || disableAll ? "w" : "";
chrome.browserAction.setIcon({
path: {
// Material Design 2016 new size is 16px
16: "16" + postfix + ".png", 32: "32" + postfix + ".png",
16: "16" + postfix + ".png",
32: "32" + postfix + ".png",
// Chromium forks or non-chromium browsers may still use the traditional 19px
19: "19" + postfix + ".png", 38: "38" + postfix + ".png",
19: "19" + postfix + ".png",
38: "38" + postfix + ".png",
},
tabId: tab.id
}, function () {
@ -59,8 +74,13 @@ function updateIcon(tab, styles) {
// e.g. 'windowPosition' pref updated in edit.js::window.onbeforeunload
if (!chrome.runtime.lastError) {
var t = prefs.get("show-badge") && styles.length ? ("" + styles.length) : "";
chrome.browserAction.setBadgeText({text: t, tabId: tab.id});
chrome.browserAction.setBadgeBackgroundColor({color: disableAll ? "#aaa" : [0, 0, 0, 0]});
chrome.browserAction.setBadgeText({
text: t,
tabId: tab.id
});
chrome.browserAction.setBadgeBackgroundColor({
color: disableAll ? "#aaa" : [0, 0, 0, 0]
});
}
});
//console.log("Tab " + tab.id + " (" + tab.url + ") badge text set to '" + t + "'.");
@ -68,7 +88,10 @@ function updateIcon(tab, styles) {
}
function getActiveTab(callback) {
chrome.tabs.query({currentWindow: true, active: true}, function(tabs) {
chrome.tabs.query({
currentWindow: true,
active: true
}, function (tabs) {
callback(tabs[0]);
});
}
@ -83,7 +106,11 @@ function getTabRealURL(tab, callback) {
if (tab.url != "chrome://newtab/") {
callback(tab.url);
} else {
chrome.webNavigation.getFrame({tabId: tab.id, frameId: 0, processId: -1}, function(frame) {
chrome.webNavigation.getFrame({
tabId: tab.id,
frameId: 0,
processId: -1
}, function (frame) {
frame && callback(frame.url);
});
}

View File

@ -1,3 +1,4 @@
/*jshint undef:false*/
var writeStyleTemplate = document.createElement("a");
writeStyleTemplate.className = "write-style-link";
@ -17,7 +18,10 @@ function updatePopUp(url) {
return;
}
chrome.runtime.sendMessage({method: "getStyles", matchUrl: url}, showStyles);
chrome.runtime.sendMessage({
method: "getStyles",
matchUrl: url
}, showStyles);
document.querySelector("#find-styles a").href = "https://userstyles.org/styles/browse/all/" + encodeURIComponent("file" === urlWillWork[1] ? "file:" : url);
// Write new style links
@ -29,22 +33,28 @@ function updatePopUp(url) {
var urlLink = writeStyleTemplate.cloneNode(true);
urlLink.href = "edit.html?url-prefix=" + encodeURIComponent(url);
urlLink.appendChild(document.createTextNode( // switchable; default="this&nbsp;URL"
!prefs.get("popup.breadcrumbs.usePath")
? t("writeStyleForURL").replace(/ /g, "\u00a0")
: /\/\/[^/]+\/(.*)/.exec(url)[1]
!prefs.get("popup.breadcrumbs.usePath") ? t("writeStyleForURL").replace(/ /g, "\u00a0") : /\/\/[^/]+\/(.*)/.exec(url)[1]
));
urlLink.title = "url-prefix(\"$\")".replace("$", url);
writeStyleLinks.push(urlLink);
document.querySelector("#write-style").appendChild(urlLink)
document.querySelector("#write-style").appendChild(urlLink);
if (prefs.get("popup.breadcrumbs")) { // switchable; default=enabled
urlLink.addEventListener("mouseenter", function(event) { this.parentNode.classList.add("url()") }, false);
urlLink.addEventListener("focus", function(event) { this.parentNode.classList.add("url()") }, false);
urlLink.addEventListener("mouseleave", function(event) { this.parentNode.classList.remove("url()") }, false);
urlLink.addEventListener("blur", function(event) { this.parentNode.classList.remove("url()") }, false);
urlLink.addEventListener("mouseenter", function (event) {
this.parentNode.classList.add("url()");
}, false);
urlLink.addEventListener("focus", function (event) {
this.parentNode.classList.add("url()");
}, false);
urlLink.addEventListener("mouseleave", function (event) {
this.parentNode.classList.remove("url()");
}, false);
urlLink.addEventListener("blur", function (event) {
this.parentNode.classList.remove("url()");
}, false);
}
// For domain
var domains = getDomains(url)
var domains = getDomains(url);
domains.forEach(function (domain) {
// Don't include TLD
if (domains.length > 1 && domain.indexOf(".") == -1) {
@ -76,7 +86,7 @@ function showStyles(styles) {
if (enabledFirst && a.enabled !== b.enabled) return !(a.enabled < b.enabled) ? -1 : 1;
return a.name.localeCompare(b.name);
});
if (styles.length == 0) {
if (styles.length === 0) {
installed.innerHTML = "<div class='entry' id='no-styles'>" + t('noStylesForSite') + "</div>";
}
styles.map(createStyleElement).forEach(function (e) {
@ -100,13 +110,24 @@ function createStyleElement(style) {
editLink.setAttribute("href", editLink.getAttribute("href") + style.id);
editLink.addEventListener("click", openLinkInTabOrWindow, false);
styleName.addEventListener("click", function() { this.checkbox.click(); event.preventDefault(); });
styleName.addEventListener("click", function () {
this.checkbox.click();
event.preventDefault();
});
// clicking the checkbox will toggle it, and this will run after that happens
checkbox.addEventListener("click", function() { enable(event, event.target.checked); }, false);
e.querySelector(".enable").addEventListener("click", function() { enable(event, true); }, false);
e.querySelector(".disable").addEventListener("click", function() { enable(event, false); }, false);
checkbox.addEventListener("click", function () {
enable(event, event.target.checked);
}, false);
e.querySelector(".enable").addEventListener("click", function () {
enable(event, true);
}, false);
e.querySelector(".disable").addEventListener("click", function () {
enable(event, false);
}, false);
e.querySelector(".delete").addEventListener("click", function() { doDelete(event, false); }, false);
e.querySelector(".delete").addEventListener("click", function () {
doDelete(event, false);
}, false);
return e;
}
@ -147,7 +168,9 @@ function getId(event) {
function openLinkInTabOrWindow(event) {
event.preventDefault();
if (prefs.get("openEditInWindow", false)) {
var options = {url: event.target.href}
var options = {
url: event.target.href
};
var wp = prefs.get("windowPosition", {});
for (var k in wp) options[k] = wp[k];
chrome.windows.create(options);
@ -159,7 +182,10 @@ function openLinkInTabOrWindow(event) {
function openLink(event) {
event.preventDefault();
chrome.runtime.sendMessage({method: "openURL", url: event.target.href});
chrome.runtime.sendMessage({
method: "openURL",
url: event.target.href
});
close();
}

View File

@ -1,3 +1,4 @@
/*jshint undef:false*/
var webSqlStorage = {
migrate: function () {
@ -10,7 +11,7 @@ var webSqlStorage = {
var tx = db.transaction(["styles"], "readwrite");
var os = tx.objectStore("styles");
styles.forEach(function (s) {
webSqlStorage.cleanStyle(s)
webSqlStorage.cleanStyle(s);
os.add(s);
});
// While this was running, the styles were loaded from the (empty) indexed db
@ -44,12 +45,13 @@ var webSqlStorage = {
var params = [];
t.executeSql('SELECT DISTINCT s.*, se.id section_id, se.code, sm.name metaName, sm.value metaValue FROM styles s LEFT JOIN sections se ON se.style_id = s.id LEFT JOIN section_meta sm ON sm.section_id = se.id WHERE 1' + where + ' ORDER BY s.id, se.id, sm.id', params, function (t, r) {
var styles = [];
var currentStyle = null;
var currentSection = null;
var styles = [],
currentStyle = null,
currentSection = null,
metaName;
for (var i = 0; i < r.rows.length; i++) {
var values = r.rows.item(i);
var metaName = null;
metaName = null;
switch (values.metaName) {
case null:
break;
@ -60,22 +62,34 @@ var webSqlStorage = {
metaName = "urlPrefixes";
break;
case "domain":
var metaName = "domains";
metaName = "domains";
break;
case "regexps":
var metaName = "regexps";
metaName = "regexps";
break;
default:
var metaName = values.metaName + "s";
metaName = values.metaName + "s";
}
var metaValue = values.metaValue;
if (currentStyle == null || currentStyle.id != values.id) {
currentStyle = {id: values.id, url: values.url, updateUrl: values.updateUrl, md5Url: values.md5Url, name: values.name, enabled: values.enabled == "true", originalMd5: values.originalMd5, sections: []};
if (currentStyle === null || currentStyle.id != values.id) {
currentStyle = {
id: values.id,
url: values.url,
updateUrl: values.updateUrl,
md5Url: values.md5Url,
name: values.name,
enabled: values.enabled == "true",
originalMd5: values.originalMd5,
sections: []
};
styles.push(currentStyle);
}
if (values.section_id != null) {
if (currentSection == null || currentSection.id != values.section_id) {
currentSection = {id: values.section_id, code: values.code};
if (values.section_id !== null) {
if (currentSection === null || currentSection.id != values.section_id) {
currentSection = {
id: values.section_id,
code: values.code
};
currentStyle.sections.push(currentSection);
}
if (metaName && metaValue) {
@ -100,7 +114,7 @@ var webSqlStorage = {
error();
throw ex;
}
if (stylishDb.version == "") {
if (stylishDb.version === "") {
// It didn't already exist, we have nothing to migrate.
ready(null);
return;
@ -125,7 +139,9 @@ var webSqlStorage = {
t.executeSql('CREATE TABLE styles (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, url TEXT, updateUrl TEXT, md5Url TEXT, name TEXT NOT NULL, code TEXT NOT NULL, enabled INTEGER NOT NULL, originalCode TEXT NULL);');
t.executeSql('CREATE TABLE style_meta (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, style_id INTEGER NOT NULL, name TEXT NOT NULL, value TEXT NOT NULL);');
t.executeSql('CREATE INDEX style_meta_style_id ON style_meta (style_id);');
}, error, function() { webSqlStorage.dbV12(d, error, done)});
}, error, function () {
webSqlStorage.dbV12(d, error, done);
});
},
dbV12: function (d, error, done) {
@ -144,7 +160,9 @@ var webSqlStorage = {
t.executeSql('INSERT INTO newstyles (id, url, updateUrl, md5Url, name, enabled) SELECT id, url, updateUrl, md5Url, name, enabled FROM styles;');
t.executeSql('DROP TABLE styles;');
t.executeSql('ALTER TABLE newstyles RENAME TO styles;');
}, error, function() { webSqlStorage.dbV13(d, error, done)});
}, error, function () {
webSqlStorage.dbV13(d, error, done);
});
},
dbV13: function (d, error, done) {
@ -152,18 +170,24 @@ var webSqlStorage = {
// clear out orphans
t.executeSql('DELETE FROM section_meta WHERE section_id IN (SELECT sections.id FROM sections LEFT JOIN styles ON styles.id = sections.style_id WHERE styles.id IS NULL);');
t.executeSql('DELETE FROM sections WHERE id IN (SELECT sections.id FROM sections LEFT JOIN styles ON styles.id = sections.style_id WHERE styles.id IS NULL);');
}, error, function() { webSqlStorage.dbV14(d, error, done)});
}, error, function () {
webSqlStorage.dbV14(d, error, done);
});
},
dbV14: function (d, error, done) {
d.changeVersion(d.version, '1.4', function (t) {
t.executeSql('UPDATE styles SET url = null WHERE url = "undefined";');
}, error, function() { webSqlStorage.dbV15(d, error, done)});
}, error, function () {
webSqlStorage.dbV15(d, error, done);
});
},
dbV15: function (d, error, done) {
d.changeVersion(d.version, '1.5', function (t) {
t.executeSql('ALTER TABLE styles ADD COLUMN originalMd5 TEXT NULL;');
}, error, function() { done(d); });
}
}, error, function () {
done(d);
});
}
};

View File

@ -1,3 +1,4 @@
/*jshint undef:false*/
function getDatabase(ready, error) {
var dbOpenRequest = window.indexedDB.open("stylish", 2);
dbOpenRequest.onsuccess = function (e) {
@ -10,16 +11,21 @@ function getDatabase(ready, error) {
}
};
dbOpenRequest.onupgradeneeded = function (event) {
if (event.oldVersion == 0) {
var os = event.target.result.createObjectStore("styles", {keyPath: 'id', autoIncrement: true});
if (event.oldVersion === 0) {
var os = event.target.result.createObjectStore("styles", {
keyPath: 'id',
autoIncrement: true
});
webSqlStorage.migrate();
}
}
};
}
var cachedStyles = null,
request;
var cachedStyles = null;
function getStyles(options, callback) {
if (cachedStyles != null) {
if (cachedStyles !== null) {
callback(filterStyles(cachedStyles, options));
return;
}
@ -30,8 +36,8 @@ function getStyles(options, callback) {
os.openCursor().onsuccess = function (event) {
var cursor = event.target.result;
if (cursor) {
var s = cursor.value
s.id = cursor.key
var s = cursor.value;
s.id = cursor.key;
all.push(cursor.value);
cursor.continue();
} else {
@ -45,7 +51,9 @@ function getStyles(options, callback) {
function invalidateCache(andNotify) {
cachedStyles = null;
if (andNotify) {
chrome.runtime.sendMessage({method: "invalidateCache"});
chrome.runtime.sendMessage({
method: "invalidateCache"
});
}
}
@ -55,26 +63,28 @@ function filterStyles(styles, options) {
var id = "id" in options ? Number(options.id) : null;
var matchUrl = "matchUrl" in options ? options.matchUrl : null;
if (enabled != null) {
if (enabled !== null) {
styles = styles.filter(function (style) {
return style.enabled == enabled;
});
}
if (url != null) {
if (url !== null) {
styles = styles.filter(function (style) {
return style.url == url;
});
}
if (id != null) {
if (id !== null) {
styles = styles.filter(function (style) {
return style.id == id;
});
}
if (matchUrl != null) {
if (matchUrl !== null) {
// Return as a hash from style to applicable sections? Can only be used with matchUrl.
var asHash = "asHash" in options ? options.asHash : false;
if (asHash) {
var h = {disableAll: prefs.get("disableAll", false)};
var h = {
disableAll: prefs.get("disableAll", false)
};
styles.forEach(function (style) {
var applicableSections = getApplicableSections(style, matchUrl);
if (applicableSections.length > 0) {
@ -98,7 +108,7 @@ function saveStyle(o, callback) {
// Update
if (o.id) {
var request = os.get(Number(o.id));
request = os.get(Number(o.id));
request.onsuccess = function (event) {
var style = request.result;
for (var prop in o) {
@ -109,7 +119,10 @@ function saveStyle(o, callback) {
}
request = os.put(style);
request.onsuccess = function (event) {
notifyAllTabs({method: "styleUpdated", style: style});
notifyAllTabs({
method: "styleUpdated",
style: style
});
invalidateCache(true);
if (callback) {
callback(style);
@ -139,13 +152,16 @@ function saveStyle(o, callback) {
o.enabled = true;
}
// Make sure it's not null - that makes indexeddb sad
delete o["id"];
var request = os.add(o);
delete o.id;
request = os.add(o);
request.onsuccess = function (event) {
invalidateCache(true);
// Give it the ID that was generated
o.id = event.target.result;
notifyAllTabs({method: "styleAdded", style: o});
notifyAllTabs({
method: "styleAdded",
style: o
});
if (callback) {
callback(o);
}
@ -154,9 +170,15 @@ function saveStyle(o, callback) {
}
function enableStyle(id, enabled) {
saveStyle({id: id, enabled: enabled}, function(style) {
saveStyle({
id: id,
enabled: enabled
}, function (style) {
handleUpdate(style);
notifyAllTabs({method: "styleUpdated", style: style});
notifyAllTabs({
method: "styleUpdated",
style: style
});
});
}
@ -164,16 +186,20 @@ function deleteStyle(id) {
getDatabase(function (db) {
var tx = db.transaction(["styles"], "readwrite");
var os = tx.objectStore("styles");
var request = os.delete(Number(id));
request = os.delete(Number(id));
request.onsuccess = function (event) {
handleDelete(id);
invalidateCache(true);
notifyAllTabs({method: "styleDeleted", id: id});
notifyAllTabs({
method: "styleDeleted",
id: id
});
};
});
}
function reportError() {
var i;
for (i in arguments) {
if ("message" in arguments[i]) {
//alert(arguments[i].message);
@ -190,7 +216,7 @@ function fixBoolean(b) {
}
function getDomains(url) {
if (url.indexOf("file:") == 0) {
if (url.indexOf("file:") === 0) {
return [];
}
var d = /.*?:\/*([^\/:]+)/.exec(url)[1];
@ -213,6 +239,7 @@ function getType(o) {
}
var namespacePattern = /^\s*(@namespace[^;]+;\s*)+$/;
function getApplicableSections(style, url) {
var sections = style.sections.filter(function (section) {
return sectionAppliesToUrl(section, url);
@ -226,14 +253,14 @@ function getApplicableSections(style, url) {
function sectionAppliesToUrl(section, url) {
// only http, https, file, and chrome-extension allowed
if (url.indexOf("http") != 0 && url.indexOf("file") != 0 && url.indexOf("chrome-extension") != 0 && url.indexOf("ftp") != 0) {
if (url.indexOf("http") !== 0 && url.indexOf("file") !== 0 && url.indexOf("chrome-extension") !== 0 && url.indexOf("ftp") !== 0) {
return false;
}
// other extensions can't be styled
if (url.indexOf("chrome-extension") == 0 && url.indexOf(chrome.extension.getURL("")) != 0) {
if (url.indexOf("chrome-extension") === 0 && url.indexOf(chrome.extension.getURL("")) !== 0) {
return false;
}
if (section.urls.length == 0 && section.domains.length == 0 && section.urlPrefixes.length == 0 && section.regexps.length == 0) {
if (section.urls.length === 0 && section.domains.length === 0 && section.urlPrefixes.length === 0 && section.regexps.length === 0) {
//console.log(section.id + " is global");
return true;
}
@ -242,7 +269,7 @@ function sectionAppliesToUrl(section, url) {
return true;
}
if (section.urlPrefixes.some(function (prefix) {
return url.indexOf(prefix) == 0;
return url.indexOf(prefix) === 0;
})) {
//console.log(section.id + " applies to " + url + " due to URL prefix rules");
return true;
@ -261,7 +288,9 @@ function sectionAppliesToUrl(section, url) {
if (regexp[regexp.length - 1] != "$") {
regexp += "$";
}
var re = runTryCatch(function() { return new RegExp(regexp) });
var re = runTryCatch(function () {
return new RegExp(regexp);
});
if (re) {
return (re).test(url);
} else {
@ -282,8 +311,9 @@ function isCheckbox(el) {
// js engine can't optimize the entire function if it contains try-catch
// so we should keep it isolated from normal code in a minimal wrapper
function runTryCatch(func) {
try { return func() }
catch(e) {}
try {
return func();
} catch (e) {}
}
// Accepts an array of pref names (values are fetched via prefs.get)
@ -301,10 +331,14 @@ function setupLivePrefs(IDs) {
updateElement(request.prefName);
}
});
function updateElement(id) {
var el = document.getElementById(id);
el[isCheckbox(el) ? "checked" : "value"] = prefs.get(id);
el.dispatchEvent(new Event("change", {bubbles: true, cancelable: true}));
el.dispatchEvent(new Event("change", {
bubbles: true,
cancelable: true
}));
return el;
}
}
@ -349,7 +383,9 @@ var prefs = chrome.extension.getBackgroundPage().prefs || new function Prefs() {
var syncTimeout; // see broadcast() function below
Object.defineProperty(this, "readOnlyValues", {value: {}});
Object.defineProperty(this, "readOnlyValues", {
value: {}
});
Prefs.prototype.get = function (key, defaultValue) {
if (key in values) {
@ -377,32 +413,47 @@ var prefs = chrome.extension.getBackgroundPage().prefs || new function Prefs() {
}
};
Prefs.prototype.remove = function(key) { me.set(key, undefined) };
Prefs.prototype.remove = function (key) {
me.set(key, undefined);
};
Prefs.prototype.broadcast = function (key, value, options) {
var message = {method: "prefChanged", prefName: key, value: value};
var message = {
method: "prefChanged",
prefName: key,
value: value
};
notifyAllTabs(message);
chrome.runtime.sendMessage(message);
if (key == "disableAll") {
notifyAllTabs({method: "styleDisableAll", disableAll: value});
notifyAllTabs({
method: "styleDisableAll",
disableAll: value
});
}
if (!options || !options.noSync) {
clearTimeout(syncTimeout);
syncTimeout = setTimeout(function () {
getSync().set({"settings": values});
getSync().set({
"settings": values
});
}, 0);
}
};
Object.keys(defaults).forEach(function (key) {
me.set(key, defaults[key], {noBroadcast: true});
me.set(key, defaults[key], {
noBroadcast: true
});
});
getSync().get("settings", function (result) {
var synced = result.settings;
for (var key in defaults) {
if (synced && (key in synced)) {
me.set(key, synced[key], {noSync: true});
me.set(key, synced[key], {
noSync: true
});
} else {
var value = tryMigrating(key);
if (value !== undefined) {
@ -414,16 +465,21 @@ var prefs = chrome.extension.getBackgroundPage().prefs || new function Prefs() {
chrome.storage.onChanged.addListener(function (changes, area) {
if (area == "sync" && "settings" in changes) {
var synced = changes.settings.newValue;
var synced = changes.settings.newValue,
key;
if (synced) {
for (key in defaults) {
if (key in synced) {
me.set(key, synced[key], {noSync: true});
me.set(key, synced[key], {
noSync: true
});
}
}
} else {
// user manually deleted our settings, we'll recreate them
getSync().set({"settings": values});
getSync().set({
"settings": values
});
}
}
});
@ -450,16 +506,22 @@ var prefs = chrome.extension.getBackgroundPage().prefs || new function Prefs() {
}
return value;
}
};
}();
function getCodeMirrorThemes(callback) {
chrome.runtime.getPackageDirectoryEntry(function (rootDir) {
rootDir.getDirectory("codemirror/theme", {create: false}, function(themeDir) {
rootDir.getDirectory("codemirror/theme", {
create: false
}, function (themeDir) {
themeDir.createReader().readEntries(function (entries) {
var themes = [chrome.i18n.getMessage("defaultTheme")];
entries
.filter(function(entry) { return entry.isFile })
.sort(function(a, b) { return a.name < b.name ? -1 : 1 })
.filter(function (entry) {
return entry.isFile;
})
.sort(function (a, b) {
return a.name < b.name ? -1 : 1;
})
.forEach(function (entry) {
themes.push(entry.name.replace(/\.css$/, ""));
});
@ -474,14 +536,24 @@ function getCodeMirrorThemes(callback) {
function sessionStorageHash(name) {
var hash = {
value: {},
set: function(k, v) { this.value[k] = v; this.updateStorage(); },
unset: function(k) { delete this.value[k]; this.updateStorage(); },
set: function (k, v) {
this.value[k] = v;
this.updateStorage();
},
unset: function (k) {
delete this.value[k];
this.updateStorage();
},
updateStorage: function () {
sessionStorage[this.name] = JSON.stringify(this.value);
}
};
try { hash.value = JSON.parse(sessionStorage[name]); } catch(e) {}
Object.defineProperty(hash, "name", {value: name});
try {
hash.value = JSON.parse(sessionStorage[name]);
} catch (e) {}
Object.defineProperty(hash, "name", {
value: name
});
return hash;
}
@ -544,7 +616,10 @@ function defineReadonlyProperty(obj, key, value) {
if (typeof copy == "object") {
Object.freeze(copy);
}
Object.defineProperty(obj, key, {value: copy, configurable: true})
Object.defineProperty(obj, key, {
value: copy,
configurable: true
});
}
// Polyfill, can be removed when Firefox gets this - https://bugzilla.mozilla.org/show_bug.cgi?id=1220494
@ -565,5 +640,5 @@ function getSync() {
}
callback();
}
}
};
}