Fix linting problems and more readable code

This commit is contained in:
DecentM 2017-01-20 11:31:15 +01:00
parent 970d48b17a
commit 560f5b666e
No known key found for this signature in database
GPG Key ID: C342F139246AF4CD
12 changed files with 3855 additions and 13379 deletions

View File

@ -1,3 +1,4 @@
/*jshint undef:false*/
var g_disableAll = false; var g_disableAll = false;
var g_styleElements = {}; var g_styleElements = {};
var iframeObserver; var iframeObserver;
@ -11,8 +12,13 @@ function requestStyles() {
// we'll request the styles directly to minimize delay and flicker, // we'll request the styles directly to minimize delay and flicker,
// unless Chrome still starts up and the background page isn't fully loaded. // 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.) // (Note: in this case the function may be invoked again from applyStyles.)
var request = {method: "getStyles", matchUrl: location.href, enabled: true, asHash: true}; var request = {
if (location.href.indexOf(chrome.extension.getURL("")) == 0) { method: "getStyles",
matchUrl: location.href,
enabled: true,
asHash: true
};
if (location.href.indexOf(chrome.extension.getURL("")) === 0) {
var bg = chrome.extension.getBackgroundPage(); var bg = chrome.extension.getBackgroundPage();
if (bg && bg.getStyles) { if (bg && bg.getStyles) {
// apply styles immediately, then proceed with a normal request that will update the icon // apply styles immediately, then proceed with a normal request that will update the icon
@ -22,8 +28,20 @@ function requestStyles() {
chrome.runtime.sendMessage(request, applyStyles); chrome.runtime.sendMessage(request, applyStyles);
} }
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) { chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
// Also handle special request just for the pop-up // 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) { switch (request.method == "updatePopup" ? request.reason : request.method) {
case "styleDeleted": case "styleDeleted":
removeStyle(request.id, document); removeStyle(request.id, document);
@ -31,15 +49,13 @@ chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
case "styleUpdated": case "styleUpdated":
if (request.style.enabled) { if (request.style.enabled) {
retireStyle(request.style.id); retireStyle(request.style.id);
// fallthrough to "styleAdded" styleAdded();
} else { } else {
removeStyle(request.style.id, document); removeStyle(request.style.id, document);
}
break; break;
}
case "styleAdded": case "styleAdded":
if (request.style.enabled) { styleAdded();
chrome.runtime.sendMessage({method: "getStyles", matchUrl: location.href, enabled: true, id: request.style.id, asHash: true}, applyStyles);
}
break; break;
case "styleApply": case "styleApply":
applyStyles(request.styles); applyStyles(request.styles);
@ -69,12 +85,12 @@ function disableAll(disable) {
} }
function disableSheets(disable, doc) { function disableSheets(disable, doc) {
Array.prototype.forEach.call(doc.styleSheets, function(stylesheet) { Array.prototype.forEach.call(doc.styleSheets, function (stylesheet) {
if (stylesheet.ownerNode.classList.contains("stylish")) { if (stylesheet.ownerNode.classList.contains("stylish")) {
stylesheet.disabled = disable; stylesheet.disabled = disable;
} }
}); });
getDynamicIFrames(doc).forEach(function(iframe) { getDynamicIFrames(doc).forEach(function (iframe) {
if (!disable) { if (!disable) {
// update the IFRAME if it was created while the observer was disconnected // update the IFRAME if it was created while the observer was disconnected
addDocumentStylesToIFrame(iframe); addDocumentStylesToIFrame(iframe);
@ -90,10 +106,10 @@ function removeStyle(id, doc) {
if (e) { if (e) {
e.remove(); e.remove();
} }
if (doc == document && Object.keys(g_styleElements).length == 0) { if (doc == document && Object.keys(g_styleElements).length === 0) {
iframeObserver.disconnect(); iframeObserver.disconnect();
} }
getDynamicIFrames(doc).forEach(function(iframe) { getDynamicIFrames(doc).forEach(function (iframe) {
removeStyle(id, iframe.contentDocument); removeStyle(id, iframe.contentDocument);
}); });
} }
@ -114,7 +130,7 @@ function retireStyle(id, doc) {
if (e) { if (e) {
e.id = "stylish-" + deadID; e.id = "stylish-" + deadID;
} }
getDynamicIFrames(doc).forEach(function(iframe) { getDynamicIFrames(doc).forEach(function (iframe) {
retireStyle(id, iframe.contentDocument); retireStyle(id, iframe.contentDocument);
}); });
} }
@ -142,14 +158,14 @@ function applyStyles(styleHash) {
document.head.appendChild(document.getElementById(id)); document.head.appendChild(document.getElementById(id));
} }
} }
document.addEventListener("DOMContentLoaded", function() { document.addEventListener("DOMContentLoaded", function () {
addDocumentStylesToAllIFrames(); addDocumentStylesToAllIFrames();
iframeObserver.start(); iframeObserver.start();
}); });
} }
if (retiredStyleIds.length) { if (retiredStyleIds.length) {
setTimeout(function() { setTimeout(function () {
while (retiredStyleIds.length) { while (retiredStyleIds.length) {
removeStyle(retiredStyleIds.shift(), document); removeStyle(retiredStyleIds.shift(), document);
} }
@ -173,7 +189,7 @@ function applySections(styleId, sections) {
styleElement.setAttribute("id", "stylish-" + styleId); styleElement.setAttribute("id", "stylish-" + styleId);
styleElement.setAttribute("class", "stylish"); styleElement.setAttribute("class", "stylish");
styleElement.setAttribute("type", "text/css"); styleElement.setAttribute("type", "text/css");
styleElement.appendChild(document.createTextNode(sections.map(function(section) { styleElement.appendChild(document.createTextNode(sections.map(function (section) {
return section.code; return section.code;
}).join("\n"))); }).join("\n")));
addStyleElement(styleElement, document); addStyleElement(styleElement, document);
@ -186,7 +202,7 @@ function addStyleElement(styleElement, doc) {
} }
doc.documentElement.appendChild(doc.importNode(styleElement, true)) doc.documentElement.appendChild(doc.importNode(styleElement, true))
.disabled = g_disableAll; .disabled = g_disableAll;
getDynamicIFrames(doc).forEach(function(iframe) { getDynamicIFrames(doc).forEach(function (iframe) {
if (iframeIsLoadingSrcDoc(iframe)) { if (iframeIsLoadingSrcDoc(iframe)) {
addStyleToIFrameSrcDoc(iframe, styleElement); addStyleToIFrameSrcDoc(iframe, styleElement);
} else { } else {
@ -224,7 +240,7 @@ function iframeIsDynamic(f) {
// Cross-origin, so it's not a dynamic iframe // Cross-origin, so it's not a dynamic iframe
return false; return false;
} }
return href == document.location.href || href.indexOf("about:") == 0; return href == document.location.href || href.indexOf("about:") === 0;
} }
function iframeIsLoadingSrcDoc(f) { function iframeIsLoadingSrcDoc(f) {
@ -244,9 +260,11 @@ function addStyleToIFrameSrcDoc(iframe, styleElement) {
function replaceAll(newStyles, doc, pass2) { function replaceAll(newStyles, doc, pass2) {
var oldStyles = [].slice.call(doc.querySelectorAll("STYLE.stylish" + (pass2 ? "[id$='-ghost']" : ""))); var oldStyles = [].slice.call(doc.querySelectorAll("STYLE.stylish" + (pass2 ? "[id$='-ghost']" : "")));
if (!pass2) { if (!pass2) {
oldStyles.forEach(function(style) { style.id += "-ghost"; }); oldStyles.forEach(function (style) {
style.id += "-ghost";
});
} }
getDynamicIFrames(doc).forEach(function(iframe) { getDynamicIFrames(doc).forEach(function (iframe) {
replaceAll(newStyles, iframe.contentDocument, pass2); replaceAll(newStyles, iframe.contentDocument, pass2);
}); });
if (doc == document && !pass2) { if (doc == document && !pass2) {
@ -255,13 +273,15 @@ function replaceAll(newStyles, doc, pass2) {
replaceAll(newStyles, doc, true); replaceAll(newStyles, doc, true);
} }
if (pass2) { if (pass2) {
oldStyles.forEach(function(style) { style.remove(); }); oldStyles.forEach(function (style) {
style.remove();
});
} }
} }
// Observe dynamic IFRAMEs being added // Observe dynamic IFRAMEs being added
function initObserver() { function initObserver() {
iframeObserver = new MutationObserver(function(mutations) { iframeObserver = new MutationObserver(function (mutations) {
if (mutations.length > 1000) { if (mutations.length > 1000) {
// use a much faster method for very complex pages with 100,000 mutations // use a much faster method for very complex pages with 100,000 mutations
// (observer usually receives 1k-10k mutations per call) // (observer usually receives 1k-10k mutations per call)
@ -288,8 +308,11 @@ function initObserver() {
} }
} }
iframeObserver.start = function() { iframeObserver.start = function () {
// will be ignored by browser if already observing // 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; var frameIdMessageable;
runTryCatch(function() { runTryCatch(function () {
chrome.tabs.sendMessage(0, {}, {frameId: 0}, function() { chrome.tabs.sendMessage(0, {}, {
frameId: 0
}, function () {
var clearError = chrome.runtime.lastError; var clearError = chrome.runtime.lastError;
frameIdMessageable = true; frameIdMessageable = true;
}); });
@ -14,28 +17,41 @@ if ("onHistoryStateUpdated" in chrome.webNavigation) {
chrome.webNavigation.onHistoryStateUpdated.addListener(webNavigationListener.bind(this, "styleReplaceAll")); chrome.webNavigation.onHistoryStateUpdated.addListener(webNavigationListener.bind(this, "styleReplaceAll"));
} }
chrome.webNavigation.onBeforeNavigate.addListener(webNavigationListener.bind(this, null)); chrome.webNavigation.onBeforeNavigate.addListener(webNavigationListener.bind(this, null));
function webNavigationListener(method, data) { function webNavigationListener(method, data) {
// Until Chrome 41, we can't target a frame with a message // Until Chrome 41, we can't target a frame with a message
// (https://developer.chrome.com/extensions/tabs#method-sendMessage) // (https://developer.chrome.com/extensions/tabs#method-sendMessage)
// so a style affecting a page with an iframe will affect the main page as well. // 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. // Skip doing this for frames in pre-41 to prevent page flicker.
if (data.frameId != 0 && !frameIdMessageable) { if (data.frameId !== 0 && !frameIdMessageable) {
return; return;
} }
getStyles({matchUrl: data.url, enabled: true, asHash: true}, function(styleHash) { getStyles({
matchUrl: data.url,
enabled: true,
asHash: true
}, function (styleHash) {
if (method) { if (method) {
chrome.tabs.sendMessage(data.tabId, {method: method, styles: styleHash}, chrome.tabs.sendMessage(data.tabId, {
frameIdMessageable ? {frameId: data.frameId} : undefined); method: method,
styles: styleHash
},
frameIdMessageable ? {
frameId: data.frameId
} : undefined);
} }
if (data.frameId == 0) { if (data.frameId === 0) {
updateIcon({id: data.tabId, url: data.url}, styleHash); updateIcon({
id: data.tabId,
url: data.url
}, styleHash);
} }
}); });
} }
// catch direct URL hash modifications not invoked via HTML5 history API // catch direct URL hash modifications not invoked via HTML5 history API
var tabUrlHasHash = {}; var tabUrlHasHash = {};
chrome.tabs.onUpdated.addListener(function(tabId, info, tab) { chrome.tabs.onUpdated.addListener(function (tabId, info, tab) {
if (info.status == "loading" && info.url) { if (info.status == "loading" && info.url) {
if (info.url.indexOf('#') > 0) { if (info.url.indexOf('#') > 0) {
tabUrlHasHash[tabId] = true; tabUrlHasHash[tabId] = true;
@ -45,21 +61,23 @@ chrome.tabs.onUpdated.addListener(function(tabId, info, tab) {
// do nothing since the tab neither had # before nor has # now // do nothing since the tab neither had # before nor has # now
return; 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) { chrome.tabs.onRemoved.addListener(function (tabId, info) {
delete tabUrlHasHash[tabId]; delete tabUrlHasHash[tabId];
}); });
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) { chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
switch (request.method) { switch (request.method) {
case "getStyles": case "getStyles":
var styles = getStyles(request, sendResponse); var styles = getStyles(request, sendResponse);
// check if this is a main content frame style enumeration // check if this is a main content frame style enumeration
if (request.matchUrl && !request.id if (request.matchUrl && !request.id && sender && sender.tab && sender.frameId === 0 && sender.tab.url == request.matchUrl) {
&& sender && sender.tab && sender.frameId == 0
&& sender.tab.url == request.matchUrl) {
updateIcon(sender.tab, styles); updateIcon(sender.tab, styles);
} }
return true; return true;
@ -72,17 +90,25 @@ chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
} }
break; break;
case "healthCheck": case "healthCheck":
getDatabase(function() { sendResponse(true); }, function() { sendResponse(false); }); getDatabase(function () {
sendResponse(true);
}, function () {
sendResponse(false);
});
return true; return true;
case "openURL": case "openURL":
openURL(request); openURL(request);
break; break;
case "styleDisableAll": case "styleDisableAll":
chrome.contextMenus.update("disableAll", {checked: request.disableAll}); chrome.contextMenus.update("disableAll", {
checked: request.disableAll
});
break; break;
case "prefChanged": case "prefChanged":
if (request.prefName == "show-badge") { if (request.prefName == "show-badge") {
chrome.contextMenus.update("show-badge", {checked: request.value}); chrome.contextMenus.update("show-badge", {
checked: request.value
});
} }
break; break;
} }
@ -91,14 +117,18 @@ chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
// Not available in Firefox - https://bugzilla.mozilla.org/show_bug.cgi?id=1240350 // Not available in Firefox - https://bugzilla.mozilla.org/show_bug.cgi?id=1240350
if ("commands" in chrome) { if ("commands" in chrome) {
chrome.commands.onCommand.addListener(function(command) { chrome.commands.onCommand.addListener(function (command) {
switch (command) { switch (command) {
case "openManage": case "openManage":
openURL({url: chrome.extension.getURL("manage.html")}); openURL({
url: chrome.extension.getURL("manage.html")
});
break; break;
case "styleDisableAll": case "styleDisableAll":
disableAllStylesToggle(); disableAllStylesToggle();
chrome.contextMenus.update("disableAll", {checked: prefs.get("disableAll")}); chrome.contextMenus.update("disableAll", {
checked: prefs.get("disableAll")
});
break; break;
} }
}); });
@ -106,18 +136,28 @@ if ("commands" in chrome) {
// contextMenus API is present in ancient Chrome but it throws an exception // contextMenus API is present in ancient Chrome but it throws an exception
// upon encountering the unsupported parameter value "browser_action", so we have to catch it. // upon encountering the unsupported parameter value "browser_action", so we have to catch it.
runTryCatch(function() { runTryCatch(function () {
chrome.contextMenus.create({ chrome.contextMenus.create({
id: "show-badge", title: chrome.i18n.getMessage("menuShowBadge"), id: "show-badge",
type: "checkbox", contexts: ["browser_action"], checked: prefs.get("show-badge") title: chrome.i18n.getMessage("menuShowBadge"),
}, function() { var clearError = chrome.runtime.lastError }); type: "checkbox",
contexts: ["browser_action"],
checked: prefs.get("show-badge")
}, function () {
var clearError = chrome.runtime.lastError;
});
chrome.contextMenus.create({ chrome.contextMenus.create({
id: "disableAll", title: chrome.i18n.getMessage("disableAllStyles"), id: "disableAll",
type: "checkbox", contexts: ["browser_action"], checked: prefs.get("disableAll") title: chrome.i18n.getMessage("disableAllStyles"),
}, function() { var clearError = chrome.runtime.lastError }); type: "checkbox",
contexts: ["browser_action"],
checked: prefs.get("disableAll")
}, function () {
var clearError = chrome.runtime.lastError;
});
}); });
chrome.contextMenus.onClicked.addListener(function(info, tab) { chrome.contextMenus.onClicked.addListener(function (info, tab) {
if (info.menuItemId == "disableAll") { if (info.menuItemId == "disableAll") {
disableAllStylesToggle(info.checked); disableAllStylesToggle(info.checked);
} else { } else {
@ -133,14 +173,16 @@ function disableAllStylesToggle(newState) {
} }
// Get the DB so that any first run actions will be performed immediately when the background page loads. // Get the DB so that any first run actions will be performed immediately when the background page loads.
getDatabase(function() {}, reportError); getDatabase(function () {}, reportError);
// When an edit page gets attached or detached, remember its state so we can do the same to the next one to open. // When an edit page gets attached or detached, remember its state so we can do the same to the next one to open.
var editFullUrl = chrome.extension.getURL("edit.html"); var editFullUrl = chrome.extension.getURL("edit.html");
chrome.tabs.onAttached.addListener(function(tabId, data) { chrome.tabs.onAttached.addListener(function (tabId, data) {
chrome.tabs.get(tabId, function(tabData) { chrome.tabs.get(tabId, function (tabData) {
if (tabData.url.indexOf(editFullUrl) == 0) { if (tabData.url.indexOf(editFullUrl) === 0) {
chrome.windows.get(tabData.windowId, {populate: true}, function(win) { 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 // If there's only one tab in this window, it's been dragged to new window
prefs.set("openEditInWindow", win.tabs.length == 1); prefs.set("openEditInWindow", win.tabs.length == 1);
}); });
@ -149,13 +191,19 @@ chrome.tabs.onAttached.addListener(function(tabId, data) {
}); });
function openURL(options) { 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 // switch to an existing tab with the requested url
if (tabs.length) { 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 { } else {
delete options.method; delete options.method;
getActiveTab(function(tab) { getActiveTab(function (tab) {
// re-use an active new tab page // re-use an active new tab page
chrome.tabs[tab.url == "chrome://newtab/" ? "update" : "create"](options); chrome.tabs[tab.url == "chrome://newtab/" ? "update" : "create"](options);
}); });
@ -164,6 +212,6 @@ function openURL(options) {
} }
var codeMirrorThemes; var codeMirrorThemes;
getCodeMirrorThemes(function(themes) { getCodeMirrorThemes(function (themes) {
codeMirrorThemes = themes; codeMirrorThemes = themes;
}); });

File diff suppressed because it is too large Load Diff

752
edit.js

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -1,5 +1,9 @@
chrome.runtime.sendMessage({method: "getStyles", url: getMeta("stylish-id-url") || location.href}, function(response) { /*jshint undef:false*/
if (response.length == 0) { chrome.runtime.sendMessage({
method: "getStyles",
url: getMeta("stylish-id-url") || location.href
}, function (response) {
if (response.length === 0) {
sendEvent("styleCanBeInstalledChrome"); sendEvent("styleCanBeInstalledChrome");
} else { } else {
var installedStyle = response[0]; var installedStyle = response[0];
@ -7,32 +11,42 @@ chrome.runtime.sendMessage({method: "getStyles", url: getMeta("stylish-id-url")
// use the md5 if available // use the md5 if available
var md5Url = getMeta("stylish-md5-url"); var md5Url = getMeta("stylish-md5-url");
if (md5Url && installedStyle.md5Url && installedStyle.originalMd5) { if (md5Url && installedStyle.md5Url && installedStyle.originalMd5) {
getResource(md5Url, function(md5) { getResource(md5Url, function (md5) {
if (md5 == installedStyle.originalMd5) { if (md5 == installedStyle.originalMd5) {
sendEvent("styleAlreadyInstalledChrome", {updateUrl: installedStyle.updateUrl}); sendEvent("styleAlreadyInstalledChrome", {
updateUrl: installedStyle.updateUrl
});
} else { } else {
sendEvent("styleCanBeUpdatedChrome", {updateUrl: installedStyle.updateUrl}); sendEvent("styleCanBeUpdatedChrome", {
updateUrl: installedStyle.updateUrl
});
} }
}); });
} else { } else {
getResource(getMeta("stylish-code-chrome"), function(code) { getResource(getMeta("stylish-code-chrome"), function (code) {
// this would indicate a failure (a style with settings?). // this would indicate a failure (a style with settings?).
if (code == null) { if (code === null) {
sendEvent("styleCanBeUpdatedChrome", {updateUrl: installedStyle.updateUrl}); sendEvent("styleCanBeUpdatedChrome", {
updateUrl: installedStyle.updateUrl
});
} }
var json = JSON.parse(code); var json = JSON.parse(code);
if (json.sections.length == installedStyle.sections.length) { if (json.sections.length == installedStyle.sections.length) {
if (json.sections.every(function(section) { if (json.sections.every(function (section) {
return installedStyle.sections.some(function(installedSection) { return installedStyle.sections.some(function (installedSection) {
return sectionsAreEqual(section, installedSection); return sectionsAreEqual(section, installedSection);
}); });
})) { })) {
// everything's the same // everything's the same
sendEvent("styleAlreadyInstalledChrome", {updateUrl: installedStyle.updateUrl}); sendEvent("styleAlreadyInstalledChrome", {
updateUrl: installedStyle.updateUrl
});
return; return;
};
} }
sendEvent("styleCanBeUpdatedChrome", {updateUrl: installedStyle.updateUrl}); }
sendEvent("styleCanBeUpdatedChrome", {
updateUrl: installedStyle.updateUrl
});
}); });
} }
} }
@ -42,7 +56,7 @@ function sectionsAreEqual(a, b) {
if (a.code != b.code) { if (a.code != b.code) {
return false; return false;
} }
return ["urls", "urlPrefixes", "domains", "regexps"].every(function(attribute) { return ["urls", "urlPrefixes", "domains", "regexps"].every(function (attribute) {
return arraysAreEqual(a[attribute], b[attribute]); return arraysAreEqual(a[attribute], b[attribute]);
}); });
} }
@ -50,13 +64,13 @@ function sectionsAreEqual(a, b) {
function arraysAreEqual(a, b) { function arraysAreEqual(a, b) {
// treat empty array and undefined as equivalent // treat empty array and undefined as equivalent
if (typeof a == "undefined") if (typeof a == "undefined")
return (typeof b == "undefined") || (b.length == 0); return (typeof b == "undefined") || (b.length === 0);
if (typeof b == "undefined") if (typeof b == "undefined")
return (typeof a == "undefined") || (a.length == 0); return (typeof a == "undefined") || (a.length === 0);
if (a.length != b.length) { if (a.length != b.length) {
return false; return false;
} }
return a.every(function(entry) { return a.every(function (entry) {
return b.indexOf(entry) != -1; return b.indexOf(entry) != -1;
}); });
} }
@ -65,18 +79,20 @@ function sendEvent(type, data) {
if (typeof data == "undefined") { if (typeof data == "undefined") {
data = null; data = null;
} }
var stylishEvent = new CustomEvent(type, {detail: data}); var stylishEvent = new CustomEvent(type, {
detail: data
});
document.dispatchEvent(stylishEvent); document.dispatchEvent(stylishEvent);
} }
document.addEventListener("stylishInstallChrome", function() { document.addEventListener("stylishInstallChrome", function () {
getResource(getMeta("stylish-description"), function(name) { getResource(getMeta("stylish-description"), function (name) {
if (confirm(chrome.i18n.getMessage('styleInstall', [name]))) { if (confirm(chrome.i18n.getMessage('styleInstall', [name]))) {
getResource(getMeta("stylish-code-chrome"), function(code) { getResource(getMeta("stylish-code-chrome"), function (code) {
// check for old style json // check for old style json
var json = JSON.parse(code); var json = JSON.parse(code);
json.method = "saveStyle"; json.method = "saveStyle";
chrome.runtime.sendMessage(json, function(response) { chrome.runtime.sendMessage(json, function (response) {
sendEvent("styleInstalledChrome"); sendEvent("styleInstalledChrome");
}); });
}); });
@ -85,15 +101,18 @@ document.addEventListener("stylishInstallChrome", function() {
}); });
}, false); }, false);
document.addEventListener("stylishUpdateChrome", function() { 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]; var style = response[0];
if (confirm(chrome.i18n.getMessage('styleUpdate', [style.name]))) { if (confirm(chrome.i18n.getMessage('styleUpdate', [style.name]))) {
getResource(getMeta("stylish-code-chrome"), function(code) { getResource(getMeta("stylish-code-chrome"), function (code) {
var json = JSON.parse(code); var json = JSON.parse(code);
json.method = "saveStyle"; json.method = "saveStyle";
json.id = style.id; json.id = style.id;
chrome.runtime.sendMessage(json, function() { chrome.runtime.sendMessage(json, function () {
sendEvent("styleInstalledChrome"); sendEvent("styleInstalledChrome");
}); });
}); });
@ -108,14 +127,14 @@ function getMeta(name) {
} }
function getResource(url, callback) { function getResource(url, callback) {
if (url.indexOf("#") == 0) { if (url.indexOf("#") === 0) {
if (callback) { if (callback) {
callback(document.getElementById(url.substring(1)).innerText); callback(document.getElementById(url.substring(1)).innerText);
} }
return; return;
} }
var xhr = new XMLHttpRequest(); var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() { xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && callback) { if (xhr.readyState == 4 && callback) {
if (xhr.status >= 400) { if (xhr.status >= 400) {
callback(null); callback(null);
@ -123,11 +142,11 @@ function getResource(url, callback) {
callback(xhr.responseText); callback(xhr.responseText);
} }
} }
} };
if (url.length > 2000) { if (url.length > 2000) {
var parts = url.split("?"); var parts = url.split("?");
xhr.open("POST", parts[0], true); xhr.open("POST", parts[0], true);
xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded"); xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhr.send(parts[1]); xhr.send(parts[1]);
} else { } else {
xhr.open("GET", url, true); xhr.open("GET", url, true);

View File

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

153
manage.js
View File

@ -1,3 +1,4 @@
/*jshint undef:false*/
var lastUpdatedStyleId = null; var lastUpdatedStyleId = null;
var installed; var installed;
@ -5,11 +6,15 @@ var appliesToExtraTemplate = document.createElement("span");
appliesToExtraTemplate.className = "applies-to-extra"; appliesToExtraTemplate.className = "applies-to-extra";
appliesToExtraTemplate.innerHTML = " " + t('appliesDisplayTruncatedSuffix'); appliesToExtraTemplate.innerHTML = " " + t('appliesDisplayTruncatedSuffix');
chrome.runtime.sendMessage({method: "getStyles"}, showStyles); chrome.runtime.sendMessage({
method: "getStyles"
}, showStyles);
function showStyles(styles) { function showStyles(styles) {
if (!styles) { // Chrome is starting up if (!styles) { // Chrome is starting up
chrome.runtime.sendMessage({method: "getStyles"}, showStyles); chrome.runtime.sendMessage({
method: "getStyles"
}, showStyles);
return; return;
} }
if (!installed) { if (!installed) {
@ -18,8 +23,10 @@ function showStyles(styles) {
document.stylishStyles = styles; document.stylishStyles = styles;
return; return;
} }
styles.sort(function(a, b) { return a.name.localeCompare(b.name)}); styles.sort(function (a, b) {
styles.map(createStyleElement).forEach(function(e) { return a.name.localeCompare(b.name);
});
styles.map(createStyleElement).forEach(function (e) {
installed.appendChild(e); installed.appendChild(e);
}); });
if (history.state) { if (history.state) {
@ -44,23 +51,24 @@ function createStyleElement(style) {
var styleName = e.querySelector(".style-name"); var styleName = e.querySelector(".style-name");
styleName.appendChild(document.createTextNode(style.name)); styleName.appendChild(document.createTextNode(style.name));
if (style.url) { if (style.url) {
var homepage = template.styleHomepage.cloneNode(true) var homepage = template.styleHomepage.cloneNode(true);
homepage.setAttribute("href", style.url); homepage.setAttribute("href", style.url);
styleName.appendChild(document.createTextNode(" " )); styleName.appendChild(document.createTextNode(" "));
styleName.appendChild(homepage); styleName.appendChild(homepage);
} }
var domains = []; var domains = [];
var urls = []; var urls = [];
var urlPrefixes = []; var urlPrefixes = [];
var regexps = []; var regexps = [];
function add(array, property) { function add(array, property) {
style.sections.forEach(function(section) { style.sections.forEach(function (section) {
if (section[property]) { if (section[property]) {
section[property].filter(function(value) { section[property].filter(function (value) {
return array.indexOf(value) == -1; return array.indexOf(value) == -1;
}).forEach(function(value) { }).forEach(function (value) {
array.push(value); array.push(value);
});; });
} }
}); });
} }
@ -74,12 +82,16 @@ function createStyleElement(style) {
if (urls) if (urls)
appliesToToShow = appliesToToShow.concat(urls); appliesToToShow = appliesToToShow.concat(urls);
if (urlPrefixes) if (urlPrefixes)
appliesToToShow = appliesToToShow.concat(urlPrefixes.map(function(u) { return u + "*"; })); appliesToToShow = appliesToToShow.concat(urlPrefixes.map(function (u) {
return u + "*";
}));
if (regexps) if (regexps)
appliesToToShow = appliesToToShow.concat(regexps.map(function(u) { return "/" + u + "/"; })); appliesToToShow = appliesToToShow.concat(regexps.map(function (u) {
return "/" + u + "/";
}));
var appliesToString = ""; var appliesToString = "";
var showAppliesToExtra = false; var showAppliesToExtra = false;
if (appliesToToShow.length == "") if (appliesToToShow.length === "")
appliesToString = t('appliesToEverything'); appliesToString = t('appliesToEverything');
else if (appliesToToShow.length <= 10) else if (appliesToToShow.length <= 10)
appliesToString = appliesToToShow.join(", "); appliesToString = appliesToToShow.join(", ");
@ -93,10 +105,12 @@ function createStyleElement(style) {
} }
var editLink = e.querySelector(".style-edit-link"); var editLink = e.querySelector(".style-edit-link");
editLink.setAttribute("href", editLink.getAttribute("href") + style.id); editLink.setAttribute("href", editLink.getAttribute("href") + style.id);
editLink.addEventListener("click", function(event) { editLink.addEventListener("click", function (event) {
if (!event.altKey) { if (!event.altKey) {
var left = event.button == 0, middle = event.button == 1, var left = event.button === 0,
shift = event.shiftKey, ctrl = event.ctrlKey; middle = event.button == 1,
shift = event.shiftKey,
ctrl = event.ctrlKey;
var openWindow = left && shift && !ctrl; var openWindow = left && shift && !ctrl;
var openBackgroundTab = (middle && !shift) || (left && ctrl && !shift); var openBackgroundTab = (middle && !shift) || (left && ctrl && !shift);
var openForegroundTab = (middle && shift) || (left && ctrl && shift); var openForegroundTab = (middle && shift) || (left && ctrl && shift);
@ -116,16 +130,22 @@ function createStyleElement(style) {
}); });
} }
} else { } else {
history.replaceState({scrollY: window.scrollY}, document.title); history.replaceState({
getActiveTab(function(tab) { scrollY: window.scrollY
}, document.title);
getActiveTab(function (tab) {
sessionStorageHash("manageStylesHistory").set(tab.id, url); sessionStorageHash("manageStylesHistory").set(tab.id, url);
location.href = url; location.href = url;
}); });
} }
} }
}); });
e.querySelector(".enable").addEventListener("click", function(event) { enable(event, true); }, false); e.querySelector(".enable").addEventListener("click", function (event) {
e.querySelector(".disable").addEventListener("click", function(event) { enable(event, false); }, false); 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(".check-update").addEventListener("click", doCheckUpdate, false);
e.querySelector(".update").addEventListener("click", doUpdate, false); e.querySelector(".update").addEventListener("click", doUpdate, false);
e.querySelector(".delete").addEventListener("click", doDelete, false); e.querySelector(".delete").addEventListener("click", doDelete, false);
@ -160,7 +180,7 @@ function getStyleElement(event) {
return null; return null;
} }
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) { chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
switch (request.method) { switch (request.method) {
case "styleUpdated": case "styleUpdated":
handleUpdate(request.style); handleUpdate(request.style);
@ -181,7 +201,7 @@ function handleUpdate(style) {
lastUpdatedStyleId = null; lastUpdatedStyleId = null;
element.className = element.className += " update-done"; element.className = element.className += " update-done";
element.querySelector(".update-note").innerHTML = t('updateCompleted'); element.querySelector(".update-note").innerHTML = t('updateCompleted');
}; }
} }
function handleDelete(id) { function handleDelete(id) {
@ -198,12 +218,12 @@ function doCheckUpdate(event) {
function applyUpdateAll() { function applyUpdateAll() {
var btnApply = document.getElementById("apply-all-updates"); var btnApply = document.getElementById("apply-all-updates");
btnApply.disabled = true; btnApply.disabled = true;
setTimeout(function() { setTimeout(function () {
btnApply.style.display = "none"; btnApply.style.display = "none";
btnApply.disabled = false; btnApply.disabled = false;
}, 1000); }, 1000);
Array.prototype.forEach.call(document.querySelectorAll(".can-update .update"), function(button) { Array.prototype.forEach.call(document.querySelectorAll(".can-update .update"), function (button) {
button.click(); button.click();
}); });
} }
@ -220,18 +240,18 @@ function checkUpdateAll() {
var elements = document.querySelectorAll("[style-update-url]"); var elements = document.querySelectorAll("[style-update-url]");
var toCheckCount = elements.length; var toCheckCount = elements.length;
var updatableCount = 0; var updatableCount = 0;
Array.prototype.forEach.call(elements, function(element) { Array.prototype.forEach.call(elements, function (element) {
checkUpdate(element, function(success) { checkUpdate(element, function (success) {
if (success) { if (success) {
++updatableCount; ++updatableCount;
} }
if (--toCheckCount == 0) { if (--toCheckCount === 0) {
btnCheck.disabled = false; btnCheck.disabled = false;
if (updatableCount) { if (updatableCount) {
btnApply.classList.remove("hidden"); btnApply.classList.remove("hidden");
} else { } else {
noUpdates.classList.remove("hidden"); noUpdates.classList.remove("hidden");
setTimeout(function() { setTimeout(function () {
noUpdates.classList.add("hidden"); noUpdates.classList.add("hidden");
}, 10000); }, 10000);
} }
@ -249,7 +269,10 @@ function checkUpdate(element, callback) {
var originalMd5 = element.getAttribute("style-original-md5"); var originalMd5 = element.getAttribute("style-original-md5");
function handleSuccess(forceUpdate, serverJson) { 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 style = styles[0];
var needsUpdate = false; var needsUpdate = false;
if (!forceUpdate && codeIsEqual(style.sections, serverJson.sections)) { if (!forceUpdate && codeIsEqual(style.sections, serverJson.sections)) {
@ -265,7 +288,7 @@ function checkUpdate(element, callback) {
} }
function handleFailure(status) { function handleFailure(status) {
if (status == 0) { if (status === 0) {
handleNeedsUpdate(t('updateCheckFailServerUnreachable'), id, null); handleNeedsUpdate(t('updateCheckFailServerUnreachable'), id, null);
} else { } else {
handleNeedsUpdate(t('updateCheckFailBadResponseCode', [status]), id, null); handleNeedsUpdate(t('updateCheckFailBadResponseCode', [status]), id, null);
@ -276,9 +299,9 @@ function checkUpdate(element, callback) {
} }
if (!md5Url || !originalMd5) { if (!md5Url || !originalMd5) {
checkUpdateFullCode(url, false, handleSuccess, handleFailure) checkUpdateFullCode(url, false, handleSuccess, handleFailure);
} else { } else {
checkUpdateMd5(originalMd5, md5Url, function(needsUpdate) { checkUpdateMd5(originalMd5, md5Url, function (needsUpdate) {
if (needsUpdate) { if (needsUpdate) {
// If the md5 shows a change we will update regardless of whether the code looks different // If the md5 shows a change we will update regardless of whether the code looks different
checkUpdateFullCode(url, true, handleSuccess, handleFailure); checkUpdateFullCode(url, true, handleSuccess, handleFailure);
@ -293,13 +316,13 @@ function checkUpdate(element, callback) {
} }
function checkUpdateFullCode(url, forceUpdate, successCallback, failureCallback) { function checkUpdateFullCode(url, forceUpdate, successCallback, failureCallback) {
download(url, function(responseText) { download(url, function (responseText) {
successCallback(forceUpdate, JSON.parse(responseText)); successCallback(forceUpdate, JSON.parse(responseText));
}, failureCallback); }, failureCallback);
} }
function checkUpdateMd5(originalMd5, md5Url, successCallback, failureCallback) { function checkUpdateMd5(originalMd5, md5Url, successCallback, failureCallback) {
download(md5Url, function(responseText) { download(md5Url, function (responseText) {
if (responseText.length != 32) { if (responseText.length != 32) {
failureCallback(-1); failureCallback(-1);
return; return;
@ -313,16 +336,16 @@ function download(url, successCallback, failureCallback) {
xhr.onreadystatechange = function (aEvt) { xhr.onreadystatechange = function (aEvt) {
if (xhr.readyState == 4) { if (xhr.readyState == 4) {
if (xhr.status == 200) { if (xhr.status == 200) {
successCallback(xhr.responseText) successCallback(xhr.responseText);
} else { } else {
failureCallback(xhr.status); failureCallback(xhr.status);
} }
} }
} };
if (url.length > 2000) { if (url.length > 2000) {
var parts = url.split("?"); var parts = url.split("?");
xhr.open("POST", parts[0], true); xhr.open("POST", parts[0], true);
xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded"); xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhr.send(parts[1]); xhr.send(parts[1]);
} else { } else {
xhr.open("GET", url, true); xhr.open("GET", url, true);
@ -371,7 +394,7 @@ function codeIsEqual(a, b) {
for (var i = 0; i < a.length; i++) { for (var i = 0; i < a.length; i++) {
var found = false; var found = false;
for (var j = 0; j < b.length; j++) { for (var j = 0; j < b.length; j++) {
var allEquals = properties.every(function(property) { var allEquals = properties.every(function (property) {
return jsonEquals(a[i], b[j], property); return jsonEquals(a[i], b[j], property);
}); });
if (allEquals) { if (allEquals) {
@ -387,11 +410,13 @@ function codeIsEqual(a, b) {
} }
function jsonEquals(a, b, property) { function jsonEquals(a, b, property) {
var aProp = a[property], typeA = getType(aProp); var aProp = a[property],
var bProp = b[property], typeB = getType(bProp); typeA = getType(aProp);
var bProp = b[property],
typeB = getType(bProp);
if (typeA != typeB) { if (typeA != typeB) {
// consider empty arrays equivalent to lack of property // 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 true;
} }
return false; return false;
@ -427,9 +452,12 @@ function searchStyles(immediately) {
clearTimeout(searchStyles.timeout); clearTimeout(searchStyles.timeout);
searchStyles.timeout = setTimeout(doSearch, 100); searchStyles.timeout = setTimeout(doSearch, 100);
} }
function doSearch() { function doSearch() {
chrome.runtime.sendMessage({method: "getStyles"}, function(styles) { chrome.runtime.sendMessage({
styles.forEach(function(style) { method: "getStyles"
}, function (styles) {
styles.forEach(function (style) {
var el = document.querySelector("[style-id='" + style.id + "']"); var el = document.querySelector("[style-id='" + style.id + "']");
if (el) { if (el) {
el.style.display = !query || isMatchingText(style.name) || isMatchingStyle(style) ? "" : "none"; el.style.display = !query || isMatchingText(style.name) || isMatchingStyle(style) ? "" : "none";
@ -437,43 +465,49 @@ function searchStyles(immediately) {
}); });
}); });
} }
function isMatchingStyle(style) { function isMatchingStyle(style) {
return style.sections.some(function(section) { return style.sections.some(function (section) {
return Object.keys(section).some(function(key) { return Object.keys(section).some(function (key) {
var value = section[key]; var value = section[key];
switch (typeof value) { switch (typeof value) {
case "string": return isMatchingText(value); case "string":
case "object": return value.some(isMatchingText); return isMatchingText(value);
case "object":
return value.some(isMatchingText);
} }
}); });
}); });
} }
function isMatchingText(text) { function isMatchingText(text) {
return text.toLocaleLowerCase().indexOf(query) >= 0; return text.toLocaleLowerCase().indexOf(query) >= 0;
} }
} }
function onFilterChange (className, event) { function onFilterChange(className, event) {
installed.classList.toggle(className, event.target.checked); installed.classList.toggle(className, event.target.checked);
} }
function initFilter(className, node) { function initFilter(className, node) {
node.addEventListener("change", onFilterChange.bind(undefined, className), false); node.addEventListener("change", onFilterChange.bind(undefined, className), false);
onFilterChange(className, {target: node}); onFilterChange(className, {
target: node
});
} }
function importStyles (e) { function importStyles(e) {
var file = e.target.files[0]; var file = e.target.files[0];
var reader = new FileReader(); var reader = new FileReader();
var styles = []; var styles = [];
function save () { function save() {
var style = styles.shift(); var style = styles.shift();
if (style) { if (style) {
delete style.id; delete style.id;
saveStyle(style, save); saveStyle(style, save);
} } else {
else { window.location.reload();
window.location.reload()
} }
} }
@ -481,22 +515,21 @@ function importStyles (e) {
try { try {
styles = JSON.parse(evt.target.result); styles = JSON.parse(evt.target.result);
save(); save();
} } catch (e) {
catch (e) {
window.alert(e.message); window.alert(e.message);
} }
}; };
reader.onerror = function (e) { reader.onerror = function (e) {
window.alert(e.message); window.alert(e.message);
} };
reader.readAsText(file) reader.readAsText(file);
} }
function selectAll () { function selectAll() {
document.execCommand('selectAll'); document.execCommand('selectAll');
} }
document.addEventListener("DOMContentLoaded", function() { document.addEventListener("DOMContentLoaded", function () {
installed = document.getElementById("installed"); installed = document.getElementById("installed");
if (document.stylishStyles) { if (document.stylishStyles) {
showStyles(document.stylishStyles); showStyles(document.stylishStyles);

View File

@ -1,14 +1,20 @@
/*jshint undef:false*/
function notifyAllTabs(request) { function notifyAllTabs(request) {
chrome.windows.getAll({populate: true}, function(windows) { chrome.windows.getAll({
windows.forEach(function(win) { populate: true
win.tabs.forEach(function(tab) { }, function (windows) {
windows.forEach(function (win) {
win.tabs.forEach(function (tab) {
chrome.tabs.sendMessage(tab.id, request); chrome.tabs.sendMessage(tab.id, request);
updateIcon(tab); updateIcon(tab);
}); });
}); });
}); });
// notify all open popups // 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); chrome.runtime.sendMessage(reqPopup);
} }
@ -20,7 +26,7 @@ function updateIcon(tab, styles) {
} }
if (styles) { if (styles) {
// check for not-yet-existing tabs e.g. omnibox instant search // check for not-yet-existing tabs e.g. omnibox instant search
chrome.tabs.get(tab.id, function() { chrome.tabs.get(tab.id, function () {
if (!chrome.runtime.lastError) { if (!chrome.runtime.lastError) {
// for 'styles' asHash:true fake the length by counting numeric ids manually // for 'styles' asHash:true fake the length by counting numeric ids manually
if (styles.length === undefined) { if (styles.length === undefined) {
@ -34,33 +40,47 @@ function updateIcon(tab, styles) {
}); });
return; return;
} }
getTabRealURL(tab, function(url) { 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 we have access to this, call directly. a page sending a message to itself doesn't seem to work right.
if (typeof getStyles != "undefined") { if (typeof getStyles != "undefined") {
getStyles({matchUrl: url, enabled: true}, stylesReceived); getStyles({
matchUrl: url,
enabled: true
}, stylesReceived);
} else { } else {
chrome.runtime.sendMessage({method: "getStyles", matchUrl: url, enabled: true}, stylesReceived); chrome.runtime.sendMessage({
method: "getStyles",
matchUrl: url,
enabled: true
}, stylesReceived);
} }
}); });
function stylesReceived(styles) { function stylesReceived(styles) {
var disableAll = "disableAll" in styles ? styles.disableAll : prefs.get("disableAll"); 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({ chrome.browserAction.setIcon({
path: { path: {
// Material Design 2016 new size is 16px // 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 // 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 tabId: tab.id
}, function() { }, function () {
// if the tab was just closed an error may occur, // if the tab was just closed an error may occur,
// e.g. 'windowPosition' pref updated in edit.js::window.onbeforeunload // e.g. 'windowPosition' pref updated in edit.js::window.onbeforeunload
if (!chrome.runtime.lastError) { if (!chrome.runtime.lastError) {
var t = prefs.get("show-badge") && styles.length ? ("" + styles.length) : ""; var t = prefs.get("show-badge") && styles.length ? ("" + styles.length) : "";
chrome.browserAction.setBadgeText({text: t, tabId: tab.id}); chrome.browserAction.setBadgeText({
chrome.browserAction.setBadgeBackgroundColor({color: disableAll ? "#aaa" : [0, 0, 0, 0]}); 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 + "'."); //console.log("Tab " + tab.id + " (" + tab.url + ") badge text set to '" + t + "'.");
@ -68,13 +88,16 @@ function updateIcon(tab, styles) {
} }
function getActiveTab(callback) { function getActiveTab(callback) {
chrome.tabs.query({currentWindow: true, active: true}, function(tabs) { chrome.tabs.query({
currentWindow: true,
active: true
}, function (tabs) {
callback(tabs[0]); callback(tabs[0]);
}); });
} }
function getActiveTabRealURL(callback) { function getActiveTabRealURL(callback) {
getActiveTab(function(tab) { getActiveTab(function (tab) {
getTabRealURL(tab, callback); getTabRealURL(tab, callback);
}); });
} }
@ -83,7 +106,11 @@ function getTabRealURL(tab, callback) {
if (tab.url != "chrome://newtab/") { if (tab.url != "chrome://newtab/") {
callback(tab.url); callback(tab.url);
} else { } 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); frame && callback(frame.url);
}); });
} }

View File

@ -1,3 +1,4 @@
/*jshint undef:false*/
var writeStyleTemplate = document.createElement("a"); var writeStyleTemplate = document.createElement("a");
writeStyleTemplate.className = "write-style-link"; writeStyleTemplate.className = "write-style-link";
@ -17,7 +18,10 @@ function updatePopUp(url) {
return; 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); document.querySelector("#find-styles a").href = "https://userstyles.org/styles/browse/all/" + encodeURIComponent("file" === urlWillWork[1] ? "file:" : url);
// Write new style links // Write new style links
@ -29,23 +33,29 @@ function updatePopUp(url) {
var urlLink = writeStyleTemplate.cloneNode(true); var urlLink = writeStyleTemplate.cloneNode(true);
urlLink.href = "edit.html?url-prefix=" + encodeURIComponent(url); urlLink.href = "edit.html?url-prefix=" + encodeURIComponent(url);
urlLink.appendChild(document.createTextNode( // switchable; default="this&nbsp;URL" urlLink.appendChild(document.createTextNode( // switchable; default="this&nbsp;URL"
!prefs.get("popup.breadcrumbs.usePath") !prefs.get("popup.breadcrumbs.usePath") ? t("writeStyleForURL").replace(/ /g, "\u00a0") : /\/\/[^/]+\/(.*)/.exec(url)[1]
? t("writeStyleForURL").replace(/ /g, "\u00a0")
: /\/\/[^/]+\/(.*)/.exec(url)[1]
)); ));
urlLink.title = "url-prefix(\"$\")".replace("$", url); urlLink.title = "url-prefix(\"$\")".replace("$", url);
writeStyleLinks.push(urlLink); writeStyleLinks.push(urlLink);
document.querySelector("#write-style").appendChild(urlLink) document.querySelector("#write-style").appendChild(urlLink);
if (prefs.get("popup.breadcrumbs")) { // switchable; default=enabled if (prefs.get("popup.breadcrumbs")) { // switchable; default=enabled
urlLink.addEventListener("mouseenter", function(event) { this.parentNode.classList.add("url()") }, false); urlLink.addEventListener("mouseenter", function (event) {
urlLink.addEventListener("focus", function(event) { this.parentNode.classList.add("url()") }, false); this.parentNode.classList.add("url()");
urlLink.addEventListener("mouseleave", function(event) { this.parentNode.classList.remove("url()") }, false); }, false);
urlLink.addEventListener("blur", function(event) { this.parentNode.classList.remove("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 // For domain
var domains = getDomains(url) var domains = getDomains(url);
domains.forEach(function(domain) { domains.forEach(function (domain) {
// Don't include TLD // Don't include TLD
if (domains.length > 1 && domain.indexOf(".") == -1) { if (domains.length > 1 && domain.indexOf(".") == -1) {
return; return;
@ -59,7 +69,7 @@ function updatePopUp(url) {
}); });
var writeStyle = document.querySelector("#write-style"); var writeStyle = document.querySelector("#write-style");
writeStyleLinks.forEach(function(link, index) { writeStyleLinks.forEach(function (link, index) {
link.addEventListener("click", openLinkInTabOrWindow, false); link.addEventListener("click", openLinkInTabOrWindow, false);
container.appendChild(link); container.appendChild(link);
}); });
@ -72,14 +82,14 @@ function updatePopUp(url) {
function showStyles(styles) { function showStyles(styles) {
var enabledFirst = prefs.get("popup.enabledFirst"); var enabledFirst = prefs.get("popup.enabledFirst");
styles.sort(function(a, b) { styles.sort(function (a, b) {
if (enabledFirst && a.enabled !== b.enabled) return !(a.enabled < b.enabled) ? -1 : 1; if (enabledFirst && a.enabled !== b.enabled) return !(a.enabled < b.enabled) ? -1 : 1;
return a.name.localeCompare(b.name); 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>"; installed.innerHTML = "<div class='entry' id='no-styles'>" + t('noStylesForSite') + "</div>";
} }
styles.map(createStyleElement).forEach(function(e) { styles.map(createStyleElement).forEach(function (e) {
installed.appendChild(e); installed.appendChild(e);
}); });
} }
@ -100,13 +110,24 @@ function createStyleElement(style) {
editLink.setAttribute("href", editLink.getAttribute("href") + style.id); editLink.setAttribute("href", editLink.getAttribute("href") + style.id);
editLink.addEventListener("click", openLinkInTabOrWindow, false); 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 // clicking the checkbox will toggle it, and this will run after that happens
checkbox.addEventListener("click", function() { enable(event, event.target.checked); }, false); checkbox.addEventListener("click", function () {
e.querySelector(".enable").addEventListener("click", function() { enable(event, true); }, false); enable(event, event.target.checked);
e.querySelector(".disable").addEventListener("click", function() { enable(event, false); }, false); }, 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; return e;
} }
@ -147,7 +168,9 @@ function getId(event) {
function openLinkInTabOrWindow(event) { function openLinkInTabOrWindow(event) {
event.preventDefault(); event.preventDefault();
if (prefs.get("openEditInWindow", false)) { if (prefs.get("openEditInWindow", false)) {
var options = {url: event.target.href} var options = {
url: event.target.href
};
var wp = prefs.get("windowPosition", {}); var wp = prefs.get("windowPosition", {});
for (var k in wp) options[k] = wp[k]; for (var k in wp) options[k] = wp[k];
chrome.windows.create(options); chrome.windows.create(options);
@ -159,7 +182,10 @@ function openLinkInTabOrWindow(event) {
function openLink(event) { function openLink(event) {
event.preventDefault(); event.preventDefault();
chrome.runtime.sendMessage({method: "openURL", url: event.target.href}); chrome.runtime.sendMessage({
method: "openURL",
url: event.target.href
});
close(); close();
} }
@ -168,7 +194,7 @@ function handleUpdate(style) {
if (styleElement) { if (styleElement) {
installed.replaceChild(createStyleElement(style), styleElement); installed.replaceChild(createStyleElement(style), styleElement);
} else { } else {
getActiveTabRealURL(function(url) { getActiveTabRealURL(function (url) {
if (chrome.extension.getBackgroundPage().getApplicableSections(style, url).length) { if (chrome.extension.getBackgroundPage().getApplicableSections(style, url).length) {
// a new style for the current url is installed // a new style for the current url is installed
document.getElementById("unavailable").style.display = "none"; document.getElementById("unavailable").style.display = "none";
@ -185,7 +211,7 @@ function handleDelete(id) {
} }
} }
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) { chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
if (request.method == "updatePopup") { if (request.method == "updatePopup") {
switch (request.reason) { switch (request.reason) {
case "styleAdded": case "styleAdded":
@ -199,11 +225,11 @@ chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
} }
}); });
["find-styles-link", "open-manage-link"].forEach(function(id) { ["find-styles-link", "open-manage-link"].forEach(function (id) {
document.getElementById(id).addEventListener("click", openLink, false); document.getElementById(id).addEventListener("click", openLink, false);
}); });
document.getElementById("disableAll").addEventListener("change", function(event) { document.getElementById("disableAll").addEventListener("change", function (event) {
installed.classList.toggle("disabled", prefs.get("disableAll")); installed.classList.toggle("disabled", prefs.get("disableAll"));
}); });
setupLivePrefs(["disableAll"]); setupLivePrefs(["disableAll"]);

View File

@ -1,31 +1,32 @@
/*jshint undef:false*/
var webSqlStorage = { var webSqlStorage = {
migrate: function() { migrate: function () {
if (typeof openDatabase == "undefined") { if (typeof openDatabase == "undefined") {
// No WebSQL - no migration! // No WebSQL - no migration!
return; return;
} }
webSqlStorage.getStyles(function(styles) { webSqlStorage.getStyles(function (styles) {
getDatabase(function(db) { getDatabase(function (db) {
var tx = db.transaction(["styles"], "readwrite"); var tx = db.transaction(["styles"], "readwrite");
var os = tx.objectStore("styles"); var os = tx.objectStore("styles");
styles.forEach(function(s) { styles.forEach(function (s) {
webSqlStorage.cleanStyle(s) webSqlStorage.cleanStyle(s);
os.add(s); os.add(s);
}); });
// While this was running, the styles were loaded from the (empty) indexed db // While this was running, the styles were loaded from the (empty) indexed db
setTimeout(function() { setTimeout(function () {
invalidateCache(true); invalidateCache(true);
}, 500); }, 500);
}); });
}, null); }, null);
}, },
cleanStyle: function(s) { cleanStyle: function (s) {
delete s.id; delete s.id;
s.sections.forEach(function(section) { s.sections.forEach(function (section) {
delete section.id; delete section.id;
["urls", "urlPrefixes", "domains", "regexps"].forEach(function(property) { ["urls", "urlPrefixes", "domains", "regexps"].forEach(function (property) {
if (!section[property]) { if (!section[property]) {
section[property] = []; section[property] = [];
} }
@ -33,8 +34,8 @@ var webSqlStorage = {
}); });
}, },
getStyles: function(callback) { getStyles: function (callback) {
webSqlStorage.getDatabase(function(db) { webSqlStorage.getDatabase(function (db) {
if (!db) { if (!db) {
callback([]); callback([]);
return; return;
@ -44,12 +45,13 @@ var webSqlStorage = {
var params = []; 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) { 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 styles = [],
var currentStyle = null; currentStyle = null,
var currentSection = null; currentSection = null,
metaName;
for (var i = 0; i < r.rows.length; i++) { for (var i = 0; i < r.rows.length; i++) {
var values = r.rows.item(i); var values = r.rows.item(i);
var metaName = null; metaName = null;
switch (values.metaName) { switch (values.metaName) {
case null: case null:
break; break;
@ -60,22 +62,34 @@ var webSqlStorage = {
metaName = "urlPrefixes"; metaName = "urlPrefixes";
break; break;
case "domain": case "domain":
var metaName = "domains"; metaName = "domains";
break; break;
case "regexps": case "regexps":
var metaName = "regexps"; metaName = "regexps";
break; break;
default: default:
var metaName = values.metaName + "s"; metaName = values.metaName + "s";
} }
var metaValue = values.metaValue; var metaValue = values.metaValue;
if (currentStyle == null || currentStyle.id != values.id) { 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: []}; 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); styles.push(currentStyle);
} }
if (values.section_id != null) { if (values.section_id !== null) {
if (currentSection == null || currentSection.id != values.section_id) { if (currentSection === null || currentSection.id != values.section_id) {
currentSection = {id: values.section_id, code: values.code}; currentSection = {
id: values.section_id,
code: values.code
};
currentStyle.sections.push(currentSection); currentStyle.sections.push(currentSection);
} }
if (metaName && metaValue) { if (metaName && metaValue) {
@ -93,14 +107,14 @@ var webSqlStorage = {
}, reportError); }, reportError);
}, },
getDatabase: function(ready, error) { getDatabase: function (ready, error) {
try { try {
stylishDb = openDatabase('stylish', '', 'Stylish Styles', 5*1024*1024); stylishDb = openDatabase('stylish', '', 'Stylish Styles', 5 * 1024 * 1024);
} catch (ex) { } catch (ex) {
error(); error();
throw ex; throw ex;
} }
if (stylishDb.version == "") { if (stylishDb.version === "") {
// It didn't already exist, we have nothing to migrate. // It didn't already exist, we have nothing to migrate.
ready(null); ready(null);
return; return;
@ -120,15 +134,17 @@ var webSqlStorage = {
} }
}, },
dbV11: function(d, error, done) { dbV11: function (d, error, done) {
d.changeVersion(d.version, '1.1', function (t) { d.changeVersion(d.version, '1.1', function (t) {
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 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 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);'); 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) { dbV12: function (d, error, done) {
d.changeVersion(d.version, '1.2', function (t) { d.changeVersion(d.version, '1.2', function (t) {
// add section table // add section table
t.executeSql('CREATE TABLE sections (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, style_id INTEGER NOT NULL, code TEXT NOT NULL);'); t.executeSql('CREATE TABLE sections (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, style_id INTEGER NOT NULL, code TEXT NOT NULL);');
@ -144,26 +160,34 @@ var webSqlStorage = {
t.executeSql('INSERT INTO newstyles (id, url, updateUrl, md5Url, name, enabled) SELECT id, url, updateUrl, md5Url, name, enabled FROM styles;'); 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('DROP TABLE styles;');
t.executeSql('ALTER TABLE newstyles RENAME TO 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) { dbV13: function (d, error, done) {
d.changeVersion(d.version, '1.3', function (t) { d.changeVersion(d.version, '1.3', function (t) {
// clear out orphans // 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 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);'); 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) { dbV14: function (d, error, done) {
d.changeVersion(d.version, '1.4', function (t) { d.changeVersion(d.version, '1.4', function (t) {
t.executeSql('UPDATE styles SET url = null WHERE url = "undefined";'); 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) { dbV15: function (d, error, done) {
d.changeVersion(d.version, '1.5', function (t) { d.changeVersion(d.version, '1.5', function (t) {
t.executeSql('ALTER TABLE styles ADD COLUMN originalMd5 TEXT NULL;'); t.executeSql('ALTER TABLE styles ADD COLUMN originalMd5 TEXT NULL;');
}, error, function() { done(d); }); }, error, function () {
done(d);
});
} }
} };

View File

@ -1,37 +1,43 @@
/*jshint undef:false*/
function getDatabase(ready, error) { function getDatabase(ready, error) {
var dbOpenRequest = window.indexedDB.open("stylish", 2); var dbOpenRequest = window.indexedDB.open("stylish", 2);
dbOpenRequest.onsuccess = function(e) { dbOpenRequest.onsuccess = function (e) {
ready(e.target.result); ready(e.target.result);
}; };
dbOpenRequest.onerror = function(event) { dbOpenRequest.onerror = function (event) {
console.log(event.target.errorCode); console.log(event.target.errorCode);
if (error) { if (error) {
error(event); error(event);
} }
}; };
dbOpenRequest.onupgradeneeded = function(event) { dbOpenRequest.onupgradeneeded = function (event) {
if (event.oldVersion == 0) { if (event.oldVersion === 0) {
var os = event.target.result.createObjectStore("styles", {keyPath: 'id', autoIncrement: true}); var os = event.target.result.createObjectStore("styles", {
keyPath: 'id',
autoIncrement: true
});
webSqlStorage.migrate(); webSqlStorage.migrate();
} }
} };
}; }
var cachedStyles = null,
request;
var cachedStyles = null;
function getStyles(options, callback) { function getStyles(options, callback) {
if (cachedStyles != null) { if (cachedStyles !== null) {
callback(filterStyles(cachedStyles, options)); callback(filterStyles(cachedStyles, options));
return; return;
} }
getDatabase(function(db) { getDatabase(function (db) {
var tx = db.transaction(["styles"], "readonly"); var tx = db.transaction(["styles"], "readonly");
var os = tx.objectStore("styles"); var os = tx.objectStore("styles");
var all = []; var all = [];
os.openCursor().onsuccess = function(event) { os.openCursor().onsuccess = function (event) {
var cursor = event.target.result; var cursor = event.target.result;
if (cursor) { if (cursor) {
var s = cursor.value var s = cursor.value;
s.id = cursor.key s.id = cursor.key;
all.push(cursor.value); all.push(cursor.value);
cursor.continue(); cursor.continue();
} else { } else {
@ -45,7 +51,9 @@ function getStyles(options, callback) {
function invalidateCache(andNotify) { function invalidateCache(andNotify) {
cachedStyles = null; cachedStyles = null;
if (andNotify) { if (andNotify) {
chrome.runtime.sendMessage({method: "invalidateCache"}); chrome.runtime.sendMessage({
method: "invalidateCache"
});
} }
} }
@ -55,27 +63,29 @@ function filterStyles(styles, options) {
var id = "id" in options ? Number(options.id) : null; var id = "id" in options ? Number(options.id) : null;
var matchUrl = "matchUrl" in options ? options.matchUrl : null; var matchUrl = "matchUrl" in options ? options.matchUrl : null;
if (enabled != null) { if (enabled !== null) {
styles = styles.filter(function(style) { styles = styles.filter(function (style) {
return style.enabled == enabled; return style.enabled == enabled;
}); });
} }
if (url != null) { if (url !== null) {
styles = styles.filter(function(style) { styles = styles.filter(function (style) {
return style.url == url; return style.url == url;
}); });
} }
if (id != null) { if (id !== null) {
styles = styles.filter(function(style) { styles = styles.filter(function (style) {
return style.id == id; 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. // Return as a hash from style to applicable sections? Can only be used with matchUrl.
var asHash = "asHash" in options ? options.asHash : false; var asHash = "asHash" in options ? options.asHash : false;
if (asHash) { if (asHash) {
var h = {disableAll: prefs.get("disableAll", false)}; var h = {
styles.forEach(function(style) { disableAll: prefs.get("disableAll", false)
};
styles.forEach(function (style) {
var applicableSections = getApplicableSections(style, matchUrl); var applicableSections = getApplicableSections(style, matchUrl);
if (applicableSections.length > 0) { if (applicableSections.length > 0) {
h[style.id] = applicableSections; h[style.id] = applicableSections;
@ -83,7 +93,7 @@ function filterStyles(styles, options) {
}); });
return h; return h;
} }
styles = styles.filter(function(style) { styles = styles.filter(function (style) {
var applicableSections = getApplicableSections(style, matchUrl); var applicableSections = getApplicableSections(style, matchUrl);
return applicableSections.length > 0; return applicableSections.length > 0;
}); });
@ -92,14 +102,14 @@ function filterStyles(styles, options) {
} }
function saveStyle(o, callback) { function saveStyle(o, callback) {
getDatabase(function(db) { getDatabase(function (db) {
var tx = db.transaction(["styles"], "readwrite"); var tx = db.transaction(["styles"], "readwrite");
var os = tx.objectStore("styles"); var os = tx.objectStore("styles");
// Update // Update
if (o.id) { if (o.id) {
var request = os.get(Number(o.id)); request = os.get(Number(o.id));
request.onsuccess = function(event) { request.onsuccess = function (event) {
var style = request.result; var style = request.result;
for (var prop in o) { for (var prop in o) {
if (prop == "id") { if (prop == "id") {
@ -108,8 +118,11 @@ function saveStyle(o, callback) {
style[prop] = o[prop]; style[prop] = o[prop];
} }
request = os.put(style); request = os.put(style);
request.onsuccess = function(event) { request.onsuccess = function (event) {
notifyAllTabs({method: "styleUpdated", style: style}); notifyAllTabs({
method: "styleUpdated",
style: style
});
invalidateCache(true); invalidateCache(true);
if (callback) { if (callback) {
callback(style); callback(style);
@ -121,14 +134,14 @@ function saveStyle(o, callback) {
// Create // Create
// Set optional things to null if they're undefined // Set optional things to null if they're undefined
["updateUrl", "md5Url", "url", "originalMd5"].filter(function(att) { ["updateUrl", "md5Url", "url", "originalMd5"].filter(function (att) {
return !(att in o); return !(att in o);
}).forEach(function(att) { }).forEach(function (att) {
o[att] = null; o[att] = null;
}); });
// Set other optional things to empty array if they're undefined // Set other optional things to empty array if they're undefined
o.sections.forEach(function(section) { o.sections.forEach(function (section) {
["urls", "urlPrefixes", "domains", "regexps"].forEach(function(property) { ["urls", "urlPrefixes", "domains", "regexps"].forEach(function (property) {
if (!section[property]) { if (!section[property]) {
section[property] = []; section[property] = [];
} }
@ -139,13 +152,16 @@ function saveStyle(o, callback) {
o.enabled = true; o.enabled = true;
} }
// Make sure it's not null - that makes indexeddb sad // Make sure it's not null - that makes indexeddb sad
delete o["id"]; delete o.id;
var request = os.add(o); request = os.add(o);
request.onsuccess = function(event) { request.onsuccess = function (event) {
invalidateCache(true); invalidateCache(true);
// Give it the ID that was generated // Give it the ID that was generated
o.id = event.target.result; o.id = event.target.result;
notifyAllTabs({method: "styleAdded", style: o}); notifyAllTabs({
method: "styleAdded",
style: o
});
if (callback) { if (callback) {
callback(o); callback(o);
} }
@ -154,26 +170,36 @@ function saveStyle(o, callback) {
} }
function enableStyle(id, enabled) { function enableStyle(id, enabled) {
saveStyle({id: id, enabled: enabled}, function(style) { saveStyle({
id: id,
enabled: enabled
}, function (style) {
handleUpdate(style); handleUpdate(style);
notifyAllTabs({method: "styleUpdated", style: style}); notifyAllTabs({
method: "styleUpdated",
style: style
});
}); });
} }
function deleteStyle(id) { function deleteStyle(id) {
getDatabase(function(db) { getDatabase(function (db) {
var tx = db.transaction(["styles"], "readwrite"); var tx = db.transaction(["styles"], "readwrite");
var os = tx.objectStore("styles"); var os = tx.objectStore("styles");
var request = os.delete(Number(id)); request = os.delete(Number(id));
request.onsuccess = function(event) { request.onsuccess = function (event) {
handleDelete(id); handleDelete(id);
invalidateCache(true); invalidateCache(true);
notifyAllTabs({method: "styleDeleted", id: id}); notifyAllTabs({
method: "styleDeleted",
id: id
});
}; };
}); });
} }
function reportError() { function reportError() {
var i;
for (i in arguments) { for (i in arguments) {
if ("message" in arguments[i]) { if ("message" in arguments[i]) {
//alert(arguments[i].message); //alert(arguments[i].message);
@ -190,7 +216,7 @@ function fixBoolean(b) {
} }
function getDomains(url) { function getDomains(url) {
if (url.indexOf("file:") == 0) { if (url.indexOf("file:") === 0) {
return []; return [];
} }
var d = /.*?:\/*([^\/:]+)/.exec(url)[1]; var d = /.*?:\/*([^\/:]+)/.exec(url)[1];
@ -213,8 +239,9 @@ function getType(o) {
} }
var namespacePattern = /^\s*(@namespace[^;]+;\s*)+$/; var namespacePattern = /^\s*(@namespace[^;]+;\s*)+$/;
function getApplicableSections(style, url) { function getApplicableSections(style, url) {
var sections = style.sections.filter(function(section) { var sections = style.sections.filter(function (section) {
return sectionAppliesToUrl(section, url); return sectionAppliesToUrl(section, url);
}); });
// ignore if it's just namespaces // ignore if it's just namespaces
@ -226,14 +253,14 @@ function getApplicableSections(style, url) {
function sectionAppliesToUrl(section, url) { function sectionAppliesToUrl(section, url) {
// only http, https, file, and chrome-extension allowed // 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; return false;
} }
// other extensions can't be styled // 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; 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"); //console.log(section.id + " is global");
return true; return true;
} }
@ -241,19 +268,19 @@ function sectionAppliesToUrl(section, url) {
//console.log(section.id + " applies to " + url + " due to URL rules"); //console.log(section.id + " applies to " + url + " due to URL rules");
return true; return true;
} }
if (section.urlPrefixes.some(function(prefix) { 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"); //console.log(section.id + " applies to " + url + " due to URL prefix rules");
return true; return true;
} }
if (section.domains.length > 0 && getDomains(url).some(function(domain) { if (section.domains.length > 0 && getDomains(url).some(function (domain) {
return section.domains.indexOf(domain) != -1; return section.domains.indexOf(domain) != -1;
})) { })) {
//console.log(section.id + " applies due to " + url + " due to domain rules"); //console.log(section.id + " applies due to " + url + " due to domain rules");
return true; return true;
} }
if (section.regexps.some(function(regexp) { if (section.regexps.some(function (regexp) {
// we want to match the full url, so add ^ and $ if not already present // we want to match the full url, so add ^ and $ if not already present
if (regexp[0] != "^") { if (regexp[0] != "^") {
regexp = "^" + regexp; regexp = "^" + regexp;
@ -261,7 +288,9 @@ function sectionAppliesToUrl(section, url) {
if (regexp[regexp.length - 1] != "$") { if (regexp[regexp.length - 1] != "$") {
regexp += "$"; regexp += "$";
} }
var re = runTryCatch(function() { return new RegExp(regexp) }); var re = runTryCatch(function () {
return new RegExp(regexp);
});
if (re) { if (re) {
return (re).test(url); return (re).test(url);
} else { } else {
@ -282,29 +311,34 @@ function isCheckbox(el) {
// js engine can't optimize the entire function if it contains try-catch // 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 // so we should keep it isolated from normal code in a minimal wrapper
function runTryCatch(func) { function runTryCatch(func) {
try { return func() } try {
catch(e) {} return func();
} catch (e) {}
} }
// Accepts an array of pref names (values are fetched via prefs.get) // Accepts an array of pref names (values are fetched via prefs.get)
// and establishes a two-way connection between the document elements and the actual prefs // and establishes a two-way connection between the document elements and the actual prefs
function setupLivePrefs(IDs) { function setupLivePrefs(IDs) {
var localIDs = {}; var localIDs = {};
IDs.forEach(function(id) { IDs.forEach(function (id) {
localIDs[id] = true; localIDs[id] = true;
updateElement(id).addEventListener("change", function() { updateElement(id).addEventListener("change", function () {
prefs.set(this.id, isCheckbox(this) ? this.checked : this.value); prefs.set(this.id, isCheckbox(this) ? this.checked : this.value);
}); });
}); });
chrome.runtime.onMessage.addListener(function(request) { chrome.runtime.onMessage.addListener(function (request) {
if (request.prefName in localIDs) { if (request.prefName in localIDs) {
updateElement(request.prefName); updateElement(request.prefName);
} }
}); });
function updateElement(id) { function updateElement(id) {
var el = document.getElementById(id); var el = document.getElementById(id);
el[isCheckbox(el) ? "checked" : "value"] = prefs.get(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; return el;
} }
} }
@ -349,9 +383,11 @@ var prefs = chrome.extension.getBackgroundPage().prefs || new function Prefs() {
var syncTimeout; // see broadcast() function below var syncTimeout; // see broadcast() function below
Object.defineProperty(this, "readOnlyValues", {value: {}}); Object.defineProperty(this, "readOnlyValues", {
value: {}
});
Prefs.prototype.get = function(key, defaultValue) { Prefs.prototype.get = function (key, defaultValue) {
if (key in values) { if (key in values) {
return values[key]; return values[key];
} }
@ -364,11 +400,11 @@ var prefs = chrome.extension.getBackgroundPage().prefs || new function Prefs() {
console.warn("No default preference for '%s'", key); console.warn("No default preference for '%s'", key);
}; };
Prefs.prototype.getAll = function(key) { Prefs.prototype.getAll = function (key) {
return deepCopy(values); return deepCopy(values);
}; };
Prefs.prototype.set = function(key, value, options) { Prefs.prototype.set = function (key, value, options) {
var oldValue = deepCopy(values[key]); var oldValue = deepCopy(values[key]);
values[key] = value; values[key] = value;
defineReadonlyProperty(this.readOnlyValues, key, value); defineReadonlyProperty(this.readOnlyValues, key, value);
@ -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) { 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); notifyAllTabs(message);
chrome.runtime.sendMessage(message); chrome.runtime.sendMessage(message);
if (key == "disableAll") { if (key == "disableAll") {
notifyAllTabs({method: "styleDisableAll", disableAll: value}); notifyAllTabs({
method: "styleDisableAll",
disableAll: value
});
} }
if (!options || !options.noSync) { if (!options || !options.noSync) {
clearTimeout(syncTimeout); clearTimeout(syncTimeout);
syncTimeout = setTimeout(function() { syncTimeout = setTimeout(function () {
getSync().set({"settings": values}); getSync().set({
"settings": values
});
}, 0); }, 0);
} }
}; };
Object.keys(defaults).forEach(function(key) { 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) { getSync().get("settings", function (result) {
var synced = result.settings; var synced = result.settings;
for (var key in defaults) { for (var key in defaults) {
if (synced && (key in synced)) { if (synced && (key in synced)) {
me.set(key, synced[key], {noSync: true}); me.set(key, synced[key], {
noSync: true
});
} else { } else {
var value = tryMigrating(key); var value = tryMigrating(key);
if (value !== undefined) { if (value !== undefined) {
@ -412,18 +463,23 @@ var prefs = chrome.extension.getBackgroundPage().prefs || new function Prefs() {
} }
}); });
chrome.storage.onChanged.addListener(function(changes, area) { chrome.storage.onChanged.addListener(function (changes, area) {
if (area == "sync" && "settings" in changes) { if (area == "sync" && "settings" in changes) {
var synced = changes.settings.newValue; var synced = changes.settings.newValue,
key;
if (synced) { if (synced) {
for (key in defaults) { for (key in defaults) {
if (key in synced) { if (key in synced) {
me.set(key, synced[key], {noSync: true}); me.set(key, synced[key], {
noSync: true
});
} }
} }
} else { } else {
// user manually deleted our settings, we'll recreate them // user manually deleted our settings, we'll recreate them
getSync().set({"settings": values}); getSync().set({
"settings": values
});
} }
} }
}); });
@ -443,24 +499,30 @@ var prefs = chrome.extension.getBackgroundPage().prefs || new function Prefs() {
case "object": case "object":
try { try {
return JSON.parse(value); return JSON.parse(value);
} catch(e) { } catch (e) {
console.log("Cannot migrate from localStorage %s = '%s': %o", key, value, e); console.log("Cannot migrate from localStorage %s = '%s': %o", key, value, e);
return undefined; return undefined;
} }
} }
return value; return value;
} }
}; }();
function getCodeMirrorThemes(callback) { function getCodeMirrorThemes(callback) {
chrome.runtime.getPackageDirectoryEntry(function(rootDir) { chrome.runtime.getPackageDirectoryEntry(function (rootDir) {
rootDir.getDirectory("codemirror/theme", {create: false}, function(themeDir) { rootDir.getDirectory("codemirror/theme", {
themeDir.createReader().readEntries(function(entries) { create: false
}, function (themeDir) {
themeDir.createReader().readEntries(function (entries) {
var themes = [chrome.i18n.getMessage("defaultTheme")]; var themes = [chrome.i18n.getMessage("defaultTheme")];
entries entries
.filter(function(entry) { return entry.isFile }) .filter(function (entry) {
.sort(function(a, b) { return a.name < b.name ? -1 : 1 }) return entry.isFile;
.forEach(function(entry) { })
.sort(function (a, b) {
return a.name < b.name ? -1 : 1;
})
.forEach(function (entry) {
themes.push(entry.name.replace(/\.css$/, "")); themes.push(entry.name.replace(/\.css$/, ""));
}); });
if (callback) { if (callback) {
@ -474,14 +536,24 @@ function getCodeMirrorThemes(callback) {
function sessionStorageHash(name) { function sessionStorageHash(name) {
var hash = { var hash = {
value: {}, value: {},
set: function(k, v) { this.value[k] = v; this.updateStorage(); }, set: function (k, v) {
unset: function(k) { delete this.value[k]; this.updateStorage(); }, this.value[k] = v;
updateStorage: function() { this.updateStorage();
},
unset: function (k) {
delete this.value[k];
this.updateStorage();
},
updateStorage: function () {
sessionStorage[this.name] = JSON.stringify(this.value); sessionStorage[this.name] = JSON.stringify(this.value);
} }
}; };
try { hash.value = JSON.parse(sessionStorage[name]); } catch(e) {} try {
Object.defineProperty(hash, "name", {value: name}); hash.value = JSON.parse(sessionStorage[name]);
} catch (e) {}
Object.defineProperty(hash, "name", {
value: name
});
return hash; return hash;
} }
@ -494,7 +566,7 @@ function deepCopy(obj) {
} }
} }
function deepMerge(target, obj1 /* plus any number of object arguments */) { function deepMerge(target, obj1 /* plus any number of object arguments */ ) {
for (var i = 1; i < arguments.length; i++) { for (var i = 1; i < arguments.length; i++) {
var obj = arguments[i]; var obj = arguments[i];
for (var k in obj) { for (var k in obj) {
@ -512,7 +584,7 @@ function deepMerge(target, obj1 /* plus any number of object arguments */) {
return target; return target;
} }
function shallowMerge(target, obj1 /* plus any number of object arguments */) { function shallowMerge(target, obj1 /* plus any number of object arguments */ ) {
for (var i = 1; i < arguments.length; i++) { for (var i = 1; i < arguments.length; i++) {
var obj = arguments[i]; var obj = arguments[i];
for (var k in obj) { for (var k in obj) {
@ -544,7 +616,10 @@ function defineReadonlyProperty(obj, key, value) {
if (typeof copy == "object") { if (typeof copy == "object") {
Object.freeze(copy); 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 // Polyfill, can be removed when Firefox gets this - https://bugzilla.mozilla.org/show_bug.cgi?id=1220494
@ -554,10 +629,10 @@ function getSync() {
} }
crappyStorage = {}; crappyStorage = {};
return { return {
get: function(key, callback) { get: function (key, callback) {
callback(crappyStorage[key] || {}); callback(crappyStorage[key] || {});
}, },
set: function(source, callback) { set: function (source, callback) {
for (var property in source) { for (var property in source) {
if (source.hasOwnProperty(property)) { if (source.hasOwnProperty(property)) {
crappyStorage[property] = source[property]; crappyStorage[property] = source[property];
@ -565,5 +640,5 @@ function getSync() {
} }
callback(); callback();
} }
} };
} }