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

479
apply.js
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;
@ -7,289 +8,311 @@ initObserver();
requestStyles(); requestStyles();
function requestStyles() { function requestStyles() {
// If this is a Stylish page (Edit Style or Manage Styles), // If this is a Stylish page (Edit Style or Manage Styles),
// 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",
var bg = chrome.extension.getBackgroundPage(); matchUrl: location.href,
if (bg && bg.getStyles) { enabled: true,
// apply styles immediately, then proceed with a normal request that will update the icon asHash: true
bg.getStyles(request, applyStyles); };
} if (location.href.indexOf(chrome.extension.getURL("")) === 0) {
} var bg = chrome.extension.getBackgroundPage();
chrome.runtime.sendMessage(request, applyStyles); if (bg && bg.getStyles) {
// apply styles immediately, then proceed with a normal request that will update the icon
bg.getStyles(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
switch (request.method == "updatePopup" ? request.reason : request.method) { function styleAdded() {
case "styleDeleted": if (request.style.enabled) {
removeStyle(request.id, document); chrome.runtime.sendMessage({
break; method: "getStyles",
case "styleUpdated": matchUrl: location.href,
if (request.style.enabled) { enabled: true,
retireStyle(request.style.id); id: request.style.id,
// fallthrough to "styleAdded" asHash: true
} else { }, applyStyles);
removeStyle(request.style.id, document); }
break; }
}
case "styleAdded": switch (request.method == "updatePopup" ? request.reason : request.method) {
if (request.style.enabled) { case "styleDeleted":
chrome.runtime.sendMessage({method: "getStyles", matchUrl: location.href, enabled: true, id: request.style.id, asHash: true}, applyStyles); removeStyle(request.id, document);
} break;
break; case "styleUpdated":
case "styleApply": if (request.style.enabled) {
applyStyles(request.styles); retireStyle(request.style.id);
break; styleAdded();
case "styleReplaceAll": } else {
replaceAll(request.styles, document); removeStyle(request.style.id, document);
break; }
case "styleDisableAll": break;
disableAll(request.disableAll); case "styleAdded":
break; styleAdded();
} break;
case "styleApply":
applyStyles(request.styles);
break;
case "styleReplaceAll":
replaceAll(request.styles, document);
break;
case "styleDisableAll":
disableAll(request.disableAll);
break;
}
}); });
function disableAll(disable) { function disableAll(disable) {
if (!disable === !g_disableAll) { if (!disable === !g_disableAll) {
return; return;
} }
g_disableAll = disable; g_disableAll = disable;
if (g_disableAll) { if (g_disableAll) {
iframeObserver.disconnect(); iframeObserver.disconnect();
} }
disableSheets(g_disableAll, document); disableSheets(g_disableAll, document);
if (!g_disableAll && document.readyState != "loading") { if (!g_disableAll && document.readyState != "loading") {
iframeObserver.start(); iframeObserver.start();
} }
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);
} }
disableSheets(disable, iframe.contentDocument); disableSheets(disable, iframe.contentDocument);
}); });
} }
} }
function removeStyle(id, doc) { function removeStyle(id, doc) {
var e = doc.getElementById("stylish-" + id); var e = doc.getElementById("stylish-" + id);
delete g_styleElements["stylish-" + id]; delete g_styleElements["stylish-" + id];
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);
}); });
} }
// to avoid page flicker when the style is updated // to avoid page flicker when the style is updated
// instead of removing it immediately we rename its ID and queue it // instead of removing it immediately we rename its ID and queue it
// to be deleted in applyStyles after a new version is fetched and applied // to be deleted in applyStyles after a new version is fetched and applied
function retireStyle(id, doc) { function retireStyle(id, doc) {
var deadID = "ghost-" + id; var deadID = "ghost-" + id;
if (!doc) { if (!doc) {
doc = document; doc = document;
retiredStyleIds.push(deadID); retiredStyleIds.push(deadID);
delete g_styleElements["stylish-" + id]; delete g_styleElements["stylish-" + id];
// in case something went wrong and new style was never applied // in case something went wrong and new style was never applied
setTimeout(removeStyle.bind(null, deadID, doc), 1000); setTimeout(removeStyle.bind(null, deadID, doc), 1000);
} }
var e = doc.getElementById("stylish-" + id); var e = doc.getElementById("stylish-" + id);
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);
}); });
} }
function applyStyles(styleHash) { function applyStyles(styleHash) {
if (!styleHash) { // Chrome is starting up if (!styleHash) { // Chrome is starting up
requestStyles(); requestStyles();
return; return;
} }
if ("disableAll" in styleHash) { if ("disableAll" in styleHash) {
disableAll(styleHash.disableAll); disableAll(styleHash.disableAll);
delete styleHash.disableAll; delete styleHash.disableAll;
} }
for (var styleId in styleHash) { for (var styleId in styleHash) {
applySections(styleId, styleHash[styleId]); applySections(styleId, styleHash[styleId]);
} }
if (Object.keys(g_styleElements).length) { if (Object.keys(g_styleElements).length) {
// when site response is application/xml Chrome displays our style elements // when site response is application/xml Chrome displays our style elements
// under document.documentElement as plain text so we need to move them into HEAD // under document.documentElement as plain text so we need to move them into HEAD
// (which already is autogenerated at this moment for the xml response) // (which already is autogenerated at this moment for the xml response)
if (document.head && document.head.firstChild && document.head.firstChild.id == "xml-viewer-style") { if (document.head && document.head.firstChild && document.head.firstChild.id == "xml-viewer-style") {
for (var id in g_styleElements) { for (var id in g_styleElements) {
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);
} }
}, 0); }, 0);
} }
} }
function applySections(styleId, sections) { function applySections(styleId, sections) {
var styleElement = document.getElementById("stylish-" + styleId); var styleElement = document.getElementById("stylish-" + styleId);
// Already there. // Already there.
if (styleElement) { if (styleElement) {
return; return;
} }
if (document.documentElement instanceof SVGSVGElement) { if (document.documentElement instanceof SVGSVGElement) {
// SVG document, make an SVG style element. // SVG document, make an SVG style element.
styleElement = document.createElementNS("http://www.w3.org/2000/svg", "style"); styleElement = document.createElementNS("http://www.w3.org/2000/svg", "style");
} else { } else {
// This will make an HTML style element. If there's SVG embedded in an HTML document, this works on the SVG too. // This will make an HTML style element. If there's SVG embedded in an HTML document, this works on the SVG too.
styleElement = document.createElement("style"); styleElement = document.createElement("style");
} }
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);
g_styleElements[styleElement.id] = styleElement; g_styleElements[styleElement.id] = styleElement;
} }
function addStyleElement(styleElement, doc) { function addStyleElement(styleElement, doc) {
if (!doc.documentElement || doc.getElementById(styleElement.id)) { if (!doc.documentElement || doc.getElementById(styleElement.id)) {
return; return;
} }
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 {
addStyleElement(styleElement, iframe.contentDocument); addStyleElement(styleElement, iframe.contentDocument);
} }
}); });
} }
function addDocumentStylesToIFrame(iframe) { function addDocumentStylesToIFrame(iframe) {
var doc = iframe.contentDocument; var doc = iframe.contentDocument;
var srcDocIsLoading = iframeIsLoadingSrcDoc(iframe); var srcDocIsLoading = iframeIsLoadingSrcDoc(iframe);
for (var id in g_styleElements) { for (var id in g_styleElements) {
if (srcDocIsLoading) { if (srcDocIsLoading) {
addStyleToIFrameSrcDoc(iframe, g_styleElements[id]); addStyleToIFrameSrcDoc(iframe, g_styleElements[id]);
} else { } else {
addStyleElement(g_styleElements[id], doc); addStyleElement(g_styleElements[id], doc);
} }
} }
} }
function addDocumentStylesToAllIFrames() { function addDocumentStylesToAllIFrames() {
getDynamicIFrames(document).forEach(addDocumentStylesToIFrame); getDynamicIFrames(document).forEach(addDocumentStylesToIFrame);
} }
// Only dynamic iframes get the parent document's styles. Other ones should get styles based on their own URLs. // Only dynamic iframes get the parent document's styles. Other ones should get styles based on their own URLs.
function getDynamicIFrames(doc) { function getDynamicIFrames(doc) {
return Array.prototype.filter.call(doc.getElementsByTagName('iframe'), iframeIsDynamic); return Array.prototype.filter.call(doc.getElementsByTagName('iframe'), iframeIsDynamic);
} }
function iframeIsDynamic(f) { function iframeIsDynamic(f) {
var href; var href;
try { try {
href = f.contentDocument.location.href; href = f.contentDocument.location.href;
} catch (ex) { } catch (ex) {
// 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) {
return f.srcdoc && f.contentDocument.all.length <= 3; return f.srcdoc && f.contentDocument.all.length <= 3;
// 3 nodes or less in total (html, head, body) == new empty iframe about to be overwritten by its 'srcdoc' // 3 nodes or less in total (html, head, body) == new empty iframe about to be overwritten by its 'srcdoc'
} }
function addStyleToIFrameSrcDoc(iframe, styleElement) { function addStyleToIFrameSrcDoc(iframe, styleElement) {
if (g_disableAll) { if (g_disableAll) {
return; return;
} }
iframe.srcdoc += styleElement.outerHTML; iframe.srcdoc += styleElement.outerHTML;
// make sure the style is added in case srcdoc was malformed // make sure the style is added in case srcdoc was malformed
setTimeout(addStyleElement.bind(null, styleElement, iframe.contentDocument), 100); setTimeout(addStyleElement.bind(null, styleElement, iframe.contentDocument), 100);
} }
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) { });
replaceAll(newStyles, iframe.contentDocument, pass2); }
}); getDynamicIFrames(doc).forEach(function (iframe) {
if (doc == document && !pass2) { replaceAll(newStyles, iframe.contentDocument, pass2);
g_styleElements = {}; });
applyStyles(newStyles); if (doc == document && !pass2) {
replaceAll(newStyles, doc, true); g_styleElements = {};
} applyStyles(newStyles);
if (pass2) { replaceAll(newStyles, doc, true);
oldStyles.forEach(function(style) { style.remove(); }); }
} if (pass2) {
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)
addDocumentStylesToAllIFrames(); addDocumentStylesToAllIFrames();
return; return;
} }
// move the check out of current execution context // move the check out of current execution context
// because some same-domain (!) iframes fail to load when their "contentDocument" is accessed (!) // because some same-domain (!) iframes fail to load when their "contentDocument" is accessed (!)
// namely gmail's old chat iframe talkgadget.google.com // namely gmail's old chat iframe talkgadget.google.com
setTimeout(process.bind(null, mutations), 0); setTimeout(process.bind(null, mutations), 0);
}); });
function process(mutations) { function process(mutations) {
for (var m = 0, ml = mutations.length; m < ml; m++) { for (var m = 0, ml = mutations.length; m < ml; m++) {
var mutation = mutations[m]; var mutation = mutations[m];
if (mutation.type === "childList") { if (mutation.type === "childList") {
for (var n = 0, nodes = mutation.addedNodes, nl = nodes.length; n < nl; n++) { for (var n = 0, nodes = mutation.addedNodes, nl = nodes.length; n < nl; n++) {
var node = nodes[n]; var node = nodes[n];
if (node.localName === "iframe" && iframeIsDynamic(node)) { if (node.localName === "iframe" && iframeIsDynamic(node)) {
addDocumentStylesToIFrame(node); addDocumentStylesToIFrame(node);
} }
} }
} }
} }
} }
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,9 +1,12 @@
/*jshint undef:false*/
var frameIdMessageable; var frameIdMessageable;
runTryCatch(function() { runTryCatch(function () {
chrome.tabs.sendMessage(0, {}, {frameId: 0}, function() { chrome.tabs.sendMessage(0, {}, {
var clearError = chrome.runtime.lastError; frameId: 0
frameIdMessageable = true; }, function () {
}); var clearError = chrome.runtime.lastError;
frameIdMessageable = true;
});
}); });
// This happens right away, sometimes so fast that the content script isn't even ready. That's // This happens right away, sometimes so fast that the content script isn't even ready. That's
@ -11,159 +14,204 @@ runTryCatch(function() {
chrome.webNavigation.onCommitted.addListener(webNavigationListener.bind(this, "styleApply")); chrome.webNavigation.onCommitted.addListener(webNavigationListener.bind(this, "styleApply"));
// Not supported in Firefox - https://bugzilla.mozilla.org/show_bug.cgi?id=1239349 // Not supported in Firefox - https://bugzilla.mozilla.org/show_bug.cgi?id=1239349
if ("onHistoryStateUpdated" in chrome.webNavigation) { 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({
if (method) { matchUrl: data.url,
chrome.tabs.sendMessage(data.tabId, {method: method, styles: styleHash}, enabled: true,
frameIdMessageable ? {frameId: data.frameId} : undefined); asHash: true
} }, function (styleHash) {
if (data.frameId == 0) { if (method) {
updateIcon({id: data.tabId, url: data.url}, styleHash); 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);
}
});
} }
// 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;
} else if (tabUrlHasHash[tabId]) { } else if (tabUrlHasHash[tabId]) {
delete tabUrlHasHash[tabId]; delete tabUrlHasHash[tabId];
} else { } else {
// 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 updateIcon(sender.tab, styles);
&& sender.tab.url == request.matchUrl) { }
updateIcon(sender.tab, styles); return true;
} case "saveStyle":
return true; saveStyle(request, sendResponse);
case "saveStyle": return true;
saveStyle(request, sendResponse); case "invalidateCache":
return true; if (typeof invalidateCache != "undefined") {
case "invalidateCache": invalidateCache(false);
if (typeof invalidateCache != "undefined") { }
invalidateCache(false); break;
} case "healthCheck":
break; getDatabase(function () {
case "healthCheck": sendResponse(true);
getDatabase(function() { sendResponse(true); }, function() { sendResponse(false); }); }, function () {
return true; sendResponse(false);
case "openURL": });
openURL(request); return true;
break; case "openURL":
case "styleDisableAll": openURL(request);
chrome.contextMenus.update("disableAll", {checked: request.disableAll}); break;
break; case "styleDisableAll":
case "prefChanged": chrome.contextMenus.update("disableAll", {
if (request.prefName == "show-badge") { checked: request.disableAll
chrome.contextMenus.update("show-badge", {checked: request.value}); });
} break;
break; case "prefChanged":
} if (request.prefName == "show-badge") {
chrome.contextMenus.update("show-badge", {
checked: request.value
});
}
break;
}
}); });
// 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({
break; url: chrome.extension.getURL("manage.html")
case "styleDisableAll": });
disableAllStylesToggle(); break;
chrome.contextMenus.update("disableAll", {checked: prefs.get("disableAll")}); case "styleDisableAll":
break; disableAllStylesToggle();
} chrome.contextMenus.update("disableAll", {
}); checked: prefs.get("disableAll")
});
break;
}
});
} }
// 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",
chrome.contextMenus.create({ contexts: ["browser_action"],
id: "disableAll", title: chrome.i18n.getMessage("disableAllStyles"), checked: prefs.get("show-badge")
type: "checkbox", contexts: ["browser_action"], checked: prefs.get("disableAll") }, function () {
}, function() { var clearError = chrome.runtime.lastError }); 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;
});
}); });
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 {
prefs.set(info.menuItemId, info.checked); prefs.set(info.menuItemId, info.checked);
} }
}); });
function disableAllStylesToggle(newState) { function disableAllStylesToggle(newState) {
if (newState === undefined || newState === null) { if (newState === undefined || newState === null) {
newState = !prefs.get("disableAll"); newState = !prefs.get("disableAll");
} }
prefs.set("disableAll", newState); prefs.set("disableAll", 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, {
// If there's only one tab in this window, it's been dragged to new window populate: true
prefs.set("openEditInWindow", win.tabs.length == 1); }, 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);
}); });
}
});
}); });
function openURL(options) { function openURL(options) {
chrome.tabs.query({currentWindow: true, url: options.url}, function(tabs) { chrome.tabs.query({
// switch to an existing tab with the requested url currentWindow: true,
if (tabs.length) { url: options.url
chrome.tabs.highlight({windowId: tabs[0].windowId, tabs: tabs[0].index}, function (window) {}); }, function (tabs) {
} else { // switch to an existing tab with the requested url
delete options.method; if (tabs.length) {
getActiveTab(function(tab) { chrome.tabs.highlight({
// re-use an active new tab page windowId: tabs[0].windowId,
chrome.tabs[tab.url == "chrome://newtab/" ? "update" : "create"](options); tabs: tabs[0].index
}); }, function (window) {});
} } else {
}); delete options.method;
getActiveTab(function (tab) {
// re-use an active new tab page
chrome.tabs[tab.url == "chrome://newtab/" ? "update" : "create"](options);
});
}
});
} }
var codeMirrorThemes; var codeMirrorThemes;
getCodeMirrorThemes(function(themes) { getCodeMirrorThemes(function (themes) {
codeMirrorThemes = themes; codeMirrorThemes = themes;
}); });

3424
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,136 +1,155 @@
chrome.runtime.sendMessage({method: "getStyles", url: getMeta("stylish-id-url") || location.href}, function(response) { /*jshint undef:false*/
if (response.length == 0) { chrome.runtime.sendMessage({
sendEvent("styleCanBeInstalledChrome"); method: "getStyles",
} else { url: getMeta("stylish-id-url") || location.href
var installedStyle = response[0]; }, function (response) {
// maybe an update is needed if (response.length === 0) {
// use the md5 if available sendEvent("styleCanBeInstalledChrome");
var md5Url = getMeta("stylish-md5-url"); } else {
if (md5Url && installedStyle.md5Url && installedStyle.originalMd5) { var installedStyle = response[0];
getResource(md5Url, function(md5) { // maybe an update is needed
if (md5 == installedStyle.originalMd5) { // use the md5 if available
sendEvent("styleAlreadyInstalledChrome", {updateUrl: installedStyle.updateUrl}); var md5Url = getMeta("stylish-md5-url");
} else { if (md5Url && installedStyle.md5Url && installedStyle.originalMd5) {
sendEvent("styleCanBeUpdatedChrome", {updateUrl: installedStyle.updateUrl}); getResource(md5Url, function (md5) {
} if (md5 == installedStyle.originalMd5) {
}); sendEvent("styleAlreadyInstalledChrome", {
} else { updateUrl: installedStyle.updateUrl
getResource(getMeta("stylish-code-chrome"), function(code) { });
// this would indicate a failure (a style with settings?). } else {
if (code == null) { sendEvent("styleCanBeUpdatedChrome", {
sendEvent("styleCanBeUpdatedChrome", {updateUrl: installedStyle.updateUrl}); updateUrl: installedStyle.updateUrl
} });
var json = JSON.parse(code); }
if (json.sections.length == installedStyle.sections.length) { });
if (json.sections.every(function(section) { } else {
return installedStyle.sections.some(function(installedSection) { getResource(getMeta("stylish-code-chrome"), function (code) {
return sectionsAreEqual(section, installedSection); // this would indicate a failure (a style with settings?).
}); if (code === null) {
})) { sendEvent("styleCanBeUpdatedChrome", {
// everything's the same updateUrl: installedStyle.updateUrl
sendEvent("styleAlreadyInstalledChrome", {updateUrl: installedStyle.updateUrl}); });
return; }
}; var json = JSON.parse(code);
} if (json.sections.length == installedStyle.sections.length) {
sendEvent("styleCanBeUpdatedChrome", {updateUrl: installedStyle.updateUrl}); if (json.sections.every(function (section) {
}); return installedStyle.sections.some(function (installedSection) {
} return sectionsAreEqual(section, installedSection);
} });
})) {
// everything's the same
sendEvent("styleAlreadyInstalledChrome", {
updateUrl: installedStyle.updateUrl
});
return;
}
}
sendEvent("styleCanBeUpdatedChrome", {
updateUrl: installedStyle.updateUrl
});
});
}
}
}); });
function sectionsAreEqual(a, b) { 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]);
}); });
} }
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;
}); });
} }
function sendEvent(type, data) { 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, {
document.dispatchEvent(stylishEvent); detail: data
});
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");
}); });
}); });
getResource(getMeta("stylish-install-ping-url-chrome")); getResource(getMeta("stylish-install-ping-url-chrome"));
} }
}); });
}, 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({
var style = response[0]; method: "getStyles",
if (confirm(chrome.i18n.getMessage('styleUpdate', [style.name]))) { url: getMeta("stylish-id-url") || location.href
getResource(getMeta("stylish-code-chrome"), function(code) { }, function (response) {
var json = JSON.parse(code); var style = response[0];
json.method = "saveStyle"; if (confirm(chrome.i18n.getMessage('styleUpdate', [style.name]))) {
json.id = style.id; getResource(getMeta("stylish-code-chrome"), function (code) {
chrome.runtime.sendMessage(json, function() { var json = JSON.parse(code);
sendEvent("styleInstalledChrome"); json.method = "saveStyle";
}); json.id = style.id;
}); chrome.runtime.sendMessage(json, function () {
} sendEvent("styleInstalledChrome");
}); });
});
}
});
}, false); }, false);
function getMeta(name) { function getMeta(name) {
var e = document.querySelector("link[rel='" + name + "']"); var e = document.querySelector("link[rel='" + name + "']");
return e ? e.getAttribute("href") : null; return e ? e.getAttribute("href") : null;
} }
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);
} else { } else {
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);
xhr.send(); xhr.send();
} }
} }

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

887
manage.js
View File

@ -1,3 +1,4 @@
/*jshint undef:false*/
var lastUpdatedStyleId = null; var lastUpdatedStyleId = null;
var installed; var installed;
@ -5,519 +6,551 @@ 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({
return; method: "getStyles"
} }, showStyles);
if (!installed) { return;
// "getStyles" message callback is invoked before document is loaded, }
// postpone the action until DOMContentLoaded is fired if (!installed) {
document.stylishStyles = styles; // "getStyles" message callback is invoked before document is loaded,
return; // postpone the action until DOMContentLoaded is fired
} document.stylishStyles = styles;
styles.sort(function(a, b) { return a.name.localeCompare(b.name)}); return;
styles.map(createStyleElement).forEach(function(e) { }
installed.appendChild(e); styles.sort(function (a, b) {
}); return a.name.localeCompare(b.name);
if (history.state) { });
window.scrollTo(0, history.state.scrollY); styles.map(createStyleElement).forEach(function (e) {
} installed.appendChild(e);
});
if (history.state) {
window.scrollTo(0, history.state.scrollY);
}
} }
function createStyleElement(style) { function createStyleElement(style) {
var e = template.style.cloneNode(true); var e = template.style.cloneNode(true);
e.setAttribute("class", style.enabled ? "enabled" : "disabled"); e.setAttribute("class", style.enabled ? "enabled" : "disabled");
e.setAttribute("style-id", style.id); e.setAttribute("style-id", style.id);
if (style.updateUrl) { if (style.updateUrl) {
e.setAttribute("style-update-url", style.updateUrl); e.setAttribute("style-update-url", style.updateUrl);
} }
if (style.md5Url) { if (style.md5Url) {
e.setAttribute("style-md5-url", style.md5Url); e.setAttribute("style-md5-url", style.md5Url);
} }
if (style.originalMd5) { if (style.originalMd5) {
e.setAttribute("style-original-md5", style.originalMd5); e.setAttribute("style-original-md5", style.originalMd5);
} }
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) {
style.sections.forEach(function(section) { function add(array, property) {
if (section[property]) { style.sections.forEach(function (section) {
section[property].filter(function(value) { if (section[property]) {
return array.indexOf(value) == -1; section[property].filter(function (value) {
}).forEach(function(value) { return array.indexOf(value) == -1;
array.push(value); }).forEach(function (value) {
});; array.push(value);
} });
}); }
} });
add(domains, 'domains'); }
add(urls, 'urls'); add(domains, 'domains');
add(urlPrefixes, 'urlPrefixes'); add(urls, 'urls');
add(regexps, 'regexps'); add(urlPrefixes, 'urlPrefixes');
var appliesToToShow = []; add(regexps, 'regexps');
if (domains) var appliesToToShow = [];
appliesToToShow = appliesToToShow.concat(domains); if (domains)
if (urls) appliesToToShow = appliesToToShow.concat(domains);
appliesToToShow = appliesToToShow.concat(urls); if (urls)
if (urlPrefixes) appliesToToShow = appliesToToShow.concat(urls);
appliesToToShow = appliesToToShow.concat(urlPrefixes.map(function(u) { return u + "*"; })); if (urlPrefixes)
if (regexps) appliesToToShow = appliesToToShow.concat(urlPrefixes.map(function (u) {
appliesToToShow = appliesToToShow.concat(regexps.map(function(u) { return "/" + u + "/"; })); return u + "*";
var appliesToString = ""; }));
var showAppliesToExtra = false; if (regexps)
if (appliesToToShow.length == "") appliesToToShow = appliesToToShow.concat(regexps.map(function (u) {
appliesToString = t('appliesToEverything'); return "/" + u + "/";
else if (appliesToToShow.length <= 10) }));
appliesToString = appliesToToShow.join(", "); var appliesToString = "";
else { var showAppliesToExtra = false;
appliesToString = appliesToToShow.slice(0, 10).join(", "); if (appliesToToShow.length === "")
showAppliesToExtra = true; appliesToString = t('appliesToEverything');
} else if (appliesToToShow.length <= 10)
e.querySelector(".applies-to").appendChild(document.createTextNode(t('appliesDisplay', [appliesToString]))); appliesToString = appliesToToShow.join(", ");
if (showAppliesToExtra) { else {
e.querySelector(".applies-to").appendChild(appliesToExtraTemplate.cloneNode(true)); appliesToString = appliesToToShow.slice(0, 10).join(", ");
} showAppliesToExtra = true;
var editLink = e.querySelector(".style-edit-link"); }
editLink.setAttribute("href", editLink.getAttribute("href") + style.id); e.querySelector(".applies-to").appendChild(document.createTextNode(t('appliesDisplay', [appliesToString])));
editLink.addEventListener("click", function(event) { if (showAppliesToExtra) {
if (!event.altKey) { e.querySelector(".applies-to").appendChild(appliesToExtraTemplate.cloneNode(true));
var left = event.button == 0, middle = event.button == 1, }
shift = event.shiftKey, ctrl = event.ctrlKey; var editLink = e.querySelector(".style-edit-link");
var openWindow = left && shift && !ctrl; editLink.setAttribute("href", editLink.getAttribute("href") + style.id);
var openBackgroundTab = (middle && !shift) || (left && ctrl && !shift); editLink.addEventListener("click", function (event) {
var openForegroundTab = (middle && shift) || (left && ctrl && shift); if (!event.altKey) {
var url = event.target.href || event.target.parentNode.href; var left = event.button === 0,
event.preventDefault(); middle = event.button == 1,
event.stopPropagation(); shift = event.shiftKey,
if (openWindow || openBackgroundTab || openForegroundTab) { ctrl = event.ctrlKey;
if (openWindow) { var openWindow = left && shift && !ctrl;
var options = prefs.get("windowPosition"); var openBackgroundTab = (middle && !shift) || (left && ctrl && !shift);
options.url = url; var openForegroundTab = (middle && shift) || (left && ctrl && shift);
chrome.windows.create(options); var url = event.target.href || event.target.parentNode.href;
} else { event.preventDefault();
chrome.runtime.sendMessage({ event.stopPropagation();
method: "openURL", if (openWindow || openBackgroundTab || openForegroundTab) {
url: url, if (openWindow) {
active: openForegroundTab var options = prefs.get("windowPosition");
}); options.url = url;
} chrome.windows.create(options);
} else { } else {
history.replaceState({scrollY: window.scrollY}, document.title); chrome.runtime.sendMessage({
getActiveTab(function(tab) { method: "openURL",
sessionStorageHash("manageStylesHistory").set(tab.id, url); url: url,
location.href = url; active: openForegroundTab
}); });
} }
} } else {
}); history.replaceState({
e.querySelector(".enable").addEventListener("click", function(event) { enable(event, true); }, false); scrollY: window.scrollY
e.querySelector(".disable").addEventListener("click", function(event) { enable(event, false); }, false); }, document.title);
e.querySelector(".check-update").addEventListener("click", doCheckUpdate, false); getActiveTab(function (tab) {
e.querySelector(".update").addEventListener("click", doUpdate, false); sessionStorageHash("manageStylesHistory").set(tab.id, url);
e.querySelector(".delete").addEventListener("click", doDelete, false); location.href = url;
return e; });
}
}
});
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);
return e;
} }
function enable(event, enabled) { function enable(event, enabled) {
var id = getId(event); var id = getId(event);
enableStyle(id, enabled); enableStyle(id, enabled);
} }
function doDelete() { function doDelete() {
if (!confirm(t('deleteStyleConfirm'))) { if (!confirm(t('deleteStyleConfirm'))) {
return; return;
} }
var id = getId(event); var id = getId(event);
deleteStyle(id); deleteStyle(id);
} }
function getId(event) { function getId(event) {
return getStyleElement(event).getAttribute("style-id"); return getStyleElement(event).getAttribute("style-id");
} }
function getStyleElement(event) { function getStyleElement(event) {
var e = event.target; var e = event.target;
while (e) { while (e) {
if (e.hasAttribute("style-id")) { if (e.hasAttribute("style-id")) {
return e; return e;
} }
e = e.parentNode; e = e.parentNode;
} }
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);
break; break;
case "styleAdded": case "styleAdded":
installed.appendChild(createStyleElement(request.style)); installed.appendChild(createStyleElement(request.style));
break; break;
case "styleDeleted": case "styleDeleted":
handleDelete(request.id); handleDelete(request.id);
break; break;
} }
}); });
function handleUpdate(style) { function handleUpdate(style) {
var element = createStyleElement(style); var element = createStyleElement(style);
installed.replaceChild(element, installed.querySelector("[style-id='" + style.id + "']")); installed.replaceChild(element, installed.querySelector("[style-id='" + style.id + "']"));
if (style.id == lastUpdatedStyleId) { if (style.id == lastUpdatedStyleId) {
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) {
var node = installed.querySelector("[style-id='" + id + "']"); var node = installed.querySelector("[style-id='" + id + "']");
if (node) { if (node) {
installed.removeChild(node); installed.removeChild(node);
} }
} }
function doCheckUpdate(event) { function doCheckUpdate(event) {
checkUpdate(getStyleElement(event)); checkUpdate(getStyleElement(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();
}); });
} }
function checkUpdateAll() { function checkUpdateAll() {
var btnCheck = document.getElementById("check-all-updates"); var btnCheck = document.getElementById("check-all-updates");
var btnApply = document.getElementById("apply-all-updates"); var btnApply = document.getElementById("apply-all-updates");
var noUpdates = document.getElementById("update-all-no-updates"); var noUpdates = document.getElementById("update-all-no-updates");
btnCheck.disabled = true; btnCheck.disabled = true;
btnApply.classList.add("hidden"); btnApply.classList.add("hidden");
noUpdates.classList.add("hidden"); noUpdates.classList.add("hidden");
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);
} }
} }
}); });
}); });
} }
function checkUpdate(element, callback) { function checkUpdate(element, callback) {
element.querySelector(".update-note").innerHTML = t('checkingForUpdate'); element.querySelector(".update-note").innerHTML = t('checkingForUpdate');
element.className = element.className.replace("checking-update", "").replace("no-update", "").replace("can-update", "") + " checking-update"; element.className = element.className.replace("checking-update", "").replace("no-update", "").replace("can-update", "") + " checking-update";
var id = element.getAttribute("style-id"); var id = element.getAttribute("style-id");
var url = element.getAttribute("style-update-url"); var url = element.getAttribute("style-update-url");
var md5Url = element.getAttribute("style-md5-url"); var md5Url = element.getAttribute("style-md5-url");
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({
var style = styles[0]; method: "getStyles",
var needsUpdate = false; id: id
if (!forceUpdate && codeIsEqual(style.sections, serverJson.sections)) { }, function (styles) {
handleNeedsUpdate("no", id, serverJson); var style = styles[0];
} else { var needsUpdate = false;
handleNeedsUpdate("yes", id, serverJson); if (!forceUpdate && codeIsEqual(style.sections, serverJson.sections)) {
needsUpdate = true; handleNeedsUpdate("no", id, serverJson);
} } else {
if (callback) { handleNeedsUpdate("yes", id, serverJson);
callback(needsUpdate); needsUpdate = true;
} }
}); if (callback) {
} callback(needsUpdate);
}
});
}
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);
} }
if (callback) { if (callback) {
callback(false); callback(false);
} }
} }
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);
} else { } else {
handleNeedsUpdate("no", id, null); handleNeedsUpdate("no", id, null);
if (callback) { if (callback) {
callback(false); callback(false);
} }
} }
}, handleFailure); }, handleFailure);
} }
} }
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;
} }
successCallback(responseText != originalMd5); successCallback(responseText != originalMd5);
}, failureCallback); }, failureCallback);
} }
function download(url, successCallback, failureCallback) { function download(url, successCallback, failureCallback) {
var xhr = new XMLHttpRequest(); var xhr = new XMLHttpRequest();
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);
xhr.send(); xhr.send();
} }
} }
function handleNeedsUpdate(needsUpdate, id, serverJson) { function handleNeedsUpdate(needsUpdate, id, serverJson) {
var e = document.querySelector("[style-id='" + id + "']"); var e = document.querySelector("[style-id='" + id + "']");
e.className = e.className.replace("checking-update", ""); e.className = e.className.replace("checking-update", "");
switch (needsUpdate) { switch (needsUpdate) {
case "yes": case "yes":
e.className += " can-update"; e.className += " can-update";
e.updatedCode = serverJson; e.updatedCode = serverJson;
e.querySelector(".update-note").innerHTML = ''; e.querySelector(".update-note").innerHTML = '';
break; break;
case "no": case "no":
e.className += " no-update"; e.className += " no-update";
e.querySelector(".update-note").innerHTML = t('updateCheckSucceededNoUpdate'); e.querySelector(".update-note").innerHTML = t('updateCheckSucceededNoUpdate');
break; break;
default: default:
e.className += " no-update"; e.className += " no-update";
e.querySelector(".update-note").innerHTML = needsUpdate; e.querySelector(".update-note").innerHTML = needsUpdate;
} }
} }
function doUpdate(event) { function doUpdate(event) {
var element = getStyleElement(event); var element = getStyleElement(event);
var updatedCode = element.updatedCode; var updatedCode = element.updatedCode;
// update everything but name // update everything but name
delete updatedCode.name; delete updatedCode.name;
updatedCode.id = element.getAttribute('style-id'); updatedCode.id = element.getAttribute('style-id');
updatedCode.method = "saveStyle"; updatedCode.method = "saveStyle";
// updating the UI will be handled by the general update listener // updating the UI will be handled by the general update listener
lastUpdatedStyleId = updatedCode.id; lastUpdatedStyleId = updatedCode.id;
chrome.runtime.sendMessage(updatedCode, function () {}); chrome.runtime.sendMessage(updatedCode, function () {});
} }
function codeIsEqual(a, b) { function codeIsEqual(a, b) {
if (a.length != b.length) { if (a.length != b.length) {
return false; return false;
} }
var properties = ["code", "urlPrefixes", "urls", "domains", "regexps"]; var properties = ["code", "urlPrefixes", "urls", "domains", "regexps"];
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) {
found = true; found = true;
break; break;
} }
} }
if (!found) { if (!found) {
return false; return false;
} }
} }
return true; return true;
} }
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);
if (typeA != typeB) { var bProp = b[property],
// consider empty arrays equivalent to lack of property typeB = getType(bProp);
if ((typeA == "undefined" || (typeA == "array" && aProp.length == 0)) && (typeB == "undefined" || (typeB == "array" && bProp.length == 0))) { if (typeA != typeB) {
return true; // consider empty arrays equivalent to lack of property
} if ((typeA == "undefined" || (typeA == "array" && aProp.length === 0)) && (typeB == "undefined" || (typeB == "array" && bProp.length === 0))) {
return false; return true;
} }
if (typeA == "undefined") { return false;
return true; }
} if (typeA == "undefined") {
if (typeA == "array") { return true;
if (aProp.length != bProp.length) { }
return false; if (typeA == "array") {
} if (aProp.length != bProp.length) {
for (var i = 0; i < aProp.length; i++) { return false;
if (bProp.indexOf(aProp[i]) == -1) { }
return false; for (var i = 0; i < aProp.length; i++) {
} if (bProp.indexOf(aProp[i]) == -1) {
} return false;
return true; }
} }
if (typeA == "string") { return true;
return aProp == bProp; }
} if (typeA == "string") {
return aProp == bProp;
}
} }
function searchStyles(immediately) { function searchStyles(immediately) {
var query = document.getElementById("search").value.toLocaleLowerCase(); var query = document.getElementById("search").value.toLocaleLowerCase();
if (query == (searchStyles.lastQuery || "")) { if (query == (searchStyles.lastQuery || "")) {
return; return;
} }
searchStyles.lastQuery = query; searchStyles.lastQuery = query;
if (immediately) { if (immediately) {
doSearch(); doSearch();
} else { } else {
clearTimeout(searchStyles.timeout); clearTimeout(searchStyles.timeout);
searchStyles.timeout = setTimeout(doSearch, 100); searchStyles.timeout = setTimeout(doSearch, 100);
} }
function doSearch() {
chrome.runtime.sendMessage({method: "getStyles"}, function(styles) { function doSearch() {
styles.forEach(function(style) { chrome.runtime.sendMessage({
var el = document.querySelector("[style-id='" + style.id + "']"); method: "getStyles"
if (el) { }, function (styles) {
el.style.display = !query || isMatchingText(style.name) || isMatchingStyle(style) ? "" : "none"; styles.forEach(function (style) {
} var el = document.querySelector("[style-id='" + style.id + "']");
}); if (el) {
}); el.style.display = !query || isMatchingText(style.name) || isMatchingStyle(style) ? "" : "none";
} }
function isMatchingStyle(style) { });
return style.sections.some(function(section) { });
return Object.keys(section).some(function(key) { }
var value = section[key];
switch (typeof value) { function isMatchingStyle(style) {
case "string": return isMatchingText(value); return style.sections.some(function (section) {
case "object": return value.some(isMatchingText); return Object.keys(section).some(function (key) {
} var value = section[key];
}); switch (typeof value) {
}); case "string":
} return isMatchingText(value);
function isMatchingText(text) { case "object":
return text.toLocaleLowerCase().indexOf(query) >= 0; return value.some(isMatchingText);
} }
});
});
}
function isMatchingText(text) {
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() }
}
}
reader.onloadend = function (evt) {
try {
styles = JSON.parse(evt.target.result);
save();
} }
catch (e) {
window.alert(e.message); reader.onloadend = function (evt) {
try {
styles = JSON.parse(evt.target.result);
save();
} catch (e) {
window.alert(e.message);
}
};
reader.onerror = function (e) {
window.alert(e.message);
};
reader.readAsText(file);
}
function selectAll() {
document.execCommand('selectAll');
}
document.addEventListener("DOMContentLoaded", function () {
installed = document.getElementById("installed");
if (document.stylishStyles) {
showStyles(document.stylishStyles);
delete document.stylishStyles;
} }
};
reader.onerror = function (e) {
window.alert(e.message);
}
reader.readAsText(file)
}
function selectAll () { document.getElementById("check-all-updates").addEventListener("click", checkUpdateAll);
document.execCommand('selectAll'); document.getElementById("apply-all-updates").addEventListener("click", applyUpdateAll);
} document.getElementById("search").addEventListener("input", searchStyles);
searchStyles(true); // re-apply filtering on history Back
document.addEventListener("DOMContentLoaded", function() { document.querySelector('input[type=file]').addEventListener('change', importStyles);
installed = document.getElementById("installed"); document.querySelector('#import pre').addEventListener('click', selectAll);
if (document.stylishStyles) {
showStyles(document.stylishStyles);
delete document.stylishStyles;
}
document.getElementById("check-all-updates").addEventListener("click", checkUpdateAll); setupLivePrefs([
document.getElementById("apply-all-updates").addEventListener("click", applyUpdateAll);
document.getElementById("search").addEventListener("input", searchStyles);
searchStyles(true); // re-apply filtering on history Back
document.querySelector('input[type=file]').addEventListener('change', importStyles);
document.querySelector('#import pre').addEventListener('click', selectAll);
setupLivePrefs([
"manage.onlyEnabled", "manage.onlyEnabled",
"manage.onlyEdited", "manage.onlyEdited",
"show-badge", "show-badge",
"popup.stylesFirst" "popup.stylesFirst"
]); ]);
initFilter("enabled-only", document.getElementById("manage.onlyEnabled")); initFilter("enabled-only", document.getElementById("manage.onlyEnabled"));
initFilter("edited-only", document.getElementById("manage.onlyEdited")); initFilter("edited-only", document.getElementById("manage.onlyEdited"));
}); });

View File

@ -1,90 +1,117 @@
/*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) {
chrome.tabs.sendMessage(tab.id, request); windows.forEach(function (win) {
updateIcon(tab); win.tabs.forEach(function (tab) {
}); chrome.tabs.sendMessage(tab.id, request);
}); updateIcon(tab);
}); });
// notify all open popups });
var reqPopup = shallowMerge({}, request, {method: "updatePopup", reason: request.method}); });
chrome.runtime.sendMessage(reqPopup); // notify all open popups
var reqPopup = shallowMerge({}, request, {
method: "updatePopup",
reason: request.method
});
chrome.runtime.sendMessage(reqPopup);
} }
function updateIcon(tab, styles) { function updateIcon(tab, styles) {
// while NTP is still loading only process the request for its main frame with a real url // while NTP is still loading only process the request for its main frame with a real url
// (but when it's loaded we should process style toggle requests from popups, for example) // (but when it's loaded we should process style toggle requests from popups, for example)
if (tab.url == "chrome://newtab/" && tab.status != "complete") { if (tab.url == "chrome://newtab/" && tab.status != "complete") {
return; return;
} }
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) {
styles.length = 0; styles.length = 0;
for (var id in styles) { for (var id in styles) {
styles.length += id.match(/^\d+$/) ? 1 : 0; styles.length += id.match(/^\d+$/) ? 1 : 0;
} }
} }
stylesReceived(styles); stylesReceived(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({
} else { matchUrl: url,
chrome.runtime.sendMessage({method: "getStyles", matchUrl: url, enabled: true}, stylesReceived); enabled: true
} }, stylesReceived);
}); } else {
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",
// Chromium forks or non-chromium browsers may still use the traditional 19px 32: "32" + postfix + ".png",
19: "19" + postfix + ".png", 38: "38" + postfix + ".png", // Chromium forks or non-chromium browsers may still use the traditional 19px
}, 19: "19" + postfix + ".png",
tabId: tab.id 38: "38" + postfix + ".png",
}, function() { },
// if the tab was just closed an error may occur, tabId: tab.id
// e.g. 'windowPosition' pref updated in edit.js::window.onbeforeunload }, function () {
if (!chrome.runtime.lastError) { // if the tab was just closed an error may occur,
var t = prefs.get("show-badge") && styles.length ? ("" + styles.length) : ""; // e.g. 'windowPosition' pref updated in edit.js::window.onbeforeunload
chrome.browserAction.setBadgeText({text: t, tabId: tab.id}); if (!chrome.runtime.lastError) {
chrome.browserAction.setBadgeBackgroundColor({color: disableAll ? "#aaa" : [0, 0, 0, 0]}); var t = prefs.get("show-badge") && styles.length ? ("" + styles.length) : "";
} chrome.browserAction.setBadgeText({
}); text: t,
//console.log("Tab " + tab.id + " (" + tab.url + ") badge text set to '" + 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 + "'.");
}
} }
function getActiveTab(callback) { function getActiveTab(callback) {
chrome.tabs.query({currentWindow: true, active: true}, function(tabs) { chrome.tabs.query({
callback(tabs[0]); currentWindow: true,
}); active: true
}, function (tabs) {
callback(tabs[0]);
});
} }
function getActiveTabRealURL(callback) { function getActiveTabRealURL(callback) {
getActiveTab(function(tab) { getActiveTab(function (tab) {
getTabRealURL(tab, callback); getTabRealURL(tab, callback);
}); });
} }
function getTabRealURL(tab, callback) { 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({
frame && callback(frame.url); tabId: tab.id,
}); frameId: 0,
} processId: -1
}, function (frame) {
frame && callback(frame.url);
});
}
} }

330
popup.js
View File

@ -1,209 +1,235 @@
/*jshint undef:false*/
var writeStyleTemplate = document.createElement("a"); var writeStyleTemplate = document.createElement("a");
writeStyleTemplate.className = "write-style-link"; writeStyleTemplate.className = "write-style-link";
var installed = document.getElementById("installed"); var installed = document.getElementById("installed");
if (!prefs.get("popup.stylesFirst")) { if (!prefs.get("popup.stylesFirst")) {
document.body.insertBefore(document.querySelector("body > .actions"), installed); document.body.insertBefore(document.querySelector("body > .actions"), installed);
} }
getActiveTabRealURL(updatePopUp); getActiveTabRealURL(updatePopUp);
function updatePopUp(url) { function updatePopUp(url) {
var urlWillWork = /^(file|http|https|ftps?|chrome\-extension):/.exec(url); var urlWillWork = /^(file|http|https|ftps?|chrome\-extension):/.exec(url);
if (!urlWillWork) { if (!urlWillWork) {
document.body.classList.add("blocked"); document.body.classList.add("blocked");
document.getElementById("unavailable").style.display = "block"; document.getElementById("unavailable").style.display = "block";
return; return;
} }
chrome.runtime.sendMessage({method: "getStyles", matchUrl: url}, showStyles); chrome.runtime.sendMessage({
document.querySelector("#find-styles a").href = "https://userstyles.org/styles/browse/all/" + encodeURIComponent("file" === urlWillWork[1] ? "file:" : url); 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 // Write new style links
var writeStyleLinks = [], var writeStyleLinks = [],
container = document.createElement('span'); container = document.createElement('span');
container.id = "match"; container.id = "match";
// For this URL // For this 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);
)); writeStyleLinks.push(urlLink);
urlLink.title = "url-prefix(\"$\")".replace("$", url); document.querySelector("#write-style").appendChild(urlLink);
writeStyleLinks.push(urlLink); if (prefs.get("popup.breadcrumbs")) { // switchable; default=enabled
document.querySelector("#write-style").appendChild(urlLink) urlLink.addEventListener("mouseenter", function (event) {
if (prefs.get("popup.breadcrumbs")) { // switchable; default=enabled this.parentNode.classList.add("url()");
urlLink.addEventListener("mouseenter", function(event) { this.parentNode.classList.add("url()") }, false); }, false);
urlLink.addEventListener("focus", function(event) { this.parentNode.classList.add("url()") }, false); urlLink.addEventListener("focus", function (event) {
urlLink.addEventListener("mouseleave", function(event) { this.parentNode.classList.remove("url()") }, false); this.parentNode.classList.add("url()");
urlLink.addEventListener("blur", function(event) { this.parentNode.classList.remove("url()") }, false); }, 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;
} }
var domainLink = writeStyleTemplate.cloneNode(true); var domainLink = writeStyleTemplate.cloneNode(true);
domainLink.href = "edit.html?domain=" + encodeURIComponent(domain); domainLink.href = "edit.html?domain=" + encodeURIComponent(domain);
domainLink.appendChild(document.createTextNode(domain)); domainLink.appendChild(document.createTextNode(domain));
domainLink.title = "domain(\"$\")".replace("$", domain); domainLink.title = "domain(\"$\")".replace("$", domain);
domainLink.setAttribute("subdomain", domain.substring(0, domain.indexOf("."))); domainLink.setAttribute("subdomain", domain.substring(0, domain.indexOf(".")));
writeStyleLinks.push(domainLink); writeStyleLinks.push(domainLink);
}); });
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);
}); });
if (prefs.get("popup.breadcrumbs")) { if (prefs.get("popup.breadcrumbs")) {
container.classList.add("breadcrumbs"); container.classList.add("breadcrumbs");
container.appendChild(container.removeChild(container.firstChild)); container.appendChild(container.removeChild(container.firstChild));
} }
writeStyle.appendChild(container); writeStyle.appendChild(container);
} }
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);
}); });
} }
function createStyleElement(style) { function createStyleElement(style) {
var e = template.style.cloneNode(true); var e = template.style.cloneNode(true);
var checkbox = e.querySelector(".checker"); var checkbox = e.querySelector(".checker");
checkbox.id = "style-" + style.id; checkbox.id = "style-" + style.id;
checkbox.checked = style.enabled; checkbox.checked = style.enabled;
e.setAttribute("class", "entry " + (style.enabled ? "enabled" : "disabled")); e.setAttribute("class", "entry " + (style.enabled ? "enabled" : "disabled"));
e.setAttribute("style-id", style.id); e.setAttribute("style-id", style.id);
var styleName = e.querySelector(".style-name"); var styleName = e.querySelector(".style-name");
styleName.appendChild(document.createTextNode(style.name)); styleName.appendChild(document.createTextNode(style.name));
styleName.setAttribute("for", "style-" + style.id); styleName.setAttribute("for", "style-" + style.id);
styleName.checkbox = checkbox; styleName.checkbox = checkbox;
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", openLinkInTabOrWindow, false); editLink.addEventListener("click", openLinkInTabOrWindow, false);
styleName.addEventListener("click", function() { this.checkbox.click(); event.preventDefault(); }); styleName.addEventListener("click", function () {
// clicking the checkbox will toggle it, and this will run after that happens this.checkbox.click();
checkbox.addEventListener("click", function() { enable(event, event.target.checked); }, false); event.preventDefault();
e.querySelector(".enable").addEventListener("click", function() { enable(event, true); }, false); });
e.querySelector(".disable").addEventListener("click", function() { enable(event, false); }, false); // 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);
e.querySelector(".delete").addEventListener("click", function() { doDelete(event, false); }, false); e.querySelector(".delete").addEventListener("click", function () {
return e; doDelete(event, false);
}, false);
return e;
} }
function enable(event, enabled) { function enable(event, enabled) {
var id = getId(event); var id = getId(event);
enableStyle(id, enabled); enableStyle(id, enabled);
} }
function doDelete() { function doDelete() {
// Opera can't do confirms in popups // Opera can't do confirms in popups
if (getBrowser() != "Opera") { if (getBrowser() != "Opera") {
if (!confirm(t('deleteStyleConfirm'))) { if (!confirm(t('deleteStyleConfirm'))) {
return; return;
} }
} }
var id = getId(event); var id = getId(event);
deleteStyle(id); deleteStyle(id);
} }
function getBrowser() { function getBrowser() {
if (navigator.userAgent.indexOf("OPR") > -1) { if (navigator.userAgent.indexOf("OPR") > -1) {
return "Opera"; return "Opera";
} }
return "Chrome"; return "Chrome";
} }
function getId(event) { function getId(event) {
var e = event.target; var e = event.target;
while (e) { while (e) {
if (e.hasAttribute("style-id")) { if (e.hasAttribute("style-id")) {
return e.getAttribute("style-id"); return e.getAttribute("style-id");
} }
e = e.parentNode; e = e.parentNode;
} }
return null; return null;
} }
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 = {
var wp = prefs.get("windowPosition", {}); url: event.target.href
for (var k in wp) options[k] = wp[k]; };
chrome.windows.create(options); var wp = prefs.get("windowPosition", {});
} else { for (var k in wp) options[k] = wp[k];
openLink(event); chrome.windows.create(options);
} } else {
close(); openLink(event);
}
close();
} }
function openLink(event) { function openLink(event) {
event.preventDefault(); event.preventDefault();
chrome.runtime.sendMessage({method: "openURL", url: event.target.href}); chrome.runtime.sendMessage({
close(); method: "openURL",
url: event.target.href
});
close();
} }
function handleUpdate(style) { function handleUpdate(style) {
var styleElement = installed.querySelector("[style-id='" + style.id + "']"); var styleElement = installed.querySelector("[style-id='" + style.id + "']");
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";
installed.appendChild(createStyleElement(style)); installed.appendChild(createStyleElement(style));
} }
}); });
} }
} }
function handleDelete(id) { function handleDelete(id) {
var styleElement = installed.querySelector("[style-id='" + id + "']"); var styleElement = installed.querySelector("[style-id='" + id + "']");
if (styleElement) { if (styleElement) {
installed.removeChild(styleElement); installed.removeChild(styleElement);
} }
} }
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":
case "styleUpdated": case "styleUpdated":
handleUpdate(request.style); handleUpdate(request.style);
break; break;
case "styleDeleted": case "styleDeleted":
handleDelete(request.id); handleDelete(request.id);
break; break;
} }
} }
}); });
["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,169 +1,193 @@
/*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] = [];
} }
}); });
}); });
}, },
getStyles: function(callback) { getStyles: function (callback) {
webSqlStorage.getDatabase(function(db) { webSqlStorage.getDatabase(function (db) {
if (!db) { if (!db) {
callback([]); callback([]);
return; return;
} }
db.readTransaction(function (t) { db.readTransaction(function (t) {
var where = ""; var where = "";
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,
for (var i = 0; i < r.rows.length; i++) { metaName;
var values = r.rows.item(i); for (var i = 0; i < r.rows.length; i++) {
var metaName = null; var values = r.rows.item(i);
switch (values.metaName) { metaName = null;
case null: switch (values.metaName) {
break; case null:
case "url": break;
metaName = "urls"; case "url":
break; metaName = "urls";
case "url-prefix": break;
metaName = "urlPrefixes"; case "url-prefix":
break; metaName = "urlPrefixes";
case "domain": break;
var metaName = "domains"; case "domain":
break; metaName = "domains";
case "regexps": break;
var metaName = "regexps"; case "regexps":
break; metaName = "regexps";
default: break;
var metaName = values.metaName + "s"; default:
} metaName = values.metaName + "s";
var metaValue = values.metaValue; }
if (currentStyle == null || currentStyle.id != values.id) { var metaValue = values.metaValue;
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) {
styles.push(currentStyle); currentStyle = {
} id: values.id,
if (values.section_id != null) { url: values.url,
if (currentSection == null || currentSection.id != values.section_id) { updateUrl: values.updateUrl,
currentSection = {id: values.section_id, code: values.code}; md5Url: values.md5Url,
currentStyle.sections.push(currentSection); name: values.name,
} enabled: values.enabled == "true",
if (metaName && metaValue) { originalMd5: values.originalMd5,
if (currentSection[metaName]) { sections: []
currentSection[metaName].push(metaValue); };
} else { styles.push(currentStyle);
currentSection[metaName] = [metaValue]; }
} if (values.section_id !== null) {
} if (currentSection === null || currentSection.id != values.section_id) {
} currentSection = {
} id: values.section_id,
callback(styles); code: values.code
}, reportError); };
}, reportError); currentStyle.sections.push(currentSection);
}, reportError); }
}, if (metaName && metaValue) {
if (currentSection[metaName]) {
currentSection[metaName].push(metaValue);
} else {
currentSection[metaName] = [metaValue];
}
}
}
}
callback(styles);
}, reportError);
}, 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;
} }
if (stylishDb.version == "1.0") { if (stylishDb.version == "1.0") {
webSqlStorage.dbV11(stylishDb, error, ready); webSqlStorage.dbV11(stylishDb, error, ready);
} else if (stylishDb.version == "1.1") { } else if (stylishDb.version == "1.1") {
webSqlStorage.dbV12(stylishDb, error, ready); webSqlStorage.dbV12(stylishDb, error, ready);
} else if (stylishDb.version == "1.2") { } else if (stylishDb.version == "1.2") {
webSqlStorage.dbV13(stylishDb, error, ready); webSqlStorage.dbV13(stylishDb, error, ready);
} else if (stylishDb.version == "1.3") { } else if (stylishDb.version == "1.3") {
webSqlStorage.dbV14(stylishDb, error, ready); webSqlStorage.dbV14(stylishDb, error, ready);
} else if (stylishDb.version == "1.4") { } else if (stylishDb.version == "1.4") {
webSqlStorage.dbV15(stylishDb, error, ready); webSqlStorage.dbV15(stylishDb, error, ready);
} else { } else {
ready(stylishDb); ready(stylishDb);
} }
}, },
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);');
t.executeSql('INSERT INTO sections (style_id, code) SELECT id, code FROM styles;'); t.executeSql('INSERT INTO sections (style_id, code) SELECT id, code FROM styles;');
// switch meta to sections // switch meta to sections
t.executeSql('DROP INDEX style_meta_style_id;'); t.executeSql('DROP INDEX style_meta_style_id;');
t.executeSql('CREATE TABLE section_meta (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, section_id INTEGER NOT NULL, name TEXT NOT NULL, value TEXT NOT NULL);'); t.executeSql('CREATE TABLE section_meta (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, section_id INTEGER NOT NULL, name TEXT NOT NULL, value TEXT NOT NULL);');
t.executeSql('INSERT INTO section_meta (section_id, name, value) SELECT s.id, sm.name, sm.value FROM sections s INNER JOIN style_meta sm ON sm.style_id = s.style_id;'); t.executeSql('INSERT INTO section_meta (section_id, name, value) SELECT s.id, sm.name, sm.value FROM sections s INNER JOIN style_meta sm ON sm.style_id = s.style_id;');
t.executeSql('CREATE INDEX section_meta_section_id ON section_meta (section_id);'); t.executeSql('CREATE INDEX section_meta_section_id ON section_meta (section_id);');
t.executeSql('DROP TABLE style_meta;'); t.executeSql('DROP TABLE style_meta;');
// drop extra fields from styles table // drop extra fields from styles table
t.executeSql('CREATE TABLE newstyles (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, url TEXT, updateUrl TEXT, md5Url TEXT, name TEXT NOT NULL, enabled INTEGER NOT NULL);'); t.executeSql('CREATE TABLE newstyles (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, url TEXT, updateUrl TEXT, md5Url TEXT, name TEXT NOT NULL, enabled INTEGER NOT NULL);');
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);
} });
}
};

1025
storage.js

File diff suppressed because it is too large Load Diff