issue 44 Stop using deprecated Chrome APIs

This commit is contained in:
Jason Barnabe 2012-08-19 20:14:33 -05:00
parent 8dbd59b08a
commit 9270dc1596
12 changed files with 522 additions and 609 deletions

View File

@ -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); response.forEach(applyStyle);
}); });
chrome.extension.onRequest.addListener(function(request, sender, sendResponse) { chrome.extension.onMessage.addListener(function(request, sender, sendResponse) {
switch(request.name) { switch(request.name) {
case "styleDeleted": case "styleDeleted":
removeStyle(request.id); removeStyle(request.id);
sendResponse({});
break; break;
case "styleUpdated": case "styleUpdated":
removeStyle(request.style.id); removeStyle(request.style.id);
//fallthrough //fallthrough
case "styleAdded": case "styleAdded":
if (request.style.enabled == "true") { if (request.style.enabled == "true") {
chrome.extension.sendRequest({name: "getStyleApplies", style: request.style, url: location.href}, function(response) { applyStyle(request.style);
if (response) {
applyStyle(response);
}
});
} }
sendResponse({});
} }
}); });
@ -31,54 +25,24 @@ function removeStyle(id) {
} }
function applyStyle(s) { function applyStyle(s) {
var style = document.createElement("style"); chrome.extension.sendMessage({method: "getStyleApplies", style: s, url: location.href}, function(response) {
style.setAttribute("id", "stylish-" + s.id); if (response && response.length > 0) {
style.setAttribute("class", "stylish"); applySections(s, response);
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;
} }
}
return section.regexps && section.regexps.some(function(regexp) {
return (new RegExp(regexp)).test(location.href);
}); });
} }
function getDomains(url) { function applySections(style, sections) {
var d = /.*?:\/*([^\/]+)/.exec(url)[1]; var styleElement = document.createElement("style");
var domains = [d]; styleElement.setAttribute("id", "stylish-" + style.id);
while (d.indexOf(".") != -1) { styleElement.setAttribute("class", "stylish");
d = d.substring(d.indexOf(".") + 1); styleElement.setAttribute("type", "text/css");
domains.push(d); 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;
} }

View File

@ -1,78 +1,283 @@
chrome.extension.onRequest.addListener(function(request, sender, sendResponse) { chrome.extension.onMessage.addListener(function(request, sender, sendResponse) {
switch (request.name) { switch (request.method) {
case "getStylesToApply": case "getStyles":
getStyles({matchUrl: sender.tab.url, enabled: true}, function(r) { getStyles(request, function(r) {
sendResponse(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; return true;
case "getStylesForUrl":
getStyles({url: request.url}, sendResponse);
break;
case "getStyleApplies": case "getStyleApplies":
sendResponse(styleAppliesToUrl(request.style, request.url)); sendResponse(getApplicableSections(request.style, request.url));
break; return true;
case "saveFromJSON": case "saveStyle":
saveFromJSON(request.json); saveStyle(request, sendResponse);
sendResponse({}); return true;
break;
case "styleChanged": case "styleChanged":
cachedGlobalStyleIds = null; cachedStyles = null;
cachedStyles = [];
sendResponse({});
break;
case "getCachedStyles":
sendResponse(cachedStyles);
break;
case "cacheStyles":
request.styles.forEach(function(style) {
cachedStyles[style.id] = style;
});
break; break;
} }
}); });
function styleAppliesToUrl(style, url) { function getStyles(options, callback) {
style.sections = style.sections.filter(function(section) {
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); return sectionAppliesToUrl(section, url);
}); });
if (style.sections.size == 0) { // ignore if it's just a namespace
return null; if (sections.length == 1 && namespacePattern.test(sections[0].code)) {
return [];
} }
return style; return sections;
} }
function sectionAppliesToUrl(section, url) { 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) { if (!section.urls && !section.domains && !section.urlPrefixes && !section.regexps) {
console.log(section + " is global"); console.log(section.id + " is global");
return true; return true;
} }
if (section.urls && section.urls.indexOf(url) != -1) { 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; return true;
} }
if (section.urlPrefixes && section.urlPrefixes.some(function(prefix) { if (section.urlPrefixes && section.urlPrefixes.some(function(prefix) {
return url.indexOf(prefix) == 0; 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; return true;
} }
if (section.domains && getDomains(url).some(function(domain) { if (section.domains && getDomains(url).some(function(domain) {
return section.domains.indexOf(domain) != -1; 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; return true;
} }
if (section.regexps && section.regexps.some(function(regexp) { 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); 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; return true;
} }
console.log(section + " does not apply due to " + url); console.log(section.id + " does not apply due to " + url);
return false; return false;
} }
var cachedGlobalStyleIds = null; var cachedStyles = null;
var cachedStyles = [];
var background = true; 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;
}

View File

@ -35,11 +35,6 @@
#sections > div:not(:first-child) { #sections > div:not(:first-child) {
border-top: 2px solid black; border-top: 2px solid black;
} }
span {
cursor: pointer;
color: #999;
}
label { label {
display: inline-block; display: inline-block;
width: 10em; width: 10em;
@ -86,23 +81,21 @@
} }
</style> </style>
<script src="localization.js"></script> <script src="localization.js"></script>
<script src="storage.js"></script>
<script src="messaging.js"></script>
<script src="edit.js"></script>
</head> </head>
<body> <body>
<div id="header"> <div id="header">
<h1 id="heading"></h1> <h1 id="heading"></h1>
<section id="basic-info"> <section id="basic-info">
<div><label for="name"><script>o("styleNameLabel")</script></label><input id="name" onchange="makeDirty()"><div> <div><label for="name" id="name-label"></label><input id="name"><div>
<div><label for="enabled"><script>o("styleEnabledLabel")</script></label><input type="checkbox" id="enabled" onchange="makeDirty()"></div> <div><label for="enabled" id="enabled-label"></label><input type="checkbox" id="enabled"></div>
</section> </section>
<button onclick="showMozillaFormat()"><script>o("styleToMozillaFormat")</script></button><img id="to-mozilla-help" src="help.png" onclick="showToMozillaHelp()"><br><br> <button id="to-mozilla"></button><img id="to-mozilla-help" src="help.png"><br><br>
<button onclick="save()"><script>o("styleSaveLabel")</script></button> <button id="save-button"></button>
<a href="manage.html"><button><script>o("styleCancelEditLabel")</script></button></a> <a href="manage.html"><button id="cancel-button"></button></a>
</div> </div>
<section id="sections"> <section id="sections">
<h2><script>o("styleSectionsTitle")</script> <img id="sections-help" src="help.png" onclick="showSectionHelp()"></h2> <h2><span id="sections-heading"></span> <img id="sections-help" src="help.png"></h2>
</section> </section>
<script src="edit.js"></script>
</body> </body>
</html> </html>

138
edit.js
View File

@ -2,14 +2,14 @@ var styleId = null;
var dirty = false; var dirty = false;
var appliesToTemplate = document.createElement("li"); var appliesToTemplate = document.createElement("li");
appliesToTemplate.innerHTML = '<select name="applies-type" onchange="makeDirty()"><option value="url">' + t("appliesUrlOption") + '</option><option value="url-prefix">' + t("appliesUrlPrefixOption") + '</option><option value="domain">' + t("appliesDomainOption") + '</option><option value="regexp">' + t("appliesRegexpOption") + '</option></select><input name="applies-value" onchange="makeDirty()"><button onclick="removeAppliesTo(event)" class="remove-applies-to">' + t("appliesRemove") + '</button><button class="add-applies-to" onclick="addAppliesTo(this.parentNode.parentNode)">' + t("appliesAdd") + '</button>'; appliesToTemplate.innerHTML = '<select name="applies-type" class="applies-type"><option value="url">' + t("appliesUrlOption") + '</option><option value="url-prefix">' + t("appliesUrlPrefixOption") + '</option><option value="domain">' + t("appliesDomainOption") + '</option><option value="regexp">' + t("appliesRegexpOption") + '</option></select><input name="applies-value" class="applies-value"><button class="remove-applies-to">' + t("appliesRemove") + '</button><button class="add-applies-to">' + t("appliesAdd") + '</button>';
var appliesToEverythingTemplate = document.createElement("li"); var appliesToEverythingTemplate = document.createElement("li");
appliesToEverythingTemplate.className = "applies-to-everything"; appliesToEverythingTemplate.className = "applies-to-everything";
appliesToEverythingTemplate.innerHTML = t("appliesToEverything") + ' <button class="add-applies-to" onclick="addAppliesTo(this.parentNode.parentNode)">' + t("appliesSpecify") + '</button>' appliesToEverythingTemplate.innerHTML = t("appliesToEverything") + ' <button class="add-applies-to">' + t("appliesSpecify") + '</button>'
var sectionTemplate = document.createElement("div"); var sectionTemplate = document.createElement("div");
sectionTemplate.innerHTML = '<label>' + t('sectionCode') + '</label><textarea class="code" onchange="makeDirty()"></textarea><br><div class="applies-to"><label>' + t("appliesLabel") + ' <img src="help.png" onclick="showAppliesToHelp()" alt="' + t('helpAlt') + '"></label><ul class="applies-to-list"></ul></div><button class="remove-section" onclick="removeSection(event)">' + t('sectionRemove') + '</button><button class="add-section" onclick="addSection()">' + t('sectionAdd') + '</button>'; sectionTemplate.innerHTML = '<label>' + t('sectionCode') + '</label><textarea class="code"></textarea><br><div class="applies-to"><label>' + t("appliesLabel") + ' <img class="applies-to-help" src="help.png" alt="' + t('helpAlt') + '"></label><ul class="applies-to-list"></ul></div><button class="remove-section">' + t('sectionRemove') + '</button><button class="add-section">' + t('sectionAdd') + '</button>';
function makeDirty() { function makeDirty() {
dirty = true; dirty = true;
@ -30,23 +30,36 @@ function addAppliesTo(list, name, value) {
e = appliesToTemplate.cloneNode(true); e = appliesToTemplate.cloneNode(true);
e.querySelector("[name=applies-type]").value = name; e.querySelector("[name=applies-type]").value = name;
e.querySelector("[name=applies-value]").value = value; 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()) { } else if (showingEverything || list.hasChildNodes()) {
e = appliesToTemplate.cloneNode(true); e = appliesToTemplate.cloneNode(true);
if (list.hasChildNodes()) { if (list.hasChildNodes()) {
e.querySelector("[name=applies-type]").value = list.querySelector("li:last-child [name='applies-type']").value; 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 { } else {
e = appliesToEverythingTemplate.cloneNode(true); e = appliesToEverythingTemplate.cloneNode(true);
} }
e.querySelector(".add-applies-to").addEventListener("click", function() {addAppliesTo(this.parentNode.parentNode)}, false);
list.appendChild(e); list.appendChild(e);
} }
function addSection(section) { function addSection(section) {
var div = sectionTemplate.cloneNode(true); 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"); var appliesTo = div.querySelector(".applies-to-list");
if (section) { 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) { if (section.urls) {
section.urls.forEach(function(url) { section.urls.forEach(function(url) {
addAppliesTo(appliesTo, "url", url); addAppliesTo(appliesTo, "url", url);
@ -82,7 +95,9 @@ function removeAppliesTo(event) {
var appliesToList = event.target.parentNode.parentNode; var appliesToList = event.target.parentNode.parentNode;
appliesToList.removeChild(event.target.parentNode); appliesToList.removeChild(event.target.parentNode);
if (!appliesToList.hasChildNodes()) { 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(); makeDirty();
} }
@ -106,7 +121,7 @@ function init() {
} }
// This is an edit // This is an edit
var id = idMatch[1]; var id = idMatch[1];
getStyles({id: id}, function(styles) { chrome.extension.sendMessage({method: "getStyles", id: id}, function(styles) {
var style = styles[0]; var style = styles[0];
styleId = style.id; styleId = style.id;
initWithStyle(style); initWithStyle(style);
@ -117,7 +132,7 @@ function initWithStyle(style) {
document.getElementById("name").value = style.name; document.getElementById("name").value = style.name;
document.getElementById("enabled").checked = style.enabled == "true"; document.getElementById("enabled").checked = style.enabled == "true";
document.getElementById("heading").innerHTML = t("editStyleHeading"); 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 // 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) { Array.prototype.forEach.call(document.querySelectorAll("#sections > div"), function(div) {
div.parentNode.removeChild(div); div.parentNode.removeChild(div);
@ -125,8 +140,8 @@ function initWithStyle(style) {
style.sections.forEach(addSection); style.sections.forEach(addSection);
} }
function initTitle(style) { function initTitle(name) {
document.title = t('editStyleTitle', [style.name]); document.title = t('editStyleTitle', [name]);
} }
function validate() { function validate() {
@ -145,31 +160,14 @@ function save() {
} }
var name = document.getElementById("name").value; var name = document.getElementById("name").value;
var enabled = document.getElementById("enabled").checked; var enabled = document.getElementById("enabled").checked;
getDatabase(function(db) { var request = {
db.transaction(function (t) { method: "saveStyle",
var sections = getSections(); id: styleId,
if (styleId == null) { name: name,
t.executeSql('INSERT INTO styles (name, enabled) VALUES (?, ?);', [name, enabled]); enabled: enabled,
sections.forEach(function(s) { sections: getSections(),
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) { chrome.extension.sendMessage(request, saveComplete);
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);
} }
function getSections() { function getSections() {
@ -179,13 +177,15 @@ function getSections() {
if (/^\s*$/.test(code)) { if (/^\s*$/.test(code)) {
return; return;
} }
sections.push({code: code, meta: getMeta(div)}); var meta = getMeta(div);
meta.code = code;
sections.push(meta);
}); });
return sections; return sections;
} }
function getMeta(e) { function getMeta(e) {
var meta = []; var meta = {urls: [], urlPrefixes: [], domains: [], regexps: []};
Array.prototype.forEach.call(e.querySelector(".applies-to-list").childNodes, function(li) { Array.prototype.forEach.call(e.querySelector(".applies-to-list").childNodes, function(li) {
if (li.className == appliesToEverythingTemplate.className) { if (li.className == appliesToEverythingTemplate.className) {
return; return;
@ -193,26 +193,33 @@ function getMeta(e) {
var a = li.querySelector("[name=applies-type]").value; var a = li.querySelector("[name=applies-type]").value;
var b = li.querySelector("[name=applies-value]").value; var b = li.querySelector("[name=applies-value]").value;
if (a && b) { 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; return meta;
} }
function saveComplete() { function saveComplete(id) {
if (styleId == null) { // Go from new style URL to edit style URL
// Load the style id if (location.href.indexOf("id=") == -1) {
getDatabase(function(db) { // give the code above a moment before we kill the page
db.readTransaction(function (t) { setTimeout(function() {location.href = "edit.html?id=" + id;}, 200);
t.executeSql('SELECT id FROM styles ORDER BY id DESC LIMIT 1', [], function(t, r) { } else {
styleId = r.rows.item(0).id; initTitle(document.getElementById("name").value);
notifySave(true);
}, reportError)
}, reportError)
});
return;
} }
notifySave(false);
} }
function showMozillaFormat() { function showMozillaFormat() {
@ -233,20 +240,6 @@ function toMozillaFormat() {
}).join("\n\n"); }).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() { function showSectionHelp() {
showHelp(t("sectionHelp")); showHelp(t("sectionHelp"));
} }
@ -263,11 +256,12 @@ function showHelp(text) {
alert(text); alert(text);
} }
chrome.extension.onRequest.addListener(function(request, sender, sendResponse) { chrome.extension.onMessage.addListener(function(request, sender, sendResponse) {
var installed = document.getElementById("installed"); var installed = document.getElementById("installed");
switch(request.name) { switch(request.name) {
case "styleUpdated": case "styleUpdated":
initWithStyle(request.style); initWithStyle(request.style);
dirty = false;
break; break;
case "styleDeleted": case "styleDeleted":
if (styleId == request.id) { 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);

View File

@ -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) { if (response.length == 0) {
sendEvent("styleCanBeInstalledChrome"); sendEvent("styleCanBeInstalledChrome");
} else { } else {
@ -18,7 +18,8 @@ document.addEventListener("stylishInstallChrome", function() {
getResource(getMeta("stylish-code-chrome"), function(code) { getResource(getMeta("stylish-code-chrome"), function(code) {
// check for old style json // check for old style json
var json = JSON.parse(code); var json = JSON.parse(code);
chrome.extension.sendRequest({name:"saveFromJSON", json: json}, function(response) { json.method = "saveStyle";
chrome.extension.sendMessage(json, function(response) {
sendEvent("styleInstalledChrome"); sendEvent("styleInstalledChrome");
}); });
}); });

View File

@ -87,16 +87,18 @@
<script src="localization.js"></script> <script src="localization.js"></script>
<script src="storage.js"></script> <script src="storage.js"></script>
<script src="messaging.js"></script> <script src="messaging.js"></script>
<script src="manage.js"></script>
</head> </head>
<body> <body>
<div id="header"> <div id="header">
<img src="128.png"> <img src="128.png">
<h1><script>o("manageHeading")</script></h1> <h1 id="manage-heading"></h1>
<p><script>o("manageText")</script></p> <p id="manage-text"></p>
<p><button onclick="checkUpdateAll()"><script>o("checkAllUpdates")</script></button></p> <p><button id="check-all-updates"></button></p>
<p><a href="edit.html"><button><script>o("addStyleLabel")</script></button></a></p> <p><a href="edit.html"><button id="add-style-label"></button></a></p>
</div> </div>
<div id="installed"></div> <div id="installed"></div>
<script src="manage.js"></script>
</body> </body>
</html> </html>

View File

@ -1,12 +1,11 @@
var styleTemplate = document.createElement("div"); var styleTemplate = document.createElement("div");
styleTemplate.innerHTML = "<h2 class='style-name'></h2><p class='applies-to'></p><p class='actions'><a class='style-edit-link' href='edit.html?id='><button>" + t('editStyleLabel') + "</button></a><button class='enable' onclick='enable(event, true)'>" + t('enableStyleLabel') + "</button><button class='disable' onclick='enable(event, false)'>" + t('disableStyleLabel') + "</button><button class='delete' onclick='doDelete(event)'>" + t('deleteStyleLabel') + "</button><button class='check-update' onclick='doCheckUpdate(event)'>" + t('checkForUpdate') + "</button><button class='update' onclick='doUpdate(event)'>" + t('installUpdate') + "</button><span class='update-note'></span></p>"; styleTemplate.innerHTML = "<h2 class='style-name'></h2><p class='applies-to'></p><p class='actions'><a class='style-edit-link' href='edit.html?id='><button>" + t('editStyleLabel') + "</button></a><button class='enable'>" + t('enableStyleLabel') + "</button><button class='disable'>" + t('disableStyleLabel') + "</button><button class='delete'>" + t('deleteStyleLabel') + "</button><button class='check-update'>" + t('checkForUpdate') + "</button><button class='update'>" + t('installUpdate') + "</button><span class='update-note'></span></p>";
var appliesToExtraTemplate = document.createElement("span"); var appliesToExtraTemplate = document.createElement("span");
appliesToExtraTemplate.className = "applies-to-extra"; appliesToExtraTemplate.className = "applies-to-extra";
appliesToExtraTemplate.innerHTML = t('appliesDisplayTruncatedSuffix'); appliesToExtraTemplate.innerHTML = " " + t('appliesDisplayTruncatedSuffix');
getStyles({}, showStyles); chrome.extension.sendMessage({method: "getStyles"}, showStyles);
function showStyles(styles) { function showStyles(styles) {
var installed = document.getElementById("installed"); var installed = document.getElementById("installed");
@ -80,6 +79,11 @@ function createStyleElement(style) {
} }
var editLink = e.querySelector(".style-edit-link"); var editLink = e.querySelector(".style-edit-link");
editLink.setAttribute("href", editLink.getAttribute("href") + style.id); editLink.setAttribute("href", editLink.getAttribute("href") + style.id);
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; return e;
} }
@ -111,19 +115,16 @@ function getStyleElement(event) {
return null; return null;
} }
chrome.extension.onRequest.addListener(function(request, sender, sendResponse) { chrome.extension.onMessage.addListener(function(request, sender, sendResponse) {
switch(request.name) { switch(request.name) {
case "styleUpdated": case "styleUpdated":
handleUpdate(request.style); handleUpdate(request.style);
sendResponse({});
break; break;
case "styleAdded": case "styleAdded":
installed.appendChild(createStyleElement(request.style)); installed.appendChild(createStyleElement(request.style));
sendResponse({});
break; break;
case "styleDeleted": case "styleDeleted":
handleDelete(request.id); handleDelete(request.id);
sendResponse({});
break; break;
} }
}); });
@ -168,7 +169,7 @@ function checkUpdate(element) {
} }
function checkNeedsUpdate(id, serverJson) { function checkNeedsUpdate(id, serverJson) {
getStyles({id: id}, function(styles) { chrome.extension.sendMessage({method: "getStyles", id: id}, function(styles) {
var style = styles[0]; var style = styles[0];
if (codeIsEqual(style.sections, serverJson.sections)) { if (codeIsEqual(style.sections, serverJson.sections)) {
handleNeedsUpdate("no", id, serverJson); handleNeedsUpdate("no", id, serverJson);
@ -199,10 +200,7 @@ function handleNeedsUpdate(needsUpdate, id, serverJson) {
function doUpdate(event) { function doUpdate(event) {
var element = getStyleElement(event); var element = getStyleElement(event);
var o = {}; chrome.extension.sendMessage({method: "saveStyle", id: element.getAttribute('style-id'), sections: element.updatedCode.sections}, function() {
o.id = element.getAttribute('style-id');
o.sections = element.updatedCode.sections;
saveFromJSON(o, function() {
element.updatedCode = ""; element.updatedCode = "";
element.className = element.className.replace("can-update", "update-done"); element.className = element.className.replace("can-update", "update-done");
element.querySelector(".update-note").innerHTML = t('updateCompleted'); element.querySelector(".update-note").innerHTML = t('updateCompleted');
@ -279,3 +277,9 @@ function getType(o) {
} }
document.title = t("manageTitle"); 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);

View File

@ -1,7 +1,9 @@
{ {
"name": "Stylish", "name": "Stylish",
"version": "0.10", "version": "1.0",
"description": "__MSG_description__", "description": "__MSG_description__",
"homepage_url": "http://userstyles.org",
"manifest_version": 2,
"icons": { "icons": {
"16": "16.png", "16": "16.png",
"48": "48.png", "48": "48.png",
@ -9,10 +11,11 @@
}, },
"permissions": [ "permissions": [
"tabs", "tabs",
"http://userstyles.org/", "http://userstyles.org/"
"http://userstyles.local/"
], ],
"background_page": "background.html", "background": {
"page": "background.html"
},
"content_scripts": [ "content_scripts": [
{ {
"matches": ["http://*/*", "https://*/*"], "matches": ["http://*/*", "https://*/*"],
@ -21,7 +24,7 @@
"js": ["apply.js"] "js": ["apply.js"]
}, },
{ {
"matches": ["http://userstyles.org/*", "http://userstyles.local/*"], "matches": ["http://userstyles.org/*"],
"run_at": "document_end", "run_at": "document_end",
"all_frames": false, "all_frames": false,
"js": ["install.js"] "js": ["install.js"]

View File

@ -2,7 +2,7 @@ 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.sendRequest(tab.id, request); chrome.tabs.sendMessage(tab.id, request);
updateBadgeText(tab); updateBadgeText(tab);
}); });
}); });
@ -10,12 +10,13 @@ function notifyAllTabs(request) {
} }
function updateBadgeText(tab) { function updateBadgeText(tab) {
getStyles({matchUrl: tab.url}, function(styles) { chrome.extension.sendMessage({method: "getStyles", matchUrl: tab.url, enabled: true}, function(styles) {
chrome.browserAction.setBadgeText({text: getBadgeText(styles), tabId: tab.id}); 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) { function getBadgeText(styles) {
var e = styles.filter(function(style) { return style.enabled == "true"; }); return styles.length == 0 ? "" : ("" + styles.length);
return e.length == 0 ? "" : ("" + e.length);
} }

View File

@ -1,117 +1,55 @@
<style> <html>
body { <head>
width: 200px; <style>
} body {
#no-styles { width: 200px;
font-style: italic;
}
.disabled {
color: #BBB;
}
.disabled .disable {
display: none;
}
.enabled .enable {
display: none;
}
.style-name {
font-weight: bold;
}
.actions {
font-size: x-small;
}
a, a:visited {
color: black;
}
.entry {
padding-bottom: 0.5em;
margin-bottom: 0.5em;
border-bottom: 1px solid black;
}
#find-styles, #manage-styles {
font-size: smaller;
}
#find-styles {
margin-bottom: 0.5em;
}
</style>
<script src="localization.js"></script>
<script src="storage.js"></script>
<script src="messaging.js"></script>
<script>
var styleTemplate = document.createElement("div");
styleTemplate.innerHTML = "<div class='style-name'></div><div class='actions'><a class='style-edit-link' href='edit.html?id=' onclick='return openLink(event);'>" + t('editStyleLabel') + "</a> <a href='#' class='enable' onclick='enable(event, true);return false;'>" + t('enableStyleLabel') + "</a> <a href='#' class='disable' onclick='enable(event, false);return false;'>" + t('disableStyleLabel') + "</a> <a href='#' class='delete' onclick='doDelete(event);return false;'>" + t('deleteStyleLabel') + "</a></div>";
chrome.tabs.getSelected(null, function(tab) {
//alert('here');
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 = "<div class='entry' id='no-styles'>" + t('noStylesForSite') + "</div>";
}
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);
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; #no-styles {
} font-style: italic;
return null; }
} .disabled {
color: #BBB;
}
.disabled .disable {
display: none;
}
.enabled .enable {
display: none;
}
.style-name {
font-weight: bold;
}
.actions {
font-size: x-small;
}
a, a:visited {
color: black;
}
.entry {
padding-bottom: 0.5em;
margin-bottom: 0.5em;
border-bottom: 1px solid black;
}
#find-styles, #manage-styles {
font-size: smaller;
}
#find-styles {
margin-bottom: 0.5em;
}
</style>
function openLink(event) { <script src="localization.js"></script>
chrome.tabs.create({url: event.target.href}); <script src="storage.js"></script>
return false; <script src="messaging.js"></script>
} </head>
<body>
function handleUpdate(style) { <div id="installed"></div>
var installed = document.getElementById("installed");
installed.replaceChild(createStyleElement(style), installed.querySelector("[style-id='" + style.id + "']"));
}
function handleDelete(id) { <div id="find-styles"><a id="find-styles-link" href="#"></a></div>
var installed = document.getElementById("installed"); <div id="manage-styles"><a id="open-manage-link" href="manage.html" target="_new"></a></div>
installed.removeChild(installed.querySelector("[style-id='" + id + "']"));
}
</script>
<div id="installed"></div> <script src="popup.js"></script>
<div id="find-styles"><a href="#" onclick="return openLink(event);"><script>o('findStylesForSite')</script></a></div> </body>
<div id="manage-styles"><a href="manage.html" target="_new"><script>o('openManage')</script></a></div> </html>

77
popup.js Normal file
View File

@ -0,0 +1,77 @@
var styleTemplate = document.createElement("div");
styleTemplate.innerHTML = "<div class='style-name'></div><div class='actions'><a class='style-edit-link' href='edit.html?id='>" + t('editStyleLabel') + "</a> <a href='#' class='enable'>" + t('enableStyleLabel') + "</a> <a href='#' class='disable'>" + t('disableStyleLabel') + "</a> <a href='#' class='delete'>" + t('deleteStyleLabel') + "</a></div>";
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 = "<div class='entry' id='no-styles'>" + t('noStylesForSite') + "</div>";
}
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);

View File

@ -1,5 +1,3 @@
var namespacePattern = /^\s*@namespace\s+([a-zA-Z]+\s+)?url\(\"?http:\/\/www.w3.org\/1999\/xhtml\"?\);?\s*$/;
var stylishDb = null; var stylishDb = null;
function getDatabase(ready, error) { function getDatabase(ready, error) {
if (stylishDb != null) { if (stylishDb != null) {
@ -53,303 +51,13 @@ function dbV13(d, error, done) {
}, error, function() { done(d)}); }, 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) { function enableStyle(id, enabled) {
getDatabase(function(db) { getDatabase(function(db) {
db.transaction(function (t) { db.transaction(function (t) {
t.executeSql("UPDATE styles SET enabled = ? WHERE id = ?;", [enabled, id]); t.executeSql("UPDATE styles SET enabled = ? WHERE id = ?;", [enabled, id]);
}, reportError, function() { }, reportError, function() {
chrome.extension.sendRequest({name: "styleChanged"}); chrome.extension.sendMessage({method: "styleChanged"});
getStyles({id: id}, function(styles) { chrome.extension.sendMessage({method: "getStyles", id: id}, function(styles) {
handleUpdate(styles[0]); handleUpdate(styles[0]);
notifyAllTabs({name:"styleUpdated", style: 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 sections WHERE style_id = ?;', [id]);
t.executeSql("DELETE FROM styles WHERE id = ?;", [id]); t.executeSql("DELETE FROM styles WHERE id = ?;", [id]);
}, reportError, function() { }, reportError, function() {
chrome.extension.sendRequest({name: "styleChanged"}); chrome.extension.sendMessage({method: "styleChanged"});
handleDelete(id); handleDelete(id);
notifyAllTabs({name:"styleDeleted", id: 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);
}
}
}