From 9270dc15966227c8b577ba0d2a0e7679822e42ee Mon Sep 17 00:00:00 2001 From: Jason Barnabe Date: Sun, 19 Aug 2012 20:14:33 -0500 Subject: [PATCH] issue 44 Stop using deprecated Chrome APIs --- apply.js | 72 +++--------- background.js | 285 +++++++++++++++++++++++++++++++++++++++------- edit.html | 21 ++-- edit.js | 138 ++++++++++++----------- install.js | 5 +- manage.html | 12 +- manage.js | 30 ++--- manifest.json | 13 ++- messaging.js | 11 +- popup.html | 160 ++++++++------------------ popup.js | 77 +++++++++++++ storage.js | 307 ++------------------------------------------------ 12 files changed, 522 insertions(+), 609 deletions(-) create mode 100644 popup.js diff --git a/apply.js b/apply.js index 50cd8480..3924028d 100644 --- a/apply.js +++ b/apply.js @@ -1,25 +1,19 @@ -chrome.extension.sendRequest({name:"getStylesToApply"}, function(response) { +chrome.extension.sendMessage({method: "getStyles", matchUrl: location.href, enabled: true, updateBadge: window == window.top}, function(response) { response.forEach(applyStyle); }); -chrome.extension.onRequest.addListener(function(request, sender, sendResponse) { +chrome.extension.onMessage.addListener(function(request, sender, sendResponse) { switch(request.name) { case "styleDeleted": removeStyle(request.id); - sendResponse({}); break; case "styleUpdated": removeStyle(request.style.id); //fallthrough case "styleAdded": if (request.style.enabled == "true") { - chrome.extension.sendRequest({name: "getStyleApplies", style: request.style, url: location.href}, function(response) { - if (response) { - applyStyle(response); - } - }); + applyStyle(request.style); } - sendResponse({}); } }); @@ -31,54 +25,24 @@ function removeStyle(id) { } function applyStyle(s) { - var style = document.createElement("style"); - style.setAttribute("id", "stylish-" + s.id); - style.setAttribute("class", "stylish"); - style.setAttribute("type", "text/css"); - style.appendChild(document.createTextNode(s.sections.filter(filterSection).map(function(section) { - return section.code; - }).join("\n"))); - if (document.head) { - document.head.appendChild(style); - } else { - document.documentElement.appendChild(style); - } -} - -function filterSection(section) { - // global - if (!section.urls && !section.urlPrefixes && !section.domains && !section.regexps) { - return true; - } - if (section.urls && section.urls.some(function(url) { - return url == location.href; - })) { - return true; - } - if (section.urlPrefixes && section.urlPrefixes.some(function(urlPrefix) { - return location.href.indexOf(urlPrefix) == 0; - })) { - return true; - } - if (section.domains) { - var currentDomains = getDomains(location.href); - if (section.domains.some(function(domain) { - return currentDomains.indexOf(domain) >= 0; - })) { - return true; + chrome.extension.sendMessage({method: "getStyleApplies", style: s, url: location.href}, function(response) { + if (response && response.length > 0) { + applySections(s, response); } - } - return section.regexps && section.regexps.some(function(regexp) { - return (new RegExp(regexp)).test(location.href); }); } -function getDomains(url) { - var d = /.*?:\/*([^\/]+)/.exec(url)[1]; - var domains = [d]; - while (d.indexOf(".") != -1) { - d = d.substring(d.indexOf(".") + 1); - domains.push(d); +function applySections(style, sections) { + var styleElement = document.createElement("style"); + styleElement.setAttribute("id", "stylish-" + style.id); + styleElement.setAttribute("class", "stylish"); + styleElement.setAttribute("type", "text/css"); + styleElement.appendChild(document.createTextNode(sections.map(function(section) { + return section.code; + }).join("\n"))); + if (document.head) { + document.head.appendChild(styleElement); + } else { + document.documentElement.appendChild(styleElement); } - return domains; } diff --git a/background.js b/background.js index a8cd05d0..1c257e73 100644 --- a/background.js +++ b/background.js @@ -1,78 +1,283 @@ -chrome.extension.onRequest.addListener(function(request, sender, sendResponse) { - switch (request.name) { - case "getStylesToApply": - getStyles({matchUrl: sender.tab.url, enabled: true}, function(r) { +chrome.extension.onMessage.addListener(function(request, sender, sendResponse) { + switch (request.method) { + case "getStyles": + getStyles(request, function(r) { sendResponse(r); - chrome.browserAction.setBadgeText({text: getBadgeText(r), tabId: sender.tab.id}); + if (request.updateBadge) { + var t = getBadgeText(r); + console.log("Tab " + sender.tab.id + " (" + sender.tab.url + ") badge text set to '" + t + "'."); + chrome.browserAction.setBadgeText({text: t, tabId: sender.tab.id}); + } else { + console.log("Tab " + sender.tab.id + " (" + sender.tab.url + ") doesn't get badge text."); + } }); - break; - case "getStylesForUrl": - getStyles({url: request.url}, sendResponse); - break; + return true; case "getStyleApplies": - sendResponse(styleAppliesToUrl(request.style, request.url)); - break; - case "saveFromJSON": - saveFromJSON(request.json); - sendResponse({}); - break; + sendResponse(getApplicableSections(request.style, request.url)); + return true; + case "saveStyle": + saveStyle(request, sendResponse); + return true; case "styleChanged": - cachedGlobalStyleIds = null; - cachedStyles = []; - sendResponse({}); - break; - case "getCachedStyles": - sendResponse(cachedStyles); - break; - case "cacheStyles": - request.styles.forEach(function(style) { - cachedStyles[style.id] = style; - }); + cachedStyles = null; break; } }); -function styleAppliesToUrl(style, url) { - style.sections = style.sections.filter(function(section) { +function getStyles(options, callback) { + + var enabled = fixBoolean(options.enabled); + var url = "url" in options ? options.url : null; + var id = "id" in options ? options.id : null; + var matchUrl = "matchUrl" in options ? options.matchUrl : null; + + var callCallback = function() { + callback(cachedStyles.filter(function(style) { + if (enabled != null && fixBoolean(style.enabled) != enabled) { + return false; + } + if (url != null && style.url != url) { + return false; + } + if (id != null && style.id != id) { + return false; + } + if (matchUrl != null && getApplicableSections(style, matchUrl) == 0) { + return false; + } + return true; + })); + } + + if (cachedStyles) { + callCallback(); + return; + } + + getDatabase(function(db) { + db.readTransaction(function (t) { + var where = ""; + 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) { + cachedStyles = []; + var currentStyle = null; + var currentSection = null; + for (var i = 0; i < r.rows.length; i++) { + var values = r.rows.item(i); + var metaName = null; + switch (values.metaName) { + case null: + break; + case "url": + metaName = "urls"; + break; + case "url-prefix": + metaName = "urlPrefixes"; + break; + case "domain": + var metaName = "domains"; + break; + case "regexps": + var metaName = "regexps"; + break; + default: + var metaName = values.metaName + "s"; + } + var metaValue = values.metaValue; + if (currentStyle == null || currentStyle.id != values.id) { + currentStyle = {id: values.id, url: values.url, updateUrl: values.updateUrl, md5Url: values.md5Url, name: values.name, enabled: values.enabled, sections: []}; + cachedStyles.push(currentStyle); + } + if (currentSection == null || currentSection.id != values.section_id) { + currentSection = {id: values.section_id, code: values.code}; + currentStyle.sections.push(currentSection); + } + if (metaName && metaValue) { + if (currentSection[metaName]) { + currentSection[metaName].push(metaValue); + } else { + currentSection[metaName] = [metaValue]; + } + } + } + callCallback(); + }, reportError); + }, reportError); + }, reportError); +} + +function fixBoolean(b) { + if (typeof b != "undefined") { + return b != "false"; + } + return null; +} + +const namespacePattern = /^\s*@namespace\s+([a-zA-Z]+\s+)?url\(\"?http:\/\/www.w3.org\/1999\/xhtml\"?\);?\s*$/; +function getApplicableSections(style, url) { + var sections = style.sections.filter(function(section) { return sectionAppliesToUrl(section, url); }); - if (style.sections.size == 0) { - return null; + // ignore if it's just a namespace + if (sections.length == 1 && namespacePattern.test(sections[0].code)) { + return []; } - return style; + return sections; } function sectionAppliesToUrl(section, url) { + // only http and https allowed + if (url.indexOf("http") != 0) { + return false; + } if (!section.urls && !section.domains && !section.urlPrefixes && !section.regexps) { - console.log(section + " is global"); + console.log(section.id + " is global"); return true; } if (section.urls && section.urls.indexOf(url) != -1) { - console.log(section + " applies to " + url + " due to URL rules"); + console.log(section.id + " applies to " + url + " due to URL rules"); return true; } if (section.urlPrefixes && section.urlPrefixes.some(function(prefix) { return url.indexOf(prefix) == 0; })) { - console.log(section + " applies to " + url + " due to URL prefix rules"); + console.log(section.id + " applies to " + url + " due to URL prefix rules"); return true; } if (section.domains && getDomains(url).some(function(domain) { return section.domains.indexOf(domain) != -1; })) { - console.log(section + " applies due to " + url + " due to domain rules"); + console.log(section.id + " applies due to " + url + " due to domain rules"); return true; } if (section.regexps && section.regexps.some(function(regexp) { + // we want to match the full url, so add ^ and $ if not already present + if (regexp[0] != "^") { + regexp = "^" + regexp; + } + if (regexp[regexp.length - 1] != "$") { + regexp += "$"; + } return (new RegExp(regexp)).test(url); })) { - console.log(section + " applies to " + url + " due to regexp rules"); + console.log(section.id + " applies to " + url + " due to regexp rules"); return true; } - console.log(section + " does not apply due to " + url); + console.log(section.id + " does not apply due to " + url); return false; } -var cachedGlobalStyleIds = null; -var cachedStyles = []; -var background = true; +var cachedStyles = null; + +function saveStyle(o, callback) { + getDatabase(function(db) { + db.transaction(function(t) { + if (o.id) { + // update whatever's been passed + if ("name" in o) { + t.executeSql('UPDATE styles SET name = ? WHERE id = ?;', [o.name, o.id]); + } + if ("enabled" in o) { + t.executeSql('UPDATE styles SET enabled = ? WHERE id = ?;', [o.enabled, o.id]); + } + if ("url" in o) { + t.executeSql('UPDATE styles SET url = ? WHERE id = ?;', [o.url, o.id]); + } + if ("updateUrl" in o) { + t.executeSql('UPDATE styles SET updateUrl = ? WHERE id = ?;', [o.updateUrl, o.id]); + } + if ("md5Url" in o) { + t.executeSql('UPDATE styles SET md5Url = ? WHERE id = ?;', [o.md5Url, o.id]); + } + } else { + // create a new record + if (!("updateUrl" in o)) { + o.updateUrl = null; + } + if (!("md5Url" in o)) { + o.md5Url = null; + } + t.executeSql('INSERT INTO styles (name, enabled, url, updateUrl, md5Url) VALUES (?, ?, ?, ?, ?);', [o.name, true, o.url, o.updateUrl, o.md5Url]); + } + + if ("sections" in o) { + if (o.id) { + // clear existing records + t.executeSql('DELETE FROM section_meta WHERE section_id IN (SELECT id FROM sections WHERE style_id = ?);', [o.id]); + t.executeSql('DELETE FROM sections WHERE style_id = ?;', [o.id]); + } + + o.sections.forEach(function(section) { + if (o.id) { + t.executeSql('INSERT INTO sections (style_id, code) VALUES (?, ?);', [o.id, section.code]); + } else { + t.executeSql('INSERT INTO sections (style_id, code) SELECT id, ? FROM styles ORDER BY id DESC LIMIT 1;', [section.code]); + } + if (section.urls) { + section.urls.forEach(function(u) { + t.executeSql("INSERT INTO section_meta (section_id, name, value) SELECT id, 'url', ? FROM sections ORDER BY id DESC LIMIT 1;", [u]); + }); + } + if (section.urlPrefixes) { + section.urlPrefixes.forEach(function(u) { + t.executeSql("INSERT INTO section_meta (section_id, name, value) SELECT id, 'url-prefix', ? FROM sections ORDER BY id DESC LIMIT 1;", [u]); + }); + } + if (section.domains) { + section.domains.forEach(function(u) { + t.executeSql("INSERT INTO section_meta (section_id, name, value) SELECT id, 'domain', ? FROM sections ORDER BY id DESC LIMIT 1;", [u]); + }); + } + if (section.regexps) { + section.regexps.forEach(function(u) { + t.executeSql("INSERT INTO section_meta (section_id, name, value) SELECT id, 'regexp', ? FROM sections ORDER BY id DESC LIMIT 1;", [u]); + }); + } + }); + } + }, reportError, function() {saveFromJSONComplete(o.id, callback)}); + }, reportError); +} + +function saveFromJSONComplete(id, callback) { + cachedStyles = null; + + if (id) { + getStyles({method: "getStyles", id: id}, function(styles) { + saveFromJSONStyleReloaded("styleUpdated", styles[0], callback); + }); + return; + } + + // we need to load the id for new ones + getDatabase(function(db) { + db.readTransaction(function (t) { + t.executeSql('SELECT id FROM styles ORDER BY id DESC LIMIT 1', [], function(t, r) { + var id = r.rows.item(0).id; + getStyles({method: "getStyles", id: id}, function(styles) { + saveFromJSONStyleReloaded("styleAdded", styles[0], callback); + }); + }, reportError) + }, reportError) + }); + +} + +function saveFromJSONStyleReloaded(updateType, style, callback) { + notifyAllTabs({name:updateType, style: style}); + if (callback) { + callback(style); + } +} + +function getDomains(url) { + var d = /.*?:\/*([^\/]+)/.exec(url)[1]; + var domains = [d]; + while (d.indexOf(".") != -1) { + d = d.substring(d.indexOf(".") + 1); + domains.push(d); + } + return domains; +} + diff --git a/edit.html b/edit.html index 496fe8c8..86864055 100644 --- a/edit.html +++ b/edit.html @@ -35,11 +35,6 @@ #sections > div:not(:first-child) { border-top: 2px solid black; } - span { - cursor: pointer; - color: #999; - } - label { display: inline-block; width: 10em; @@ -86,23 +81,21 @@ } - - -
-

+

+ diff --git a/edit.js b/edit.js index 3b01ad30..64377fc4 100644 --- a/edit.js +++ b/edit.js @@ -2,14 +2,14 @@ var styleId = null; var dirty = false; var appliesToTemplate = document.createElement("li"); -appliesToTemplate.innerHTML = ''; +appliesToTemplate.innerHTML = ''; var appliesToEverythingTemplate = document.createElement("li"); appliesToEverythingTemplate.className = "applies-to-everything"; -appliesToEverythingTemplate.innerHTML = t("appliesToEverything") + ' ' +appliesToEverythingTemplate.innerHTML = t("appliesToEverything") + ' ' var sectionTemplate = document.createElement("div"); -sectionTemplate.innerHTML = '
'; +sectionTemplate.innerHTML = '
'; function makeDirty() { dirty = true; @@ -30,23 +30,36 @@ function addAppliesTo(list, name, value) { e = appliesToTemplate.cloneNode(true); e.querySelector("[name=applies-type]").value = name; e.querySelector("[name=applies-value]").value = value; + e.querySelector(".remove-applies-to").addEventListener("click", removeAppliesTo, false); + e.querySelector(".applies-value").addEventListener("change", makeDirty, false); + e.querySelector(".applies-type").addEventListener("change", makeDirty, false); } else if (showingEverything || list.hasChildNodes()) { e = appliesToTemplate.cloneNode(true); if (list.hasChildNodes()) { e.querySelector("[name=applies-type]").value = list.querySelector("li:last-child [name='applies-type']").value; } + e.querySelector(".remove-applies-to").addEventListener("click", removeAppliesTo, false); + e.querySelector(".applies-value").addEventListener("change", makeDirty, false); + e.querySelector(".applies-type").addEventListener("change", makeDirty, false); } else { e = appliesToEverythingTemplate.cloneNode(true); } + e.querySelector(".add-applies-to").addEventListener("click", function() {addAppliesTo(this.parentNode.parentNode)}, false); list.appendChild(e); } function addSection(section) { var div = sectionTemplate.cloneNode(true); + div.querySelector(".applies-to-help").addEventListener("click", showAppliesToHelp, false); + div.querySelector(".remove-section").addEventListener("click", removeSection, false); + div.querySelector(".add-section").addEventListener("click", function() {addSection()}, false); + var appliesTo = div.querySelector(".applies-to-list"); if (section) { - div.querySelector(".code").value = section.code; + var codeElement = div.querySelector(".code"); + codeElement.value = section.code; + codeElement.addEventListener("change", makeDirty, false); if (section.urls) { section.urls.forEach(function(url) { addAppliesTo(appliesTo, "url", url); @@ -82,7 +95,9 @@ function removeAppliesTo(event) { var appliesToList = event.target.parentNode.parentNode; appliesToList.removeChild(event.target.parentNode); if (!appliesToList.hasChildNodes()) { - appliesToList.appendChild(appliesToEverythingTemplate); + var e = appliesToEverythingTemplate.cloneNode(true); + e.querySelector(".add-applies-to").addEventListener("click", function() {addAppliesTo(this.parentNode.parentNode)}, false); + appliesToList.appendChild(e); } makeDirty(); } @@ -106,7 +121,7 @@ function init() { } // This is an edit var id = idMatch[1]; - getStyles({id: id}, function(styles) { + chrome.extension.sendMessage({method: "getStyles", id: id}, function(styles) { var style = styles[0]; styleId = style.id; initWithStyle(style); @@ -117,7 +132,7 @@ function initWithStyle(style) { document.getElementById("name").value = style.name; document.getElementById("enabled").checked = style.enabled == "true"; document.getElementById("heading").innerHTML = t("editStyleHeading"); - initTitle(style); + initTitle(style.name); // if this was done in response to an update, we need to clear existing sections Array.prototype.forEach.call(document.querySelectorAll("#sections > div"), function(div) { div.parentNode.removeChild(div); @@ -125,8 +140,8 @@ function initWithStyle(style) { style.sections.forEach(addSection); } -function initTitle(style) { - document.title = t('editStyleTitle', [style.name]); +function initTitle(name) { + document.title = t('editStyleTitle', [name]); } function validate() { @@ -145,31 +160,14 @@ function save() { } var name = document.getElementById("name").value; var enabled = document.getElementById("enabled").checked; - getDatabase(function(db) { - db.transaction(function (t) { - var sections = getSections(); - if (styleId == null) { - t.executeSql('INSERT INTO styles (name, enabled) VALUES (?, ?);', [name, enabled]); - sections.forEach(function(s) { - t.executeSql("INSERT INTO sections (style_id, code) SELECT id, ? FROM styles ORDER BY id DESC LIMIT 1;", [s.code]); - s.meta.forEach(function(m) { - t.executeSql("INSERT INTO section_meta (section_id, name, value) SELECT id, ?, ? FROM sections ORDER BY id DESC LIMIT 1;", [m[0], m[1]]); - }); - }); - } else { - t.executeSql('UPDATE styles SET name = ?, enabled = ? WHERE id = ?;', [name, enabled, styleId]); - t.executeSql('DELETE FROM section_meta WHERE section_id IN (SELECT id FROM sections WHERE style_id = ?);', [styleId]); - t.executeSql('DELETE FROM sections WHERE style_id = ?;', [styleId]); - sections.forEach(function(s) { - t.executeSql("INSERT INTO sections (style_id, code) VALUES (?, ?);", [styleId, s.code]); - s.meta.forEach(function(m) { - t.executeSql("INSERT INTO section_meta (section_id, name, value) SELECT id, ?, ? FROM sections ORDER BY id DESC LIMIT 1;", [m[0], m[1]]); - }); - }); - } - dirty = false; - }, reportError, saveComplete); - }, reportError); + var request = { + method: "saveStyle", + id: styleId, + name: name, + enabled: enabled, + sections: getSections(), + }; + chrome.extension.sendMessage(request, saveComplete); } function getSections() { @@ -179,13 +177,15 @@ function getSections() { if (/^\s*$/.test(code)) { return; } - sections.push({code: code, meta: getMeta(div)}); + var meta = getMeta(div); + meta.code = code; + sections.push(meta); }); return sections; } function getMeta(e) { - var meta = []; + var meta = {urls: [], urlPrefixes: [], domains: [], regexps: []}; Array.prototype.forEach.call(e.querySelector(".applies-to-list").childNodes, function(li) { if (li.className == appliesToEverythingTemplate.className) { return; @@ -193,26 +193,33 @@ function getMeta(e) { var a = li.querySelector("[name=applies-type]").value; var b = li.querySelector("[name=applies-value]").value; if (a && b) { - meta.push([a, b]); + switch (a) { + case "url": + meta.urls.push(b); + break; + case "url-prefix": + meta.urlPrefixes.push(b); + break; + case "domain": + meta.domains.push(b); + break; + case "regexp": + meta.regexps.push(b); + break; + } } }); return meta; } -function saveComplete() { - if (styleId == null) { - // Load the style id - getDatabase(function(db) { - db.readTransaction(function (t) { - t.executeSql('SELECT id FROM styles ORDER BY id DESC LIMIT 1', [], function(t, r) { - styleId = r.rows.item(0).id; - notifySave(true); - }, reportError) - }, reportError) - }); - return; +function saveComplete(id) { + // Go from new style URL to edit style URL + if (location.href.indexOf("id=") == -1) { + // give the code above a moment before we kill the page + setTimeout(function() {location.href = "edit.html?id=" + id;}, 200); + } else { + initTitle(document.getElementById("name").value); } - notifySave(false); } function showMozillaFormat() { @@ -233,20 +240,6 @@ function toMozillaFormat() { }).join("\n\n"); } -function notifySave(newStyle) { - chrome.extension.sendRequest({name: "styleChanged"}); - getStyles({id: styleId}, function(styles) { - if (newStyle) { - notifyAllTabs({name:"styleAdded", style: styles[0]}); - // give the code above a moment before we kill the page - setTimeout(function() {location.href = "edit.html?id=" + styleId;}, 200); - } else { - initTitle(styles[0]); - notifyAllTabs({name:"styleUpdated", style: styles[0]}); - } - }); -} - function showSectionHelp() { showHelp(t("sectionHelp")); } @@ -263,11 +256,12 @@ function showHelp(text) { alert(text); } -chrome.extension.onRequest.addListener(function(request, sender, sendResponse) { +chrome.extension.onMessage.addListener(function(request, sender, sendResponse) { var installed = document.getElementById("installed"); switch(request.name) { case "styleUpdated": initWithStyle(request.style); + dirty = false; break; case "styleDeleted": if (styleId == request.id) { @@ -276,3 +270,17 @@ chrome.extension.onRequest.addListener(function(request, sender, sendResponse) { } } }); + +tE("name-label", "styleNameLabel"); +tE("enabled-label", "styleEnabledLabel"); +tE("to-mozilla", "styleToMozillaFormat"); +tE("save-button", "styleSaveLabel"); +tE("cancel-button", "styleCancelEditLabel"); +tE("sections-heading", "styleSectionsTitle"); +document.getElementById("name").addEventListener("change", makeDirty, false); +document.getElementById("enabled").addEventListener("change", makeDirty, false); +document.getElementById("to-mozilla").addEventListener("click", showMozillaFormat, false); +document.getElementById("to-mozilla-help").addEventListener("click", showToMozillaHelp, false); +document.getElementById("save-button").addEventListener("click", save, false); +document.getElementById("sections-help").addEventListener("click", showSectionHelp, false); + diff --git a/install.js b/install.js index 059433b2..c435d214 100644 --- a/install.js +++ b/install.js @@ -1,4 +1,4 @@ -chrome.extension.sendRequest({name:"getStylesForUrl", url: getMeta("stylish-id-url") || location.href}, function(response) { +chrome.extension.sendMessage({method: "getStyles", url: getMeta("stylish-id-url") || location.href}, function(response) { if (response.length == 0) { sendEvent("styleCanBeInstalledChrome"); } else { @@ -18,7 +18,8 @@ document.addEventListener("stylishInstallChrome", function() { getResource(getMeta("stylish-code-chrome"), function(code) { // check for old style json var json = JSON.parse(code); - chrome.extension.sendRequest({name:"saveFromJSON", json: json}, function(response) { + json.method = "saveStyle"; + chrome.extension.sendMessage(json, function(response) { sendEvent("styleInstalledChrome"); }); }); diff --git a/manage.html b/manage.html index d4c8514a..c673d76f 100644 --- a/manage.html +++ b/manage.html @@ -87,16 +87,18 @@ - +
+ + diff --git a/manage.js b/manage.js index 8ec02c83..3d7c2d08 100644 --- a/manage.js +++ b/manage.js @@ -1,12 +1,11 @@ - var styleTemplate = document.createElement("div"); -styleTemplate.innerHTML = "

"; +styleTemplate.innerHTML = "

"; var appliesToExtraTemplate = document.createElement("span"); appliesToExtraTemplate.className = "applies-to-extra"; -appliesToExtraTemplate.innerHTML = t('appliesDisplayTruncatedSuffix'); +appliesToExtraTemplate.innerHTML = " " + t('appliesDisplayTruncatedSuffix'); -getStyles({}, showStyles); +chrome.extension.sendMessage({method: "getStyles"}, showStyles); function showStyles(styles) { var installed = document.getElementById("installed"); @@ -80,6 +79,11 @@ function createStyleElement(style) { } var editLink = e.querySelector(".style-edit-link"); editLink.setAttribute("href", editLink.getAttribute("href") + style.id); + e.querySelector(".enable").addEventListener("click", function(event) { enable(event, true); }, false); + e.querySelector(".disable").addEventListener("click", function(event) { enable(event, false); }, false); + e.querySelector(".check-update").addEventListener("click", doCheckUpdate, false); + e.querySelector(".update").addEventListener("click", doUpdate, false); + e.querySelector(".delete").addEventListener("click", doDelete, false); return e; } @@ -111,19 +115,16 @@ function getStyleElement(event) { return null; } -chrome.extension.onRequest.addListener(function(request, sender, sendResponse) { +chrome.extension.onMessage.addListener(function(request, sender, sendResponse) { switch(request.name) { case "styleUpdated": handleUpdate(request.style); - sendResponse({}); break; case "styleAdded": installed.appendChild(createStyleElement(request.style)); - sendResponse({}); break; case "styleDeleted": handleDelete(request.id); - sendResponse({}); break; } }); @@ -168,7 +169,7 @@ function checkUpdate(element) { } function checkNeedsUpdate(id, serverJson) { - getStyles({id: id}, function(styles) { + chrome.extension.sendMessage({method: "getStyles", id: id}, function(styles) { var style = styles[0]; if (codeIsEqual(style.sections, serverJson.sections)) { handleNeedsUpdate("no", id, serverJson); @@ -199,10 +200,7 @@ function handleNeedsUpdate(needsUpdate, id, serverJson) { function doUpdate(event) { var element = getStyleElement(event); - var o = {}; - o.id = element.getAttribute('style-id'); - o.sections = element.updatedCode.sections; - saveFromJSON(o, function() { + chrome.extension.sendMessage({method: "saveStyle", id: element.getAttribute('style-id'), sections: element.updatedCode.sections}, function() { element.updatedCode = ""; element.className = element.className.replace("can-update", "update-done"); element.querySelector(".update-note").innerHTML = t('updateCompleted'); @@ -279,3 +277,9 @@ function getType(o) { } document.title = t("manageTitle"); +tE("manage-heading", "manageHeading"); +tE("manage-text", "manageText", null, false); +tE("check-all-updates", "checkAllUpdates"); +tE("add-style-label", "addStyleLabel"); + +document.getElementById("check-all-updates").addEventListener("click", checkUpdateAll, false); diff --git a/manifest.json b/manifest.json index ac6ee64a..61267c91 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,9 @@ { "name": "Stylish", - "version": "0.10", + "version": "1.0", "description": "__MSG_description__", + "homepage_url": "http://userstyles.org", + "manifest_version": 2, "icons": { "16": "16.png", "48": "48.png", @@ -9,10 +11,11 @@ }, "permissions": [ "tabs", - "http://userstyles.org/", - "http://userstyles.local/" + "http://userstyles.org/" ], - "background_page": "background.html", + "background": { + "page": "background.html" + }, "content_scripts": [ { "matches": ["http://*/*", "https://*/*"], @@ -21,7 +24,7 @@ "js": ["apply.js"] }, { - "matches": ["http://userstyles.org/*", "http://userstyles.local/*"], + "matches": ["http://userstyles.org/*"], "run_at": "document_end", "all_frames": false, "js": ["install.js"] diff --git a/messaging.js b/messaging.js index 1fb4206f..8df9f5f7 100644 --- a/messaging.js +++ b/messaging.js @@ -2,7 +2,7 @@ function notifyAllTabs(request) { chrome.windows.getAll({populate: true}, function(windows) { windows.forEach(function(win) { win.tabs.forEach(function(tab) { - chrome.tabs.sendRequest(tab.id, request); + chrome.tabs.sendMessage(tab.id, request); updateBadgeText(tab); }); }); @@ -10,12 +10,13 @@ function notifyAllTabs(request) { } function updateBadgeText(tab) { - getStyles({matchUrl: tab.url}, function(styles) { - chrome.browserAction.setBadgeText({text: getBadgeText(styles), tabId: tab.id}); + chrome.extension.sendMessage({method: "getStyles", matchUrl: tab.url, enabled: true}, function(styles) { + var t = getBadgeText(styles); + console.log("Tab " + tab.id + " (" + tab.url + ") badge text set to '" + t + "'."); + chrome.browserAction.setBadgeText({text: t, tabId: tab.id}); }); } function getBadgeText(styles) { - var e = styles.filter(function(style) { return style.enabled == "true"; }); - return e.length == 0 ? "" : ("" + e.length); + return styles.length == 0 ? "" : ("" + styles.length); } diff --git a/popup.html b/popup.html index fd9c97dc..2f9a1eaf 100644 --- a/popup.html +++ b/popup.html @@ -1,117 +1,55 @@ - - - - - - - + + + + - function handleUpdate(style) { - var installed = document.getElementById("installed"); - installed.replaceChild(createStyleElement(style), installed.querySelector("[style-id='" + style.id + "']")); - } +
- function handleDelete(id) { - var installed = document.getElementById("installed"); - installed.removeChild(installed.querySelector("[style-id='" + id + "']")); - } - +
+
-
+ -
-
+ + diff --git a/popup.js b/popup.js new file mode 100644 index 00000000..980106ed --- /dev/null +++ b/popup.js @@ -0,0 +1,77 @@ +var styleTemplate = document.createElement("div"); +styleTemplate.innerHTML = "
" + t('editStyleLabel') + " " + t('enableStyleLabel') + " " + t('disableStyleLabel') + " " + t('deleteStyleLabel') + "
"; + +chrome.tabs.getSelected(null, function(tab) { + chrome.extension.sendMessage({method: "getStyles", matchUrl: tab.url}, showStyles); + document.querySelector("#find-styles a").href = "http://userstyles.org/styles/browse/all/" + encodeURIComponent(tab.url); +}); + +function showStyles(styles) { + var installed = document.getElementById("installed"); + if (styles.length == 0) { + installed.innerHTML = "
" + t('noStylesForSite') + "
"; + } + styles.map(createStyleElement).forEach(function(e) { + installed.appendChild(e); + }); +} + +function createStyleElement(style) { + var e = styleTemplate.cloneNode(true); + e.setAttribute("class", "entry " + (style.enabled == "true" ? "enabled" : "disabled")); + e.setAttribute("style-id", style.id); + var styleName = e.querySelector(".style-name"); + styleName.appendChild(document.createTextNode(style.name)); + var editLink = e.querySelector(".style-edit-link"); + editLink.setAttribute("href", editLink.getAttribute("href") + style.id); + editLink.addEventListener("click", openLink, 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); + return e; +} + +function enable(event, enabled) { + var id = getId(event); + enableStyle(id, enabled); +} + +function doDelete() { + if (!confirm(t('deleteStyleConfirm'))) { + return; + } + var id = getId(event); + deleteStyle(id); +} + +function getId(event) { + var e = event.target; + while (e) { + if (e.hasAttribute("style-id")) { + return e.getAttribute("style-id"); + } + e = e.parentNode; + } + return null; +} + +function openLink(event) { + chrome.tabs.create({url: event.target.href}); + return false; +} + +function handleUpdate(style) { + var installed = document.getElementById("installed"); + installed.replaceChild(createStyleElement(style), installed.querySelector("[style-id='" + style.id + "']")); +} + +function handleDelete(id) { + var installed = document.getElementById("installed"); + installed.removeChild(installed.querySelector("[style-id='" + id + "']")); +} + +tE("open-manage-link", "openManage"); +tE("find-styles-link", "findStylesForSite"); + +document.getElementById("find-styles-link").addEventListener("click", openLink, false); + diff --git a/storage.js b/storage.js index 5389cd36..3ee901bc 100644 --- a/storage.js +++ b/storage.js @@ -1,5 +1,3 @@ -var namespacePattern = /^\s*@namespace\s+([a-zA-Z]+\s+)?url\(\"?http:\/\/www.w3.org\/1999\/xhtml\"?\);?\s*$/; - var stylishDb = null; function getDatabase(ready, error) { if (stylishDb != null) { @@ -53,303 +51,13 @@ function dbV13(d, error, done) { }, error, function() { done(d)}); } -function getStyles(options, callback) { - getDatabase(function(db) { - db.readTransaction(function (t) { - if ("matchUrl" in options) { - // get a list of style ids that apply to the url. we need to do this separately because we need all the metas for the styles, not just the matching ones - // find site-specific ones - var sql = "SELECT DISTINCT s.style_id FROM sections s INNER JOIN section_meta sm ON sm.section_id = s.id WHERE (sm.name = 'url' and sm.value = ?) OR (sm.name = 'url-prefix' AND ? LIKE (sm.value || '%')) OR (sm.name = 'regexp' AND ? REGEXP sm.value)"; - var matchParams = []; - var domains = getDomains(options.matchUrl); - matchParams = matchParams.concat([options.matchUrl, options.matchUrl, options.matchUrl]).concat(domains); - var domainClause = ""; - if (domains.length == 1) { - sql += " OR (sm.name = 'domain' AND sm.value = ?)"; - } else if (domains.length > 1) { - sql += " OR (sm.name = 'domain' AND sm.value IN ("; - sql += domains.map(function(d) { return "?";}).join(","); - sql += '))'; - } - t.executeSql(sql, matchParams, function (t, r) { - var style_ids = []; - if (options.id) { - style_ids.push(options.id); - } - for (var i = 0; i < r.rows.length; i++) { - var values = r.rows.item(i); - style_ids.push(values.style_id); - } - // now add in global ones - getGlobalStyleIds(function(ids) { - style_ids = uniqueArray(style_ids.concat(ids)); - loadStyles(style_ids, options.enabled, options.url, callback); - }); - }); - } else { - loadStyles(options.id ? [options.id] : null, options.enabled, options.url, callback); - } - }, reportError); - }, reportError); -} - -function uniqueArray(ar) { - return ar.filter(function(s, i, a){ - return i === a.lastIndexOf(s); - }); -} - -function getCache(callback) { - if (isBackground()) { - callback(cachedStyles); - return; - } - chrome.extension.sendRequest({name: "getCachedStyles"}, callback); -} - -function fixBoolean(b) { - if (typeof b != "undefined") { - return b != "false"; - } - return null; -} - -function loadStyles(styleIds, enabled, url, callback) { - // clean up the parameters - enabled = fixBoolean(enabled); - if (typeof url == "undefined") { - url = null; - } - // grab what we can from the cache - if (styleIds) { - getCache(function(cache) { - var styles = []; - var styleIdsNeeded = []; - styleIds.forEach(function(id) { - if (cache[id]) { - if (checkStyle(cache[id], enabled, url)) { - styles.push(cache[id]); - } - } else { - styleIdsNeeded.push(id); - } - }); - styleIds = styleIdsNeeded; - // do we have everything we need? - if (styleIds.length == 0) { - callback(styles); - return; - } - loadStylesFromDB(styles, styleIds, enabled, url, callback); - }); - return; - } - loadStylesFromDB([], styleIds, enabled, url, callback); -} - -function checkStyle(style, enabled, url) { - return (enabled == null || enabled == fixBoolean(style.enabled)) && (url == null || url == style.url); -} - -function loadStylesFromDB(styles, styleIds, enabled, url, callback) { - // load from the db for the rest - getDatabase(function(db) { - db.readTransaction(function (t) { - var where = ""; - var params = []; - if (styleIds) { - if (styleIds.size == 0) { - callback([]); - return; - } - - where += " AND s.id IN (" - var firstStyleId = true; - styleIds.forEach(function(styleId) { - where += firstStyleId ? "?" : ",?"; - firstStyleId = false; - params.push(styleId); - }); - where += ")"; - } - /*if (enabled != null) { - where += ' AND enabled = ?'; - params.push(enabled); - } - if (url != null) { - where += ' AND s.url = ?'; - params.push(url); - }*/ - 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 currentStyle = null; - var currentSection = null; - for (var i = 0; i < r.rows.length; i++) { - var values = r.rows.item(i); - var metaName = null; - switch (values.metaName) { - case null: - break; - case "url": - metaName = "urls"; - break; - case "url-prefix": - metaName = "urlPrefixes"; - break; - case "domain": - var metaName = "domains"; - break; - case "regexps": - var metaName = "regexps"; - break; - default: - var metaName = values.metaName + "s"; - } - var metaValue = values.metaValue; - if (currentStyle == null || currentStyle.id != values.id) { - currentStyle = {id: values.id, url: values.url, updateUrl: values.updateUrl, md5Url: values.md5Url, name: values.name, enabled: values.enabled, sections: []}; - styles.push(currentStyle); - } - if (currentSection == null || currentSection.id != values.section_id) { - currentSection = {id: values.section_id, code: values.code}; - currentStyle.sections.push(currentSection); - } - if (metaName && metaValue) { - if (currentSection[metaName]) { - currentSection[metaName].push(metaValue); - } else { - currentSection[metaName] = [metaValue]; - } - } - } - if (isBackground()) { - styles.forEach(function(style) { - cachedStyles[style.id] = style; - - }); - } else { - chrome.extension.sendRequest({name: "cacheStyles", styles: styles}); - } - callback(styles.filter(function(style) { - return checkStyle(style, enabled, url); - })); - }, reportError); - }, reportError); - }, reportError); -} - -function getGlobalStyleIds(callback) { - if (isBackground() && cachedGlobalStyleIds != null) { - callback(cachedGlobalStyleIds); - return; - } - getDatabase(function(db) { - db.readTransaction(function (t) { - t.executeSql("SELECT DISTINCT s.style_id, s.code FROM sections s LEFT JOIN section_meta sm ON sm.section_id = s.id INNER JOIN styles st ON st.id = s.style_id GROUP BY s.id HAVING COUNT(sm.id) = 0", [], function (t, r) { - var style_ids = []; - for (var i = 0; i < r.rows.length; i++) { - var values = r.rows.item(i); - // ignore namespace only sections - if (!namespacePattern.test(values.code) && style_ids.indexOf(values.style_id) == -1) { - style_ids.push(values.style_id); - } - } - if (isBackground()) { - cachedGlobalStyleIds = style_ids; - } - callback(style_ids); - }, reportError); - }, reportError); - }, reportError); -} - - -function saveFromJSON(o, callback) { - getDatabase(function(db) { - db.transaction(function (t) { - if (o.id) { - t.executeSql('DELETE FROM section_meta WHERE section_id IN (SELECT id FROM sections WHERE style_id = ?);', [o.id]); - t.executeSql('DELETE FROM sections WHERE style_id = ?;', [o.id]); - } else { - t.executeSql('INSERT INTO styles (name, enabled, url, updateUrl) VALUES (?, ?, ?, ?);', [o.name, true, o.url, o.updateUrl]); - } - o.sections.forEach(function(section) { - if (o.id) { - t.executeSql('INSERT INTO sections (style_id, code) VALUES (?, ?);', [o.id, section.code]); - } else { - t.executeSql('INSERT INTO sections (style_id, code) SELECT id, ? FROM styles ORDER BY id DESC LIMIT 1;', [section.code]); - } - section.urls.forEach(function(u) { - t.executeSql("INSERT INTO section_meta (section_id, name, value) SELECT id, 'url', ? FROM sections ORDER BY id DESC LIMIT 1;", [u]); - }); - section.urlPrefixes.forEach(function(u) { - t.executeSql("INSERT INTO section_meta (section_id, name, value) SELECT id, 'url-prefix', ? FROM sections ORDER BY id DESC LIMIT 1;", [u]); - }); - section.domains.forEach(function(u) { - t.executeSql("INSERT INTO section_meta (section_id, name, value) SELECT id, 'domain', ? FROM sections ORDER BY id DESC LIMIT 1;", [u]); - }); - section.regexps.forEach(function(u) { - t.executeSql("INSERT INTO section_meta (section_id, name, value) SELECT id, 'regexp', ? FROM sections ORDER BY id DESC LIMIT 1;", [u]); - }); - }); - }, reportError, function() {saveFromJSONComplete(o.id, callback)}); - }, reportError); -} - -function saveFromJSONComplete(id, callback) { - chrome.extension.sendRequest({name: "styleChanged"}); - if (id) { - notifyAllTabs({name:"styleUpdated", style: id}); - if (callback) { - callback(id); - } - return; - } - // Load the style id - getDatabase(function(db) { - db.readTransaction(function (t) { - t.executeSql('SELECT id FROM styles ORDER BY id DESC LIMIT 1', [], function(t, r) { - var styleId = r.rows.item(0).id; - getStyles({id: styleId}, function(styles) { - notifyAllTabs({name:"styleAdded", style: styles[0]}); - }); - if (callback) { - callback(styleId); - } - }, reportError) - }, reportError) - }); -} - -function reportError() { - for (i in arguments) { - if ("message" in arguments[i]) { - //alert(arguments[i].message); - console.log(arguments[i].message); - } - } -} - -function isBackground() { - return typeof background != "undefined" && background; -} - -function getDomains(url) { - var d = /.*?:\/*([^\/]+)/.exec(url)[1]; - var domains = [d]; - while (d.indexOf(".") != -1) { - d = d.substring(d.indexOf(".") + 1); - domains.push(d); - } - return domains; -} - function enableStyle(id, enabled) { getDatabase(function(db) { db.transaction(function (t) { t.executeSql("UPDATE styles SET enabled = ? WHERE id = ?;", [enabled, id]); }, reportError, function() { - chrome.extension.sendRequest({name: "styleChanged"}); - getStyles({id: id}, function(styles) { + chrome.extension.sendMessage({method: "styleChanged"}); + chrome.extension.sendMessage({method: "getStyles", id: id}, function(styles) { handleUpdate(styles[0]); notifyAllTabs({name:"styleUpdated", style: styles[0]}); }); @@ -364,9 +72,18 @@ function deleteStyle(id) { t.executeSql('DELETE FROM sections WHERE style_id = ?;', [id]); t.executeSql("DELETE FROM styles WHERE id = ?;", [id]); }, reportError, function() { - chrome.extension.sendRequest({name: "styleChanged"}); + chrome.extension.sendMessage({method: "styleChanged"}); handleDelete(id); notifyAllTabs({name:"styleDeleted", id: id}); }); }); } + +function reportError() { + for (i in arguments) { + if ("message" in arguments[i]) { + //alert(arguments[i].message); + console.log(arguments[i].message); + } + } +}