From 2f4da37fdb1fe036a916bf54c6f26612d35858e0 Mon Sep 17 00:00:00 2001 From: tophf Date: Tue, 21 Mar 2017 04:32:38 +0300 Subject: [PATCH] Refactor and speed up popup & manager Popup: * Enforce 200-800px range for the popup width option Manage: * faster search via cachedStyles.byId * faster restoration of search results on history nav * style name is clickable and opens the editor * animated highlight of style element on update/add/save * expandable extra applies-to targets * remember scroll position on normal history navigation * boz-sizing in #header, also in editor * applies-to targets use structured markup * get*Tab*, enableStyle and deleteStyle are promisified --- .eslintrc | 2 + background.js | 17 +- backup/fileSaveLoad.js | 1 + edit.html | 5 +- edit.js | 2 +- health.js | 2 +- manage.css | 284 ++++++++++++++ manage.html | 412 ++++++--------------- manage.js | 819 +++++++++++++++++++++-------------------- messaging.js | 82 ++++- options/index.html | 2 +- options/index.js | 16 +- popup.css | 10 +- popup.html | 8 +- popup.js | 493 +++++++++++++------------ storage.js | 26 +- 16 files changed, 1199 insertions(+), 982 deletions(-) create mode 100644 manage.css diff --git a/.eslintrc b/.eslintrc index 479e9a94..c32b677b 100644 --- a/.eslintrc +++ b/.eslintrc @@ -34,6 +34,8 @@ globals: getType: true importStyles: true getActiveTabRealURL: true + openURL: true + onDOMready: true getDomains: true webSqlStorage: true notifyAllTabs: true diff --git a/background.js b/background.js index b7915c76..79940789 100644 --- a/background.js +++ b/background.js @@ -1,4 +1,4 @@ -/* globals wildcardAsRegExp, KEEP_CHANNEL_OPEN */ +/* globals openURL, wildcardAsRegExp, KEEP_CHANNEL_OPEN */ // This happens right away, sometimes so fast that the content script isn't even ready. That's // why the content script also asks for this stuff. @@ -149,21 +149,6 @@ chrome.tabs.onAttached.addListener(function(tabId, data) { }); }); -function openURL(options) { - chrome.tabs.query({currentWindow: true, url: options.url}, function(tabs) { - // switch to an existing tab with the requested url - if (tabs.length) { - chrome.tabs.highlight({windowId: tabs[0].windowId, tabs: tabs[0].index}, function (window) {}); - } else { - delete options.method; - getActiveTab(function(tab) { - // re-use an active new tab page - chrome.tabs[tab.url == "chrome://newtab/" ? "update" : "create"](options); - }); - } - }); -} - var codeMirrorThemes; getCodeMirrorThemes(function(themes) { codeMirrorThemes = themes; diff --git a/backup/fileSaveLoad.js b/backup/fileSaveLoad.js index 2c4736a7..89db6fc9 100644 --- a/backup/fileSaveLoad.js +++ b/backup/fileSaveLoad.js @@ -64,6 +64,7 @@ function importFromString(jsonString) { }); } else { refreshAllTabs().then(() => { + scrollTo(0, 0); setTimeout(alert, 100, numStyles + ' styles installed/updated'); resolve(numStyles); }); diff --git a/edit.html b/edit.html index 7cde2c7b..2d9e8d06 100644 --- a/edit.html +++ b/edit.html @@ -45,14 +45,15 @@ } /************ header ************/ #header { - height: calc(100vh - 30px); + width: 280px; + height: 100vh; overflow: auto; - width: 250px; position: fixed; top: 0; padding: 15px; border-right: 1px dashed #AAA; -webkit-box-shadow: 0 0 3rem -1.2rem black; + box-sizing: border-box; } #header h1 { margin-top: 0; diff --git a/edit.js b/edit.js index a17fabdb..a2043067 100644 --- a/edit.js +++ b/edit.js @@ -415,7 +415,7 @@ chrome.tabs.query({currentWindow: true}, function(tabs) { }); }); -getActiveTab(function(tab) { +getActiveTab().then(tab => { useHistoryBack = sessionStorageHash("manageStylesHistory").value[tab.id] == location.href; }); diff --git a/health.js b/health.js index 857e0736..727fa4ad 100644 --- a/health.js +++ b/health.js @@ -1,4 +1,4 @@ -healthCheck(); +setTimeout(healthCheck, 0); function healthCheck() { chrome.runtime.sendMessage({method: "healthCheck"}, function(ok) { diff --git a/manage.css b/manage.css new file mode 100644 index 00000000..b83669de --- /dev/null +++ b/manage.css @@ -0,0 +1,284 @@ +body { + margin: 0; + font: 12px arial, sans-serif; +} + +a, +a:visited { + color: inherit; + opacity: .75; + -webkit-transition: opacity 0.5s; +} + +a:hover, +a.homepage:hover { + opacity: .6; +} + +a.homepage { + opacity: 1; +} + +#header { + width: 280px; + height: 100vh; + position: fixed; + top: 0; + padding: 15px; + border-right: 1px dashed #AAA; + -webkit-box-shadow: 0 0 50px -18px black; + overflow: auto; + box-sizing: border-box; +} + +#header h1 { + margin-top: 0; +} + +#installed { + position: relative; + margin-left: 280px; +} + +.entry { + margin: 0; + padding: 1.25em 2em 1.5em; + border-top: 1px solid #ddd; +} + +.entry:first-child { + border-top: none; +} + +.svg-icon { + cursor: pointer; + vertical-align: middle; + margin-left: 0.3rem; + margin-right: 0.3rem; + margin-top: -4px; + transition: opacity .5s; + width: 16px; + height: 16px; + fill: currentColor; +} + +.style-name { + margin-top: .25em; +} + +.style-name a, .style-edit-link { + text-decoration: none; + color: inherit; +} + +.applies-to { + word-break: break-word; +} + +.applies-to, +.actions { + padding-left: 15px; + margin-bottom: 0; +} + +.applies-to > :first-child { + margin-right: .5ex; +} + +.applies-to .target:hover { + background-color: rgba(128, 128, 128, .15); +} + +.applies-to-extra { + display: inline; +} + +.applies-to-extra summary { + font-weight: bold; + cursor: pointer; + list-style-type: none; /* for FF, allegedly */ +} + +.applies-to-extra summary::-webkit-details-marker { + display: none; +} + +.disabled h2::after { + content: " (Disabled)"; +} + +.disabled { + opacity: 0.5; +} + +.disabled .disable { + display: none; +} + +.enabled .enable { + display: none; +} + +/* Default, no update buttons */ + +.update, +.check-update { + display: none; +} + +/* Check update button for things that can*/ + +*[style-update-url] .check-update { + display: inline; +} + +/* Update check in progress */ + +.checking-update .check-update { + display: none; +} + +/* Updates available */ + +.can-update .update { + display: inline; +} + +.can-update .check-update { + display: none; +} + +/* Updates not available */ + +.no-update .check-update { + display: none; +} + +/* Updates done */ + +.update-done .check-update { + display: none; +} + +.hidden { + display: none +} + +fieldset { + border-width: 1px; + border-radius: 6px; + margin: 1em 0; +} + +.enabled-only > .disabled, +.edited-only > [style-update-url] { + display: none; +} + +#search { + width: calc(100% - 4px); + margin: 0.25rem 4px 0; + border-radius: 0.25rem; + padding-left: 0.25rem; + border-width: 1px; +} + +#import ul { + margin-left: 0; + padding-left: 0; + list-style: none; +} + +#import li { + margin-bottom: .5em; +} + +#import pre { + background: #eee; + overflow: auto; + margin: 0 0 .5em 0; +} + +/* drag-n-drop on import button */ +.dropzone:after { + background-color: rgba(0, 0, 0, 0.7); + color: white; + left: 0; + top: 0; + right: 0; + bottom: 0; + z-index: 1000; + position: fixed; + padding: calc(50vh - 3em) calc(50vw - 5em); + content: attr(dragndrop-hint); + text-shadow: 1px 1px 10px black; + font-size: xx-large; + text-align: center; + animation: fadein 1s cubic-bezier(.03, .67, .08, .94); + animation-fill-mode: both; +} + +.fadeout.dropzone:after { + animation: fadeout .25s ease-in-out; + animation-fill-mode: both; +} + +@keyframes fadein { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +@keyframes fadeout { + from { + opacity: 1; + } + to { + opacity: 0; + } +} + +@media (max-width: 675px) { + #header { + height: auto; + position: static; + width: auto; + border-right: none; + border-bottom: 1px dashed #AAA; + } + + #installed { + position: static; + margin-left: 0; + overflow: visible; + } + + #header h1, + #header h2, + #header h3, + #backup-message { + display: none; + } + + #header p, + #header fieldset div, + #backup { + display: inline-block; + } + + #backup { + margin-right: 1em; + } + + #backup p, + #header fieldset { + margin: 0; + } + + .entry { + margin: 0; + } +} diff --git a/manage.html b/manage.html index 6a5e4a15..ea2629d6 100644 --- a/manage.html +++ b/manage.html @@ -1,312 +1,128 @@ - + - - - + + + - - - - - - - - - + + + + + + + + + + + + + + + - + + + @@ -75,11 +80,10 @@ - diff --git a/popup.js b/popup.js index 75b0ccb8..3fe9fbf3 100644 --- a/popup.js +++ b/popup.js @@ -1,275 +1,298 @@ -/* globals configureCommands */ +/* globals configureCommands, openURL */ -var writeStyleTemplate = document.createElement("a"); -writeStyleTemplate.className = "write-style-link"; +const RX_SUPPORTED_URLS = new RegExp( + `^(file|https?|ftps?):|^${OWN_ORIGIN}`); +let installed; -var installed = document.getElementById("installed"); -if (!prefs.get("popup.stylesFirst")) { - document.body.insertBefore(document.querySelector("body > .actions"), installed); +getActiveTabRealURL().then(url => { + const isUrlSupported = RX_SUPPORTED_URLS.test(url); + Promise.all([ + isUrlSupported ? getStylesSafe({matchUrl: url}) : null, + onDOMready().then(() => initPopup(isUrlSupported ? url : '')), + ]) + .then(([styles]) => styles && showStyles(styles)); +}); + + +chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => { + if (msg.method == 'updatePopup') { + switch (msg.reason) { + case 'styleAdded': + case 'styleUpdated': + handleUpdate(msg.style); + break; + case 'styleDeleted': + handleDelete(msg.id); + break; + } + } +}); + + +function initPopup(url) { + installed = $('#installed'); + + // popup width + document.body.style.width = + Math.max(200, Math.min(800, Number(localStorage.popupWidth) || 246)) + 'px'; + + // confirm dialog + $('#confirm').onclick = e => { + const cmd = e.target.dataset.cmd; + if (cmd === 'ok') { + deleteStyle($('#confirm').dataset.id).then(() => { + // update view with 'No styles installed for this site' message + if ($('#installed').children.length === 0) { + showStyles([]); + } + }); + } + // + if (cmd) { + $('#confirm').dataset.display = false; + } + }; + + // action buttons + $('#disableAll').onchange = () => + installed.classList.toggle('disabled', prefs.get('disableAll')); + setupLivePrefs(['disableAll']); + $('#find-styles-link').onclick = openURLandHide; + $('#popup-manage-button').href = 'manage.html'; + $('#popup-manage-button').onclick = openURLandHide; + $('#popup-options-button').onclick = () => chrome.runtime.openOptionsPage(); + $('#popup-shortcuts-button').onclick = configureCommands.open; + + // styles first? + if (!prefs.get('popup.stylesFirst')) { + document.body.insertBefore( + $('body > .actions'), + installed); + } + + // find styles link + $('#find-styles a').href = + 'https://userstyles.org/styles/browse/all/' + + encodeURIComponent(url.startsWith('file:') ? 'file:' : url); + + if (!url) { + document.body.classList.add('blocked'); + return; + } + + // Write new style links + const writeStyle = $('#write-style'); + const matchTargets = document.createElement('span'); + matchTargets.id = 'match'; + + // For this URL + const urlLink = template.writeStyle.cloneNode(true); + Object.assign(urlLink, { + href: 'edit.html?url-prefix=' + encodeURIComponent(url), + title: `url-prefix("${url}")`, + textContent: prefs.get('popup.breadcrumbs.usePath') + ? new URL(url).pathname.slice(1) + : t('writeStyleForURL').replace(/ /g, '\u00a0'), // this URL + onclick: openLinkInTabOrWindow, + }); + if (prefs.get('popup.breadcrumbs')) { + urlLink.onmouseenter = + urlLink.onfocus = () => urlLink.parentNode.classList.add('url()'); + urlLink.onmouseleave = + urlLink.onblur = () => urlLink.parentNode.classList.remove('url()'); + } + matchTargets.appendChild(urlLink); + + // For domain + const domains = getDomains(url); + for (let domain of domains) { + // Don't include TLD + if (domains.length > 1 && !domain.includes('.')) { + continue; + } + const domainLink = template.writeStyle.cloneNode(true); + Object.assign(domainLink, { + href: 'edit.html?domain=' + encodeURIComponent(domain), + textContent: domain, + title: `domain("${domain}")`, + onclick: openLinkInTabOrWindow, + }); + domainLink.setAttribute('subdomain', domain.substring(0, domain.indexOf('.'))); + matchTargets.appendChild(domainLink); + } + + if (prefs.get('popup.breadcrumbs')) { + matchTargets.classList.add('breadcrumbs'); + matchTargets.appendChild(matchTargets.removeChild(matchTargets.firstElementChild)); + } + writeStyle.appendChild(matchTargets); } -getActiveTabRealURL(updatePopUp); - -function updatePopUp(url) { - var urlWillWork = /^(file|http|https|ftps?|chrome\-extension):/.exec(url); - if (!urlWillWork) { - document.body.classList.add("blocked"); - document.getElementById("unavailable").style.display = "flex"; - return; - } - - getStylesSafe({matchUrl: url}).then(showStyles); - - document.querySelector("#find-styles a").href = "https://userstyles.org/styles/browse/all/" + encodeURIComponent("file" === urlWillWork[1] ? "file:" : url); - - // Write new style links - var writeStyleLinks = [], - container = document.createElement('span'); - container.id = "match"; - - // For this URL - var urlLink = writeStyleTemplate.cloneNode(true); - urlLink.href = "edit.html?url-prefix=" + encodeURIComponent(url); - urlLink.appendChild(document.createTextNode( // switchable; default="this URL" - !prefs.get("popup.breadcrumbs.usePath") - ? t("writeStyleForURL").replace(/ /g, "\u00a0") - : /\/\/[^/]+\/(.*)/.exec(url)[1] - )); - urlLink.title = "url-prefix(\"$\")".replace("$", url); - writeStyleLinks.push(urlLink); - document.querySelector("#write-style").appendChild(urlLink) - if (prefs.get("popup.breadcrumbs")) { // switchable; default=enabled - 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); - } - - // For domain - var domains = getDomains(url) - 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)); - domainLink.title = "domain(\"$\")".replace("$", domain); - domainLink.setAttribute("subdomain", domain.substring(0, domain.indexOf("."))); - writeStyleLinks.push(domainLink); - }); - - var writeStyle = document.querySelector("#write-style"); - writeStyleLinks.forEach(function(link, index) { - link.addEventListener("click", openLinkInTabOrWindow, false); - container.appendChild(link); - }); - if (prefs.get("popup.breadcrumbs")) { - container.classList.add("breadcrumbs"); - container.appendChild(container.removeChild(container.firstChild)); - } - writeStyle.appendChild(container); -} function showStyles(styles) { - var enabledFirst = prefs.get("popup.enabledFirst"); - styles.sort(function(a, b) { - if (enabledFirst && a.enabled !== b.enabled) return !(a.enabled < b.enabled) ? -1 : 1; - return a.name.localeCompare(b.name); - }); - if (styles.length == 0) { - installed.innerHTML = "
" + t('noStylesForSite') + "
"; - } - styles.map(createStyleElement).forEach(function(e) { - installed.appendChild(e); - }); - // force Chrome to resize the popup - document.body.style.height = '10px'; - document.documentElement.style.height = '10px'; + if (!styles.length) { + installed.innerHTML = + `
${t('noStylesForSite')}
`; + } else { + const enabledFirst = prefs.get('popup.enabledFirst'); + styles.sort((a, b) => + enabledFirst && a.enabled !== b.enabled + ? !(a.enabled < b.enabled) ? -1 : 1 + : a.name.localeCompare(b.name)); + const fragment = document.createDocumentFragment(); + for (let style of styles) { + fragment.appendChild(createStyleElement(style)); + } + installed.appendChild(fragment); + } + // force Chrome to resize the popup + document.body.style.height = '10px'; + document.documentElement.style.height = '10px'; } + function createStyleElement(style) { - // reuse event function references - createStyleElement.events = createStyleElement.events || { - checkboxClick() { - enableStyle(getClickedStyleId(event), this.checked); - }, - styleNameClick(event) { - this.checkbox.click(); - event.preventDefault(); - }, - toggleClick(event) { - enableStyle(getClickedStyleId(event), this.matches('.enable')); - }, - deleteClick() { - doDelete(event); + // reuse event listener function references + const listeners = createStyleElement.listeners = createStyleElement.listeners || { + checkboxClick() { + enableStyle(getClickedStyleId(event), this.checked) + .then(handleUpdate); + }, + styleNameClick(event) { + this.checkbox.click(); + event.preventDefault(); + }, + toggleClick(event) { + enableStyle(getClickedStyleId(event), this.matches('.enable')) + .then(handleUpdate); + }, + deleteClick(event) { + doDelete(event); } - }; - 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 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 checkbox = $('.checker', entry); + Object.assign(checkbox, { + id: 'style-' + style.id, + checked: style.enabled, + onclick: listeners.checkboxClick, + }); - const editLink = entry.querySelector('.style-edit-link'); - Object.assign(editLink, { - href: editLink.getAttribute('href') + style.id, - onclick: openLinkInTabOrWindow, - }); + const editLink = $('.style-edit-link', entry); + 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, - }); - styleName.checkbox = checkbox; - styleName.appendChild(document.createTextNode(style.name)); + const styleName = $('.style-name', entry); + Object.assign(styleName, { + htmlFor: 'style-' + style.id, + onclick: listeners.styleNameClick, + }); + styleName.checkbox = checkbox; + styleName.appendChild(document.createTextNode(style.name)); - entry.querySelector('.enable').onclick = createStyleElement.events.toggleClick; - entry.querySelector('.disable').onclick = createStyleElement.events.toggleClick; - entry.querySelector('.delete').onclick = createStyleElement.events.deleteClick; + $('.enable', entry).onclick = listeners.toggleClick; + $('.disable', entry).onclick = listeners.toggleClick; + $('.delete', entry).onclick = listeners.deleteClick; - return entry; + return entry; } + function doDelete(event) { - document.getElementById('confirm').dataset.display = true; - const id = getClickedStyleId(event); - document.querySelector('#confirm b').textContent = - document.querySelector(`[style-id="${id}"] label`).textContent; - document.getElementById('confirm').dataset.id = id; + $('#confirm').dataset.display = true; + const id = getClickedStyleId(event); + $('#confirm b').textContent = + $(`[style-id="${id}"] label`).textContent; + $('#confirm').dataset.id = id; } -document.getElementById('confirm').addEventListener('click', e => { - let cmd = e.target.dataset.cmd; - if (cmd === 'ok') { - deleteStyle(document.getElementById('confirm').dataset.id, () => { - // update view with 'No styles installed for this site' message - if (document.getElementById('installed').children.length === 0) { - showStyles([]); - } - }); - } - // - if (cmd) { - document.getElementById('confirm').dataset.display = false; - } -}); function getClickedStyleId(event) { - const entry = event.target.closest('.entry'); - return entry ? entry.styleId : null; + const entry = event.target.closest('.entry'); + return entry ? entry.styleId : null; } + function openLinkInTabOrWindow(event) { - event.preventDefault(); - if (prefs.get("openEditInWindow", false)) { - var options = {url: event.target.href} - var wp = prefs.get("windowPosition", {}); - for (var k in wp) options[k] = wp[k]; - chrome.windows.create(options); - } else { - openLink(event); - } - close(); + if (!prefs.get('openEditInWindow', false)) { + openURLandHide(event); + return; + } + event.preventDefault(); + chrome.windows.create( + Object.assign({ + url: event.target.href + }, prefs.get('windowPosition', {})) + ); + close(); } + 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; - } + if (event.button != 1) { + return; + } + // open an editor on middleclick + if (event.target.matches('.entry, .style-name, .style-edit-link')) { + $('.style-edit-link', this).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; + } } -function openLink(event) { - event.preventDefault(); - chrome.runtime.sendMessage({method: "openURL", url: event.target.href}); - close(); + +function openURLandHide(event) { + event.preventDefault(); + openURL({url: event.target.href}) + .then(close); } + function handleUpdate(style) { - var styleElement = installed.querySelector("[style-id='" + style.id + "']"); - if (styleElement) { - installed.replaceChild(createStyleElement(style), styleElement); - } else { - getActiveTabRealURL(function(url) { - if (chrome.extension.getBackgroundPage().getApplicableSections(style, url).length) { - // a new style for the current url is installed - document.getElementById("unavailable").style.display = "none"; - installed.appendChild(createStyleElement(style)); - } - }); - } + const styleElement = $(`[style-id="${style.id}"]`, installed); + if (styleElement) { + installed.replaceChild(createStyleElement(style), styleElement); + } else { + getActiveTabRealURL().then(url => { + if (getApplicableSections(style, url).length) { + // a new style for the current url is installed + $('#unavailable').style.display = 'none'; + installed.appendChild(createStyleElement(style)); + } + }); + } } + function handleDelete(id) { - var styleElement = installed.querySelector("[style-id='" + id + "']"); - if (styleElement) { - installed.removeChild(styleElement); - } + var styleElement = $(`[style-id="${id}"]`, installed); + if (styleElement) { + installed.removeChild(styleElement); + } } -chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) { - if (request.method == "updatePopup") { - switch (request.reason) { - case "styleAdded": - case "styleUpdated": - handleUpdate(request.style); - break; - case "styleDeleted": - handleDelete(request.id); - break; - } - } -}); -["find-styles-link"].forEach(function(id) { - document.getElementById(id).addEventListener("click", openLink, false); -}); - -document.getElementById("disableAll").addEventListener("change", function(event) { - installed.classList.toggle("disabled", prefs.get("disableAll")); -}); -setupLivePrefs(["disableAll"]); - -document.querySelector('#popup-manage-button').addEventListener("click", function() { - window.open(chrome.runtime.getURL('manage.html')); -}); - -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')); - } -}); - -document.querySelector('#popup-shortcuts-button').addEventListener("click", configureCommands.open); - -// popup width -document.body.style.width = (localStorage.getItem('popupWidth') || '246') + 'px'; +function $(selector, base = document) { + if (selector.startsWith('#') && /^#[^,\s]+$/.test(selector)) { + return document.getElementById(selector.slice(1)); + } else { + return base.querySelector(selector); + } +} diff --git a/storage.js b/storage.js index 9d7fd5cd..4908aec7 100644 --- a/storage.js +++ b/storage.js @@ -277,23 +277,21 @@ function addMissingStyleTargets(style) { function enableStyle(id, enabled) { - saveStyle({id, enabled}) - .then(handleUpdate); + return saveStyle({id, enabled}); } -function deleteStyle(id, callback = function (){}) { - getDatabase(function(db) { - var tx = db.transaction(["styles"], "readwrite"); - var os = tx.objectStore("styles"); - var request = os.delete(Number(id)); - request.onsuccess = function(event) { - handleDelete(id); - invalidateCache(true, {deletedId: id}); - notifyAllTabs({method: "styleDeleted", id}); - callback(); - }; - }); +function deleteStyle(id) { + return new Promise(resolve => + getDatabase(db => { + const tx = db.transaction(['styles'], 'readwrite'); + const os = tx.objectStore('styles'); + os.delete(Number(id)).onsuccess = event => { + invalidateCache(true, {deletedId: id}); + notifyAllTabs({method: 'styleDeleted', id}); + resolve(id); + }; + })); }