diff --git a/js/usercss.js b/js/usercss.js index 51a44fed..7ff85ad2 100644 --- a/js/usercss.js +++ b/js/usercss.js @@ -4,6 +4,12 @@ // eslint-disable-next-line no-var var usercss = (function () { + const METAS = [ + 'author', 'description', 'homepageURL', 'icon', 'license', 'name', + 'namespace', 'noframes', 'preprocessor', 'supportURL', 'var', 'version' + ]; + + // FIXME: use a real semver module function semverTest(a, b) { a = a.split('.').map(Number); b = b.split('.').map(Number); @@ -27,23 +33,6 @@ var usercss = (function () { return 0; } - function guessType(value) { - if (/^url\(.+\)$/i.test(value)) { - return 'image'; - } - if (/^#[0-9a-f]{3,8}$/i.test(value)) { - return 'color'; - } - if (/^hsla?\(.+\)$/i.test(value)) { - return 'color'; - } - if (/^rgba?\(.+\)$/i.test(value)) { - return 'color'; - } - // should we use a color-name table to guess type? - return 'text'; - } - const BUILDER = { default: { postprocess(sections, vars) { @@ -102,6 +91,70 @@ var usercss = (function () { return style; } + function *parseMetas(source) { + for (const line of source.split(/\r?\n/)) { + const match = line.match(/@(\w+)/); + if (!match) { + continue; + } + yield [match[1], line.slice(match.index + match[0].length).trim()]; + } + } + + function matchString(s) { + const match = matchFollow(s, /^(?:\w+|(['"])(?:\\\1|.)*?\1)/); + match.value = match[1] ? match[0].slice(1, -1) : match[0]; + return match; + } + + function matchFollow(s, re) { + const match = s.match(re); + match.follow = s.slice(match.index + match[0].length).trim(); + return match; + } + + // FIXME: need color converter + function normalizeColor(color) { + return color; + } + + function parseVar(source) { + const result = { + label: null, + name: null, + value: null, + default: null, + select: null + }; + + { + // type & name + const match = matchFollow(source, /^([\w-]+)\s+([\w-]+)/); + ([, result.type, result.name] = match); + source = match.follow; + } + + { + // label + const match = matchString(source); + result.label = match.value; + source = match.follow; + } + + // value + if (result.type === 'color') { + source = normalizeColor(source); + } else if (result.type === 'select') { + const match = matchString(source); + result.select = JSON.parse(match.follow); + source = match.value; + } + + result.default = source; + + return result; + } + function _buildMeta(source) { const style = { name: null, @@ -111,52 +164,27 @@ var usercss = (function () { enabled: true, sections: [], vars: {}, - preprocessor: null + preprocessor: null, + noframes: false }; const metaSource = getMetaSource(source); - const match = (re, callback) => { - let m; - if (!re.global) { - if ((m = metaSource.match(re))) { - if (m.length === 1) { - callback(m[0]); - } else { - callback(...m.slice(1)); - } - } - } else { - const result = []; - while ((m = re.exec(metaSource))) { - if (m.length <= 2) { - result.push(m[m.length - 1]); - } else { - result.push(m.slice(1)); - } - } - if (result.length) { - callback(result); - } + for (const [key, value] of parseMetas(metaSource)) { + if (!METAS.includes(key)) { + continue; } - }; - - // FIXME: finish all metas - match(/@name[^\S\r\n]+(.+?)[^\S\r\n]*$/m, m => (style.name = m)); - match(/@namespace[^\S\r\n]+(\S+)/, m => (style.namespace = m)); - match(/@preprocessor[^\S\r\n]+(\S+)/, m => (style.preprocessor = m)); - match(/@version[^\S\r\n]+(\S+)/, m => (style.version = m)); - match( - /@var[^\S\r\n]+(\S+)[^\S\r\n]+(?:(['"])((?:\\\2|.)*?)\2|(\S+))[^\S\r\n]+(.+?)[^\S\r\n]*$/gm, - ms => ms.forEach(([key,, label1, label2, value]) => ( - style.vars[key] = { - type: guessType(value), - label: label1 || label2, - value: null, // '.value' holds the value set by users. - default: value // '.default' holds the value extract from meta. - } - )) - ); + if (key === 'noframes') { + style.noframes = true; + } else if (key === 'var') { + const va = parseVar(value); + style.vars[va.name] = va; + } else if (key === 'homepageURL') { + style.url = value; + } else { + style[key] = value; + } + } return style; } @@ -212,6 +240,7 @@ var usercss = (function () { throw new Error(chrome.i18n.getMessage('styleMissingMeta', prop)); } } + // FIXME: validate variable formats } return {buildMeta, buildCode, semverTest};