diff --git a/.eslintrc b/.eslintrc index 3d844c56..6eb52b33 100644 --- a/.eslintrc +++ b/.eslintrc @@ -55,7 +55,8 @@ globals: animateElement: false $: false $$: false - $element: false + $create: false + $createLink: false # prefs.js prefs: false setupLivePrefs: false @@ -236,7 +237,7 @@ rules: one-var: [0] operator-assignment: [2, always] operator-linebreak: [2, after, overrides: {"?": ignore, ":": ignore, "&&": ignore, "||": ignore}] - padded-blocks: [2, never] + padded-blocks: [0] prefer-numeric-literals: [2] prefer-rest-params: [0] prefer-const: [1, {destructuring: any, ignoreReadBeforeAssign: true}] diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 6128364a..d57ea719 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -377,6 +377,10 @@ "message": "Open this style on userstyles.org to customize via 'Advanced Style Settings'", "description": "Tooltip for a button that opens style on userstyles.org for customizing" }, + "filteredStylesAllHidden": { + "message": "Currently applied filters match no styles", + "description": "Text shown when no styles match currently applied filter in the style manager" + }, "findStylesForSite": { "message": "Find styles", "description": "Text for a link that gets a list of styles for the current site" @@ -774,6 +778,10 @@ "message": "Invalid regexps skipped", "description": "RegExp test report: label for the invalid expressions" }, + "styleRegexpTestNote": { + "message": "Note: use a single \\ for escaping in the regexp input field, which will be automatically converted to \\\\ in the style code as per specification for quoted strings in CSS.", + "description": "RegExp test report: a note displayed at the bottom of the dialog" + }, "styleRegexpPartialExplanation": { "message": "This style uses partially matching regexps in violation of CSS4 @document specification which requires a full URL match. The affected CSS sections were not applied to the page. This style was probably created in Stylish-for-Chrome which incorrectly checks 'regexp()' rules since the very first version (known bug)." }, diff --git a/_locales/zh_TW/messages.json b/_locales/zh_TW/messages.json index eb2bf237..0c6fd026 100644 --- a/_locales/zh_TW/messages.json +++ b/_locales/zh_TW/messages.json @@ -36,7 +36,7 @@ "description": "Label for the button to export a style ('edit' page) or all styles ('manage' page)" }, "installButton": { - "message": "安裝", + "message": "安裝樣式", "description": "Label for install button" }, "styleMetaErrorCheckbox": { @@ -47,6 +47,10 @@ "message": "無效的 JSON 格式", "description": "Setting linter config with invalid JSON" }, + "popupHotkeysTooltip": { + "message": "點選以檢視可用的快捷鍵", + "description": "Tooltip displayed when hovering the right edge of the extension popup" + }, "optionsBadgeNormal": { "message": "背景顏色", "description": "" @@ -330,6 +334,10 @@ "message": "顯示生效的樣式數目", "description": "Label (must be very short) for the checkbox in the toolbar button context menu controlling toolbar badge text." }, + "popupHotkeysInfo": { + "message": "<1>-<9>、<0>,數字鍵盤也可以 — 切換第 N 個樣式(0 是切換到第 10 個)\n- 切換以該字母為名稱第一個字的樣式\n 開啟編輯器而非切換\n 啟用列出的樣式\n 停用列出的樣式\n 與 <`> (倒引號)— 切換初始啟用的樣式;不要在彈出式視窗開啟時套用到後來啟用的樣式,這樣您就可以在測試完後復原初始選擇:僅停用全部,然後切換。 \n更多資訊請見 wiki", + "description": "NOTE1: preserve < and > symbols so that is styled as a key.\nNOTE2: the last line is displayed as a text of the link to the wiki page.\nNOTE3: this is the list of hotkeys displayed after clicking the right edge of the extension popup." + }, "cm_lineWrapping": { "message": "自動換行", "description": "Label for the checkbox controlling word wrap option for the style editor." @@ -934,7 +942,7 @@ "description": "Label for the checkbox in the style editor." }, "installButtonReinstall": { - "message": "重新安裝", + "message": "重新安裝樣式", "description": "Label for reinstall button" }, "linterInvalidConfigError": { @@ -1036,7 +1044,7 @@ "description": "" }, "installButtonUpdate": { - "message": "更新", + "message": "更新樣式", "description": "Label for update button" }, "backupButtons": { @@ -1052,7 +1060,7 @@ "description": "Label for the button to go to the edit style page" }, "installButtonInstalled": { - "message": "已安裝", + "message": "樣式已安裝", "description": "Text displayed when the style is successfully installed" }, "author": { diff --git a/background/storage.js b/background/storage.js index eee6a011..3d728473 100644 --- a/background/storage.js +++ b/background/storage.js @@ -238,6 +238,9 @@ function getStyles(options) { cachedStyles.byId.clear(); for (const style of cachedStyles.list) { cachedStyles.byId.set(style.id, style); + if (!style.name) { + style.name = 'ID: ' + style.id; + } } cachedStyles.mutex.inProgress = false; diff --git a/background/update.js b/background/update.js index e9018d55..945db534 100644 --- a/background/update.js +++ b/background/update.js @@ -56,26 +56,26 @@ var updater = { 'ignoreDigest' option is set on the second manual individual update check on the manage page. */ - const maybeUpdate = style.usercssData ? maybeUpdateUsercss : maybeUpdateUSO; - return (ignoreDigest ? Promise.resolve() : calcStyleDigest(style)) - .then(checkIfEdited) - .then(maybeUpdate) - .then(maybeValidate) + return Promise.resolve(style) + .then([calcStyleDigest][!ignoreDigest ? 0 : 'skip']) + .then([checkIfEdited][!ignoreDigest ? 0 : 'skip']) + .then([maybeUpdateUSO, maybeUpdateUsercss][style.usercssData ? 1 : 0]) .then(maybeSave) - .then(saved => { - observer(updater.UPDATED, saved); - updater.log(updater.UPDATED + ` #${saved.id} ${saved.name}`); - }) - .catch(err => { - observer(updater.SKIPPED, style, err); - err = err === 0 ? 'server unreachable' : err; - updater.log(updater.SKIPPED + ` (${err}) #${style.id} ${style.name}`); - }); + .then(reportSuccess) + .catch(reportFailure); + + function reportSuccess(saved) { + observer(updater.UPDATED, saved); + updater.log(updater.UPDATED + ` #${saved.id} ${saved.name}`); + } + + function reportFailure(err) { + observer(updater.SKIPPED, style, err); + err = err === 0 ? 'server unreachable' : err; + updater.log(updater.SKIPPED + ` (${err}) #${style.id} ${style.name}`); + } function checkIfEdited(digest) { - if (ignoreDigest) { - return; - } if (style.originalDigest && style.originalDigest !== digest) { return Promise.reject(updater.EDITED); } @@ -95,6 +95,7 @@ var updater = { } function maybeUpdateUsercss() { + // TODO: when sourceCode is > 100kB use http range request(s) for version check return download(style.updateUrl).then(text => { const json = usercss.buildMeta(text); const {usercssData: {version}} = style; @@ -104,6 +105,8 @@ var updater = { // re-install is invalid in a soft upgrade if (!ignoreDigest) { return Promise.reject(updater.SAME_VERSION); + } else if (text === style.sourceCode) { + return Promise.reject(updater.SAME_CODE); } break; case 1: @@ -114,38 +117,31 @@ var updater = { }); } - function maybeValidate(json) { - if (json.usercssData) { - // usercss is already validated while building - return json; - } - if (!styleJSONseemsValid(json)) { + function maybeSave(json = {}) { + // usercss is already validated while building + if (!json.usercssData && !styleJSONseemsValid(json)) { return Promise.reject(updater.ERROR_JSON); } - return json; - } - - function maybeSave(json) { json.id = style.id; json.updateDate = Date.now(); + json.reason = 'update'; + // keep current state + delete json.enabled; + // keep local name customizations + delete json.name; + if (styleSectionsEqual(json, style)) { - // JSONs may have different order of items even if sections are effectively equal - // so we'll update the digest anyway - // always update digest even if (save === false) + // update digest even if save === false as there might be just a space added etc. saveStyle(Object.assign(json, {reason: 'update-digest'})); return Promise.reject(updater.SAME_CODE); } else if (!style.originalDigest && !ignoreDigest) { return Promise.reject(updater.MAYBE_EDITED); } - if (!save) { - return json; - } - json.reason = 'update'; - if (json.usercssData) { - return usercssHelper.save(json); - } - json.name = null; // keep local name customizations - return saveStyle(json); + + return !save ? json : + json.usercssData + ? usercssHelper.save(json) + : saveStyle(json); } function styleJSONseemsValid(json) { diff --git a/content/apply.js b/content/apply.js index e2f52124..31320aa7 100644 --- a/content/apply.js +++ b/content/apply.js @@ -386,6 +386,7 @@ let restorationCounter = 0; let observing = false; let sorting = false; + let timer; // allow any types of elements between ours, except for the following: const ORDERED_TAGS = ['head', 'body', 'style', 'link']; @@ -395,6 +396,10 @@ function init() { docRootObserver = new MutationObserver(sortStyleElements); Object.assign(docRootObserver, {start, stop}); + if (!chrome.app) { + // compensate for Firefox missing a step when loading the tab in background + setTimeout(sortStyleElements); + } } function start({sort = false} = {}) { if (sort && sortStyleMap()) { @@ -453,8 +458,8 @@ if (sorting) { sorting = false; docRootObserver.takeRecords(); - setTimeout(start); - //docRootObserver.start(); + clearTimeout(timer); + timer = setTimeout(start); } } function isMovable(el) { diff --git a/edit.html b/edit.html index 008a8cc7..7efc20ce 100644 --- a/edit.html +++ b/edit.html @@ -3,6 +3,9 @@ + + +