diff --git a/background/update-manager.js b/background/update-manager.js index d2c5dd53..22cf0316 100644 --- a/background/update-manager.js +++ b/background/update-manager.js @@ -115,7 +115,7 @@ const updateMan = (() => { } = opts; if (!id) id = style.id; const {md5Url} = style; - let ucd = style.usercssData; + let {usercssData: ucd, updateUrl} = style; let res, state; try { await checkIfEdited(); @@ -151,9 +151,11 @@ const updateMan = (() => { if (md5 === style.originalMd5 && style.originalDigest && !ignoreDigest) { return Promise.reject(STATES.SAME_MD5); } + let varsUrl = ''; if (!ucd) { - ucd = true; - style.updateUrl = `${URLS.uso}api/v1/styles/${md5Url.match(/\/(\d+)/)[1]}`; + ucd = {}; + varsUrl = updateUrl; + updateUrl = style.updateUrl = `${URLS.uso}api/v1/styles/${md5Url.match(/\/(\d+)/)[1]}`; } usoSpooferStart(); let json; @@ -161,6 +163,7 @@ const updateMan = (() => { json = await tryDownload(style.updateUrl, {responseType: 'json'}); json = await updateUsercss(json.css) || (await API.uso.toUsercss(json)).style; + if (varsUrl) await API.uso.useVarsUrl(json, varsUrl); } finally { usoSpooferStop(); } diff --git a/background/uso-api.js b/background/uso-api.js index 2276d275..ad30347c 100644 --- a/background/uso-api.js +++ b/background/uso-api.js @@ -2,18 +2,31 @@ /* global usercssMan */ 'use strict'; -/* exported usoApi */ -const usoApi = { +const usoApi = {}; + +(() => { + const pingers = {}; + + usoApi.pingback = (usoId, delay) => { + clearTimeout(pingers[usoId]); + delete pingers[usoId]; + if (delay > 0) { + return new Promise(resolve => (pingers[usoId] = setTimeout(ping, delay, usoId, resolve))); + } else if (delay !== false) { + return ping(usoId); + } + }; + /** * Replicating USO-Archive format * https://github.com/33kk/uso-archive/blob/flomaster/lib/uso.js * https://github.com/33kk/uso-archive/blob/flomaster/lib/converters.js */ - async toUsercss(data, {metaOnly = true} = {}) { + usoApi.toUsercss = async (data, {metaOnly = true, varsUrl} = {}) => { const badKeys = {}; const newKeys = []; const descr = JSON.stringify(data.description.trim()); - const vars = (data.style_settings || []).map(makeVar).join(''); + const vars = (data.style_settings || []).map(makeVar, {badKeys, newKeys}).join(''); const sourceCode = `\ /* ==UserStyle== @name ${data.name} @@ -23,102 +36,122 @@ const usoApi = { @author ${(data.user || {}).name || '?'} @license ${makeLicense(data.license)}${vars ? '\n@preprocessor uso' + vars : ''}` .replace(/\*\//g, '*\\/') + - `==/UserStyle== */\n${newKeys[0] ? useNewKeys(data.css) : data.css}`; - const res = await usercssMan.build({sourceCode, metaOnly}); - return Object.assign(res, {badKeys, newKeys}); + `==/UserStyle== */\n${newKeys[0] ? useNewKeys(data.css, badKeys) : data.css}`; + const {style} = await usercssMan.build({sourceCode, metaOnly}); + usoApi.useVarsUrl(style, varsUrl); + return {style, badKeys, newKeys}; + }; - function makeLicense(s) { - return !s ? 'NO-REDISTRIBUTION' : - s === 'publicdomain' ? 'CC0-1.0' : - s.startsWith('ccby') ? `${s.toUpperCase().match(/(..)/g).join('-')}-4.0` : - s; + usoApi.useVarsUrl = (style, url) => { + if (!/\?ik-/.test(url)) { + return; } - - function makeVar({ - label, - setting_type: type, - install_key: ik, - style_setting_options: opts, - }) { - let value, suffix; - ik = makeKey(ik); - label = JSON.stringify(label); - switch (type) { - - case 'color': - value = opts[0].value; - break; - - case 'text': - value = JSON.stringify(opts[0].value); - break; - - case 'image': { - const ikCust = `${ik}-custom`; - opts.push({ - label: 'Custom', - install_key: `${ikCust}-dropdown`, - value: `/*[[${ikCust}]]*/`, - }); - suffix = `\n@advanced text ${ikCust} ${label.slice(0, -1)} (Custom)" "https://foo.com/123.jpg"`; - type = 'dropdown'; - } // fallthrough - - case 'dropdown': - value = ''; - for (const o of opts) { - const def = o.default ? '*' : ''; - const val = o.value; - const s = ` ${makeKey(o.install_key)} ${JSON.stringify(o.label + def)} << a + badKeys[key]); - } - }, -}; - -(() => { - const timers = {}; - - usoApi.pingback = (usoId, delay) => { - clearTimeout(timers[usoId]); - delete timers[usoId]; - if (delay > 0) { - return new Promise(resolve => { - timers[usoId] = setTimeout(ping, delay, usoId, resolve); - }); - } else if (delay !== false) { - return ping(usoId); } + return true; }; function ping(id, resolve) { - return fetch(`${URLS.uso}styles/install/${id}?source=stylish-ch`).then(resolve); + return fetch(`${URLS.uso}styles/install/${id}?source=stylish-ch`) + .then(resolve); + } + + function makeKey(key, {badKeys, newKeys}) { + let res = badKeys[key]; + if (!res) { + res = key.replace(/[^-\w]/g, '-'); + res += newKeys.includes(res) ? '-' : ''; + if (key !== res) { + badKeys[key] = res; + newKeys.push(res); + } + } + return res; + } + + function makeLicense(s) { + return !s ? 'NO-REDISTRIBUTION' : + s === 'publicdomain' ? 'CC0-1.0' : + s.startsWith('ccby') ? `${s.toUpperCase().match(/(..)/g).join('-')}-4.0` : + s; + } + + function makeVar({ + label, + setting_type: type, + install_key: ik, + style_setting_options: opts, + }) { + const cfg = this; + let value, suffix; + ik = makeKey(ik, cfg); + label = JSON.stringify(label); + switch (type) { + + case 'color': + value = opts[0].value; + break; + + case 'text': + value = JSON.stringify(opts[0].value); + break; + + case 'image': { + const ikCust = `${ik}-custom`; + opts.push({ + label: 'Custom', + install_key: `${ikCust}-dropdown`, + value: `/*[[${ikCust}]]*/`, + }); + suffix = `\n@advanced text ${ikCust} ${label.slice(0, -1)} (Custom)" "https://foo.com/123.jpg"`; + type = 'dropdown'; + } // fallthrough + + case 'dropdown': + value = ''; + for (const o of opts) { + const def = o.default ? '*' : ''; + const val = o.value; + const s = ` ${makeKey(o.install_key, cfg)} ${JSON.stringify(o.label + def)} << o.name === name); + } + + function useNewKeys(css, badKeys) { + const rxsKeys = stringAsRegExp(Object.keys(badKeys).join('\n'), '', true).replace(/\n/g, '|'); + const rxUsoVars = new RegExp(`(/\\*\\[\\[)(${rxsKeys})(?=]]\\*/)`, 'g'); + return css.replace(rxUsoVars, (s, a, key) => a + badKeys[key]); } })(); diff --git a/content/install-hook-userstyles.js b/content/install-hook-userstyles.js index c7724b41..786e7ce4 100644 --- a/content/install-hook-userstyles.js +++ b/content/install-hook-userstyles.js @@ -108,7 +108,7 @@ async function buildStyle() { if (!pageData) pageData = await (await fetch(apiUrl)).json(); - ({style, badKeys} = await API.uso.toUsercss(pageData)); + ({style, badKeys} = await API.uso.toUsercss(pageData, {varsUrl: dup.updateUrl})); Object.assign(style, { md5Url, id: dup.id,