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

View File

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

View File

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

310
edit.js
View File

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

View File

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

View File

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

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

101
manage.js
View File

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

View File

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

View File

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

View File

@ -1,3 +1,4 @@
/*jshint undef:false*/
var webSqlStorage = { var webSqlStorage = {
migrate: function () { migrate: function () {
@ -10,7 +11,7 @@ var webSqlStorage = {
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
@ -44,12 +45,13 @@ var webSqlStorage = {
var params = []; var params = [];
t.executeSql('SELECT DISTINCT s.*, se.id section_id, se.code, sm.name metaName, sm.value metaValue FROM styles s LEFT JOIN sections se ON se.style_id = s.id LEFT JOIN section_meta sm ON sm.section_id = se.id WHERE 1' + where + ' ORDER BY s.id, se.id, sm.id', params, function (t, r) { t.executeSql('SELECT DISTINCT s.*, se.id section_id, se.code, sm.name metaName, sm.value metaValue FROM styles s LEFT JOIN sections se ON se.style_id = s.id LEFT JOIN section_meta sm ON sm.section_id = se.id WHERE 1' + where + ' ORDER BY s.id, se.id, sm.id', params, function (t, r) {
var styles = []; var styles = [],
var currentStyle = null; currentStyle = null,
var currentSection = null; currentSection = null,
metaName;
for (var i = 0; i < r.rows.length; i++) { for (var i = 0; i < r.rows.length; i++) {
var values = r.rows.item(i); var values = r.rows.item(i);
var metaName = null; metaName = null;
switch (values.metaName) { switch (values.metaName) {
case null: case null:
break; break;
@ -60,22 +62,34 @@ var webSqlStorage = {
metaName = "urlPrefixes"; metaName = "urlPrefixes";
break; break;
case "domain": case "domain":
var metaName = "domains"; metaName = "domains";
break; break;
case "regexps": case "regexps":
var metaName = "regexps"; metaName = "regexps";
break; break;
default: default:
var metaName = values.metaName + "s"; metaName = values.metaName + "s";
} }
var metaValue = values.metaValue; var metaValue = values.metaValue;
if (currentStyle == null || currentStyle.id != values.id) { if (currentStyle === null || currentStyle.id != values.id) {
currentStyle = {id: values.id, url: values.url, updateUrl: values.updateUrl, md5Url: values.md5Url, name: values.name, enabled: values.enabled == "true", originalMd5: values.originalMd5, sections: []}; currentStyle = {
id: values.id,
url: values.url,
updateUrl: values.updateUrl,
md5Url: values.md5Url,
name: values.name,
enabled: values.enabled == "true",
originalMd5: values.originalMd5,
sections: []
};
styles.push(currentStyle); styles.push(currentStyle);
} }
if (values.section_id != null) { if (values.section_id !== null) {
if (currentSection == null || currentSection.id != values.section_id) { if (currentSection === null || currentSection.id != values.section_id) {
currentSection = {id: values.section_id, code: values.code}; currentSection = {
id: values.section_id,
code: values.code
};
currentStyle.sections.push(currentSection); currentStyle.sections.push(currentSection);
} }
if (metaName && metaValue) { if (metaName && metaValue) {
@ -100,7 +114,7 @@ var webSqlStorage = {
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;
@ -125,7 +139,9 @@ var webSqlStorage = {
t.executeSql('CREATE TABLE styles (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, url TEXT, updateUrl TEXT, md5Url TEXT, name TEXT NOT NULL, code TEXT NOT NULL, enabled INTEGER NOT NULL, originalCode TEXT NULL);'); t.executeSql('CREATE TABLE 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) {
@ -144,7 +160,9 @@ var webSqlStorage = {
t.executeSql('INSERT INTO newstyles (id, url, updateUrl, md5Url, name, enabled) SELECT id, url, updateUrl, md5Url, name, enabled FROM styles;'); t.executeSql('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) {
@ -152,18 +170,24 @@ var webSqlStorage = {
// clear out orphans // clear out orphans
t.executeSql('DELETE FROM section_meta WHERE section_id IN (SELECT sections.id FROM sections LEFT JOIN styles ON styles.id = sections.style_id WHERE styles.id IS NULL);'); t.executeSql('DELETE FROM section_meta WHERE section_id IN (SELECT sections.id FROM sections LEFT JOIN styles ON styles.id = sections.style_id WHERE styles.id IS NULL);');
t.executeSql('DELETE FROM sections WHERE id IN (SELECT sections.id FROM sections LEFT JOIN styles ON styles.id = sections.style_id WHERE styles.id IS NULL);'); t.executeSql('DELETE FROM sections WHERE id IN (SELECT sections.id FROM sections LEFT JOIN styles ON styles.id = sections.style_id WHERE styles.id IS NULL);');
}, error, function() { webSqlStorage.dbV14(d, error, done)}); }, error, function () {
webSqlStorage.dbV14(d, error, done);
});
}, },
dbV14: function (d, error, done) { dbV14: function (d, error, done) {
d.changeVersion(d.version, '1.4', function (t) { d.changeVersion(d.version, '1.4', function (t) {
t.executeSql('UPDATE styles SET url = null WHERE url = "undefined";'); t.executeSql('UPDATE styles SET url = null WHERE url = "undefined";');
}, error, function() { webSqlStorage.dbV15(d, error, done)}); }, error, function () {
webSqlStorage.dbV15(d, error, done);
});
}, },
dbV15: function (d, error, done) { dbV15: function (d, error, done) {
d.changeVersion(d.version, '1.5', function (t) { d.changeVersion(d.version, '1.5', function (t) {
t.executeSql('ALTER TABLE styles ADD COLUMN originalMd5 TEXT NULL;'); t.executeSql('ALTER TABLE styles ADD COLUMN originalMd5 TEXT NULL;');
}, error, function() { done(d); }); }, error, function () {
} done(d);
});
} }
};

View File

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