2017-03-16 13:36:33 +00:00
|
|
|
/* globals configureCommands */
|
|
|
|
|
2015-01-30 17:28:05 +00:00
|
|
|
var writeStyleTemplate = document.createElement("a");
|
|
|
|
writeStyleTemplate.className = "write-style-link";
|
|
|
|
|
2015-03-24 14:07:59 +00:00
|
|
|
var installed = document.getElementById("installed");
|
|
|
|
|
2015-10-05 11:27:17 +00:00
|
|
|
if (!prefs.get("popup.stylesFirst")) {
|
2015-03-24 14:07:59 +00:00
|
|
|
document.body.insertBefore(document.querySelector("body > .actions"), installed);
|
2015-03-12 23:00:23 +00:00
|
|
|
}
|
|
|
|
|
2015-05-21 09:34:44 +00:00
|
|
|
getActiveTabRealURL(updatePopUp);
|
2015-03-23 18:56:11 +00:00
|
|
|
|
|
|
|
function updatePopUp(url) {
|
2015-08-06 10:05:36 +00:00
|
|
|
var urlWillWork = /^(file|http|https|ftps?|chrome\-extension):/.exec(url);
|
2015-01-30 17:28:05 +00:00
|
|
|
if (!urlWillWork) {
|
2015-03-12 23:00:23 +00:00
|
|
|
document.body.classList.add("blocked");
|
2017-03-06 20:17:32 +00:00
|
|
|
document.getElementById("unavailable").style.display = "flex";
|
2015-03-17 18:03:20 +00:00
|
|
|
return;
|
2015-01-30 17:28:05 +00:00
|
|
|
}
|
|
|
|
|
Improve style caching, cache requests too, add code:false mode
Previously, when a cache was invalidated and every tab/iframe issued a getStyles request, we previous needlessly accessed IndexedDB for each of these requests. It happened because 1) the global cachedStyles was created only at the end of the async DB-reading, 2) and each style record is retrieved asynchronously so the single threaded JS engine interleaved all these operations. It could easily span a few seconds when many tabs are open and you have like 100 styles.
Now, in getStyles: all requests issued while cachedStyles is being populated are queued and invoked at the end.
Now, in filterStyles: all requests are cached using the request's options combined in a string as a key. It also helps on each navigation because we monitor page loading process at different stages: before, when committed, history traversal, requesting applicable styles by a content script. Icon badge update also may issue a copy of the just issued request by one of the navigation listeners.
Now, the caches are invalidated smartly: style add/update/delete/toggle only purges filtering cache, and modifies style cache in-place without re-reading the entire IndexedDB.
Now, code:false mode for manage page that only needs style meta. It reduces the transferred message size 10-100 times thus reducing the overhead caused by to internal JSON-fication in the extensions API.
Also fast&direct getStylesSafe for own pages; code cosmetics
2017-03-17 22:50:35 +00:00
|
|
|
getStylesSafe({matchUrl: url}).then(showStyles);
|
|
|
|
|
2015-03-23 18:56:11 +00:00
|
|
|
document.querySelector("#find-styles a").href = "https://userstyles.org/styles/browse/all/" + encodeURIComponent("file" === urlWillWork[1] ? "file:" : url);
|
2015-01-30 17:28:05 +00:00
|
|
|
|
|
|
|
// Write new style links
|
2015-02-18 18:56:39 +00:00
|
|
|
var writeStyleLinks = [],
|
|
|
|
container = document.createElement('span');
|
|
|
|
container.id = "match";
|
2015-01-30 17:28:05 +00:00
|
|
|
|
|
|
|
// For this URL
|
|
|
|
var urlLink = writeStyleTemplate.cloneNode(true);
|
2015-03-23 18:56:11 +00:00
|
|
|
urlLink.href = "edit.html?url-prefix=" + encodeURIComponent(url);
|
2015-03-02 04:23:03 +00:00
|
|
|
urlLink.appendChild(document.createTextNode( // switchable; default="this URL"
|
2015-10-05 11:27:17 +00:00
|
|
|
!prefs.get("popup.breadcrumbs.usePath")
|
2015-03-02 04:23:03 +00:00
|
|
|
? t("writeStyleForURL").replace(/ /g, "\u00a0")
|
2015-03-23 18:56:11 +00:00
|
|
|
: /\/\/[^/]+\/(.*)/.exec(url)[1]
|
2015-03-02 04:23:03 +00:00
|
|
|
));
|
2015-03-23 18:56:11 +00:00
|
|
|
urlLink.title = "url-prefix(\"$\")".replace("$", url);
|
2015-01-30 17:28:05 +00:00
|
|
|
writeStyleLinks.push(urlLink);
|
|
|
|
document.querySelector("#write-style").appendChild(urlLink)
|
2015-10-05 11:27:17 +00:00
|
|
|
if (prefs.get("popup.breadcrumbs")) { // switchable; default=enabled
|
2015-03-02 04:23:03 +00:00
|
|
|
urlLink.addEventListener("mouseenter", function(event) { this.parentNode.classList.add("url()") }, false);
|
|
|
|
urlLink.addEventListener("focus", function(event) { this.parentNode.classList.add("url()") }, false);
|
|
|
|
urlLink.addEventListener("mouseleave", function(event) { this.parentNode.classList.remove("url()") }, false);
|
|
|
|
urlLink.addEventListener("blur", function(event) { this.parentNode.classList.remove("url()") }, false);
|
|
|
|
}
|
2015-01-30 17:28:05 +00:00
|
|
|
|
|
|
|
// For domain
|
2015-03-23 18:56:11 +00:00
|
|
|
var domains = getDomains(url)
|
2015-01-30 17:28:05 +00:00
|
|
|
domains.forEach(function(domain) {
|
|
|
|
// Don't include TLD
|
|
|
|
if (domains.length > 1 && domain.indexOf(".") == -1) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
var domainLink = writeStyleTemplate.cloneNode(true);
|
|
|
|
domainLink.href = "edit.html?domain=" + encodeURIComponent(domain);
|
|
|
|
domainLink.appendChild(document.createTextNode(domain));
|
2015-03-03 19:31:32 +00:00
|
|
|
domainLink.title = "domain(\"$\")".replace("$", domain);
|
2015-02-18 18:56:39 +00:00
|
|
|
domainLink.setAttribute("subdomain", domain.substring(0, domain.indexOf(".")));
|
2015-01-30 17:28:05 +00:00
|
|
|
writeStyleLinks.push(domainLink);
|
|
|
|
});
|
|
|
|
|
|
|
|
var writeStyle = document.querySelector("#write-style");
|
|
|
|
writeStyleLinks.forEach(function(link, index) {
|
2015-01-30 18:35:37 +00:00
|
|
|
link.addEventListener("click", openLinkInTabOrWindow, false);
|
2015-02-18 18:56:39 +00:00
|
|
|
container.appendChild(link);
|
2015-01-30 17:28:05 +00:00
|
|
|
});
|
2015-10-05 11:27:17 +00:00
|
|
|
if (prefs.get("popup.breadcrumbs")) {
|
2015-03-02 04:23:03 +00:00
|
|
|
container.classList.add("breadcrumbs");
|
|
|
|
container.appendChild(container.removeChild(container.firstChild));
|
|
|
|
}
|
2015-02-18 18:56:39 +00:00
|
|
|
writeStyle.appendChild(container);
|
2015-03-23 18:56:11 +00:00
|
|
|
}
|
2012-08-20 01:14:33 +00:00
|
|
|
|
|
|
|
function showStyles(styles) {
|
2015-10-05 11:27:17 +00:00
|
|
|
var enabledFirst = prefs.get("popup.enabledFirst");
|
2015-03-03 21:49:23 +00:00
|
|
|
styles.sort(function(a, b) {
|
|
|
|
if (enabledFirst && a.enabled !== b.enabled) return !(a.enabled < b.enabled) ? -1 : 1;
|
|
|
|
return a.name.localeCompare(b.name);
|
|
|
|
});
|
2012-08-20 01:14:33 +00:00
|
|
|
if (styles.length == 0) {
|
|
|
|
installed.innerHTML = "<div class='entry' id='no-styles'>" + t('noStylesForSite') + "</div>";
|
|
|
|
}
|
|
|
|
styles.map(createStyleElement).forEach(function(e) {
|
|
|
|
installed.appendChild(e);
|
|
|
|
});
|
2017-03-14 06:12:00 +00:00
|
|
|
// force Chrome to resize the popup
|
|
|
|
document.body.style.height = '10px';
|
|
|
|
document.documentElement.style.height = '10px';
|
2012-08-20 01:14:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function createStyleElement(style) {
|
2017-03-19 21:24:29 +00:00
|
|
|
// reuse event function references
|
|
|
|
createStyleElement.events = createStyleElement.events || {
|
|
|
|
checkboxClick() {
|
2017-03-19 22:58:10 +00:00
|
|
|
enableStyle(getClickedStyleId(event), this.checked);
|
2017-03-19 21:24:29 +00:00
|
|
|
},
|
2017-03-19 22:58:10 +00:00
|
|
|
styleNameClick(event) {
|
2017-03-19 21:24:29 +00:00
|
|
|
this.checkbox.click();
|
2017-03-19 22:58:10 +00:00
|
|
|
event.preventDefault();
|
2017-03-19 21:24:29 +00:00
|
|
|
},
|
2017-03-19 22:58:10 +00:00
|
|
|
toggleClick(event) {
|
|
|
|
enableStyle(getClickedStyleId(event), this.matches('.enable'));
|
2017-03-19 21:24:29 +00:00
|
|
|
},
|
|
|
|
deleteClick() {
|
2017-03-19 22:58:10 +00:00
|
|
|
doDelete(event);
|
2017-03-19 21:24:29 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
const entry = template.style.cloneNode(true);
|
|
|
|
entry.setAttribute('style-id', style.id);
|
|
|
|
Object.assign(entry, {
|
|
|
|
styleId: style.id,
|
|
|
|
className: ['entry', style.enabled ? 'enabled' : 'disabled'].join(' '),
|
|
|
|
onmousedown: openEditorOnMiddleclick,
|
|
|
|
onauxclick: openEditorOnMiddleclick,
|
|
|
|
});
|
|
|
|
|
|
|
|
const checkbox = entry.querySelector('.checker');
|
|
|
|
Object.assign(checkbox, {
|
|
|
|
id: 'style-' + style.id,
|
|
|
|
checked: style.enabled,
|
|
|
|
onclick: createStyleElement.events.checkboxClick,
|
|
|
|
});
|
|
|
|
|
|
|
|
const editLink = entry.querySelector('.style-edit-link');
|
|
|
|
Object.assign(editLink, {
|
|
|
|
href: editLink.getAttribute('href') + style.id,
|
|
|
|
onclick: openLinkInTabOrWindow,
|
|
|
|
});
|
|
|
|
|
|
|
|
const styleName = entry.querySelector('.style-name');
|
|
|
|
Object.assign(styleName, {
|
|
|
|
htmlFor: 'style-' + style.id,
|
|
|
|
onclick: createStyleElement.events.styleNameClick,
|
|
|
|
});
|
2015-05-07 23:07:37 +00:00
|
|
|
styleName.checkbox = checkbox;
|
2017-03-19 21:24:29 +00:00
|
|
|
styleName.appendChild(document.createTextNode(style.name));
|
2012-08-20 01:14:33 +00:00
|
|
|
|
2017-03-19 21:24:29 +00:00
|
|
|
entry.querySelector('.enable').onclick = createStyleElement.events.toggleClick;
|
|
|
|
entry.querySelector('.disable').onclick = createStyleElement.events.toggleClick;
|
|
|
|
entry.querySelector('.delete').onclick = createStyleElement.events.deleteClick;
|
|
|
|
|
|
|
|
return entry;
|
2012-08-20 01:14:33 +00:00
|
|
|
}
|
|
|
|
|
2017-03-19 22:58:10 +00:00
|
|
|
function doDelete(event) {
|
2017-03-13 12:58:35 +00:00
|
|
|
document.getElementById('confirm').dataset.display = true;
|
2017-03-19 22:58:10 +00:00
|
|
|
const id = getClickedStyleId(event);
|
2017-03-13 13:41:01 +00:00
|
|
|
document.querySelector('#confirm b').textContent =
|
|
|
|
document.querySelector(`[style-id="${id}"] label`).textContent;
|
2017-03-13 12:58:35 +00:00
|
|
|
document.getElementById('confirm').dataset.id = id;
|
2012-08-20 01:14:33 +00:00
|
|
|
}
|
2017-03-19 21:24:29 +00:00
|
|
|
|
2017-03-13 12:58:35 +00:00
|
|
|
document.getElementById('confirm').addEventListener('click', e => {
|
|
|
|
let cmd = e.target.dataset.cmd;
|
|
|
|
if (cmd === 'ok') {
|
2017-03-13 15:48:00 +00:00
|
|
|
deleteStyle(document.getElementById('confirm').dataset.id, () => {
|
2017-03-13 19:26:43 +00:00
|
|
|
// update view with 'No styles installed for this site' message
|
|
|
|
if (document.getElementById('installed').children.length === 0) {
|
|
|
|
showStyles([]);
|
|
|
|
}
|
2017-03-13 15:48:00 +00:00
|
|
|
});
|
2017-03-13 12:58:35 +00:00
|
|
|
}
|
|
|
|
//
|
|
|
|
if (cmd) {
|
|
|
|
document.getElementById('confirm').dataset.display = false;
|
|
|
|
}
|
|
|
|
});
|
2012-08-20 01:14:33 +00:00
|
|
|
|
2017-03-19 22:58:10 +00:00
|
|
|
function getClickedStyleId(event) {
|
|
|
|
const entry = event.target.closest('.entry');
|
2017-03-19 21:24:29 +00:00
|
|
|
return entry ? entry.styleId : null;
|
2012-08-20 01:14:33 +00:00
|
|
|
}
|
|
|
|
|
2015-01-30 18:35:37 +00:00
|
|
|
function openLinkInTabOrWindow(event) {
|
|
|
|
event.preventDefault();
|
2015-10-05 11:27:17 +00:00
|
|
|
if (prefs.get("openEditInWindow", false)) {
|
2015-03-11 00:00:44 +00:00
|
|
|
var options = {url: event.target.href}
|
2015-10-05 11:27:17 +00:00
|
|
|
var wp = prefs.get("windowPosition", {});
|
2015-03-11 00:00:44 +00:00
|
|
|
for (var k in wp) options[k] = wp[k];
|
|
|
|
chrome.windows.create(options);
|
2015-01-30 18:35:37 +00:00
|
|
|
} else {
|
2015-03-04 02:06:43 +00:00
|
|
|
openLink(event);
|
2015-01-30 18:35:37 +00:00
|
|
|
}
|
2015-05-10 16:46:09 +00:00
|
|
|
close();
|
2015-01-30 18:35:37 +00:00
|
|
|
}
|
|
|
|
|
2017-03-19 21:24:29 +00:00
|
|
|
function openEditorOnMiddleclick(event) {
|
|
|
|
if (event.button != 1) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// open an editor on middleclick
|
|
|
|
if (event.target.matches('.entry, .style-name, .style-edit-link')) {
|
|
|
|
this.querySelector('.style-edit-link').click();
|
|
|
|
event.preventDefault();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// prevent the popup being opened in a background tab
|
|
|
|
// when an irrelevant link was accidentally clicked
|
|
|
|
if (event.target.closest('a')) {
|
|
|
|
event.preventDefault();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-08-20 01:14:33 +00:00
|
|
|
function openLink(event) {
|
2013-07-04 22:12:16 +00:00
|
|
|
event.preventDefault();
|
2016-01-30 23:08:10 +00:00
|
|
|
chrome.runtime.sendMessage({method: "openURL", url: event.target.href});
|
2015-03-13 22:45:38 +00:00
|
|
|
close();
|
2012-08-20 01:14:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function handleUpdate(style) {
|
2015-03-24 14:07:59 +00:00
|
|
|
var styleElement = installed.querySelector("[style-id='" + style.id + "']");
|
|
|
|
if (styleElement) {
|
|
|
|
installed.replaceChild(createStyleElement(style), styleElement);
|
2015-05-08 00:48:09 +00:00
|
|
|
} else {
|
2015-05-21 09:34:44 +00:00
|
|
|
getActiveTabRealURL(function(url) {
|
|
|
|
if (chrome.extension.getBackgroundPage().getApplicableSections(style, url).length) {
|
2015-05-08 00:48:09 +00:00
|
|
|
// a new style for the current url is installed
|
|
|
|
document.getElementById("unavailable").style.display = "none";
|
|
|
|
installed.appendChild(createStyleElement(style));
|
|
|
|
}
|
|
|
|
});
|
2015-03-24 14:07:59 +00:00
|
|
|
}
|
2012-08-20 01:14:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function handleDelete(id) {
|
2015-03-24 14:07:59 +00:00
|
|
|
var styleElement = installed.querySelector("[style-id='" + id + "']");
|
|
|
|
if (styleElement) {
|
|
|
|
installed.removeChild(styleElement);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-31 00:06:04 +00:00
|
|
|
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
|
2015-03-24 14:07:59 +00:00
|
|
|
if (request.method == "updatePopup") {
|
|
|
|
switch (request.reason) {
|
2015-05-08 00:48:09 +00:00
|
|
|
case "styleAdded":
|
2015-03-24 14:07:59 +00:00
|
|
|
case "styleUpdated":
|
|
|
|
handleUpdate(request.style);
|
|
|
|
break;
|
|
|
|
case "styleDeleted":
|
|
|
|
handleDelete(request.id);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2017-02-28 23:57:03 +00:00
|
|
|
["find-styles-link"].forEach(function(id) {
|
2015-01-30 17:28:05 +00:00
|
|
|
document.getElementById(id).addEventListener("click", openLink, false);
|
|
|
|
});
|
2015-03-17 18:03:20 +00:00
|
|
|
|
|
|
|
document.getElementById("disableAll").addEventListener("change", function(event) {
|
2015-10-05 11:27:17 +00:00
|
|
|
installed.classList.toggle("disabled", prefs.get("disableAll"));
|
2015-03-17 18:03:20 +00:00
|
|
|
});
|
2015-10-05 11:27:17 +00:00
|
|
|
setupLivePrefs(["disableAll"]);
|
2017-02-24 11:04:43 +00:00
|
|
|
|
2017-02-28 23:57:03 +00:00
|
|
|
document.querySelector('#popup-manage-button').addEventListener("click", function() {
|
|
|
|
window.open(chrome.runtime.getURL('manage.html'));
|
|
|
|
});
|
|
|
|
|
2017-02-24 11:04:43 +00:00
|
|
|
document.querySelector('#popup-options-button').addEventListener("click", function() {
|
|
|
|
if (chrome.runtime.openOptionsPage) {
|
|
|
|
// Supported (Chrome 42+)
|
|
|
|
chrome.runtime.openOptionsPage();
|
|
|
|
} else {
|
|
|
|
// Fallback
|
|
|
|
window.open(chrome.runtime.getURL('options/index.html'));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2017-03-16 13:36:33 +00:00
|
|
|
document.querySelector('#popup-shortcuts-button').addEventListener("click", configureCommands.open);
|
2017-03-06 20:17:32 +00:00
|
|
|
|
|
|
|
// popup width
|
2017-03-08 13:11:26 +00:00
|
|
|
document.body.style.width = (localStorage.getItem('popupWidth') || '246') + 'px';
|