From 4e0f4b34bb2bf1d4f0d73a566f2bd9e502d84146 Mon Sep 17 00:00:00 2001 From: eight Date: Wed, 6 Sep 2017 04:26:01 +0800 Subject: [PATCH] Add: colorParser --- js/usercss.js | 45 ++++++++++++++- manage.html | 2 + manage/config-dialog.js | 121 ++++++++++++++++++++++++++++++++++++++ manage/manage.js | 125 +++------------------------------------- 4 files changed, 176 insertions(+), 117 deletions(-) create mode 100644 manage/config-dialog.js diff --git a/js/usercss.js b/js/usercss.js index 551fcd39..7e2f41ad 100644 --- a/js/usercss.js +++ b/js/usercss.js @@ -46,6 +46,49 @@ var usercss = (function () { } }; + const colorParser = (function () { + const el = document.createElement('div'); + // https://bugs.webkit.org/show_bug.cgi?id=14563 + document.head.appendChild(el); + + function _parse(color) { + const [r, g, b, a = 1] = color.match(/[.\d]+/g).map(Number); + return {r, g, b, a}; + } + + function parse(color) { + el.style.color = color; + if (el.style.color === '') { + throw new Error(`"${color}" is not a valid color`); + } + color = getComputedStyle(el).color; + el.style.color = ''; + return _parse(color); + } + + function format({r, g, b, a = 1}) { + return `rgba(${r}, ${g}, ${b}, ${a})`; + } + + function pad(s) { + if (s.padStart) { + // chrome 57+ + return s.padStart(2, '0'); + } + return `00${s}`.slice(-2); + } + + function formatHex({r, g, b, a = null}) { + const values = [r, g, b]; + if (a !== null) { + values.push(Math.floor(a * 255)); + } + return '#' + values.map(n => pad(n.toString(16))).join(''); + } + + return {parse, format, formatHex}; + })(); + function getMetaSource(source) { const commentRe = /\/\*[\s\S]*?\*\//g; const metaRe = /==userstyle==[\s\S]*?==\/userstyle==/i; @@ -219,5 +262,5 @@ var usercss = (function () { // FIXME: validate variable formats } - return {buildMeta, buildCode}; + return {buildMeta, buildCode, colorParser}; })(); diff --git a/manage.html b/manage.html index 9f7752ca..308c954f 100644 --- a/manage.html +++ b/manage.html @@ -139,6 +139,8 @@ + + diff --git a/manage/config-dialog.js b/manage/config-dialog.js new file mode 100644 index 00000000..6c7d9905 --- /dev/null +++ b/manage/config-dialog.js @@ -0,0 +1,121 @@ +/* global usercss messageBox */ + +'use strict'; + +function configDialog(style) { + const {colorParser} = usercss; + const form = buildConfigForm(); + + return messageBox({ + title: `Configure ${style.name}`, + className: 'config-dialog', + contents: form.el, + buttons: [ + t('confirmSave'), + { + textContent: t('confirmDefault'), + onclick: form.useDefault + }, + t('confirmCancel') + ] + }).then(result => { + if (result.button !== 0 && !result.enter) { + return; + } + return form.getVars(); + }); + + function buildConfigForm() { + const labels = []; + const vars = deepCopy(style.vars); + for (const key of Object.keys(vars)) { + const va = vars[key]; + let appendChild; + if (va.type === 'color') { + va.inputColor = $element({tag: 'input', type: 'color'}); + // FIXME: i18n + va.inputAlpha = $element({tag: 'input', type: 'range', min: 0, max: 1, title: 'Opacity', step: 'any'}); + va.inputColor.onchange = va.inputAlpha.oninput = () => { + va.dirty = true; + const color = colorParser.parse(va.inputColor.value); + color.a = Number(va.inputAlpha.value); + va.value = colorParser.format(color); + va.inputColor.style.opacity = color.a; + }; + appendChild = [va.label, va.inputColor, va.inputAlpha]; + } else if (va.type === 'checkbox') { + va.input = $element({tag: 'input', type: 'checkbox'}); + va.input.onchange = () => { + va.dirty = true; + va.value = String(Number(va.input.checked)); + }; + appendChild = [va.input, $element({tag: 'span', appendChild: va.label})]; + } else if (va.type === 'select') { + va.input = $element({ + tag: 'select', + appendChild: Object.keys(va.select).map(key => $element({ + tag: 'option', value: key, appendChild: va.select[key] + })) + }); + va.input.onchange = () => { + va.dirty = true; + va.value = va.input.value; + }; + appendChild = [va.label, va.input]; + } else { + va.input = $element({tag: 'input', type: 'text'}); + va.input.oninput = () => { + va.dirty = true; + va.value = va.input.value; + }; + appendChild = [va.label, va.input]; + } + labels.push($element({ + tag: 'label', + className: `config-${va.type}`, + appendChild + })); + } + drawValues(); + + function drawValues() { + for (const key of Object.keys(vars)) { + const va = vars[key]; + const value = va.value === null || va.value === undefined ? + va.default : va.value; + + if (va.type === 'color') { + const color = colorParser.parse(value); + va.inputAlpha.value = color.a; + va.inputColor.style.opacity = color.a; + delete color.a; + va.inputColor.value = colorParser.formatHex(color); + } else if (va.type === 'checkbox') { + va.input.checked = Number(value); + } else { + va.input.value = value; + } + } + } + + function useDefault() { + for (const key of Object.keys(vars)) { + const va = vars[key]; + va.dirty = va.value !== null && va.value !== undefined && + va.value !== va.default; + va.value = null; + } + drawValues(); + } + + function getVars() { + return vars; + } + + return { + el: labels, + useDefault, + getVars + }; + } +} diff --git a/manage/manage.js b/manage/manage.js index 9524406e..4e8d447a 100644 --- a/manage/manage.js +++ b/manage/manage.js @@ -2,6 +2,7 @@ /* global filtersSelector, filterAndAppend */ /* global checkUpdate, handleUpdateInstalled */ /* global objectDiff */ +/* global configDialog */ 'use strict'; let installed; @@ -282,128 +283,20 @@ Object.assign(handleEvent, { }, config(event, {styleMeta: style}) { - const form = buildConfigForm(); - - messageBox({ - title: `Configure ${style.name}`, - className: 'config-dialog', - contents: form.el, - buttons: [ - t('confirmSave'), - { - textContent: t('confirmDefault'), - onclick: form.useDefault - }, - t('confirmCancel') - ] - }).then(result => { - if (result.button !== 0 && !result.enter) { + configDialog(style).then(vars => { + if (!vars) { + return; + } + const keys = Object.keys(vars).filter(k => vars[k].dirty); + if (!keys.length) { return; } style.reason = 'config'; - const vars = form.getVars(); - let dirty = false; - for (const key of Object.keys(vars)) { - if (vars[key].dirty) { - dirty = true; - style.vars[key].value = vars[key].value; - } - } - if (!dirty) { - return; + for (const key of keys) { + style.vars[key].value = vars[key].value; } saveStyleSafe(style); }); - - function buildConfigForm() { - const labels = []; - const vars = deepCopy(style.vars); - for (const key of Object.keys(vars)) { - const va = vars[key]; - let appendChild; - if (va.type === 'color') { - va.inputColor = $element({tag: 'input', type: 'color'}); - // FIXME: i18n - va.inputAlpha = $element({tag: 'input', type: 'range', min: 0, max: 255, title: 'Opacity'}); - va.inputColor.onchange = va.inputAlpha.oninput = () => { - va.dirty = true; - va.value = va.inputColor.value + Number(va.inputAlpha.value).toString(16); - va.inputColor.style.opacity = va.inputAlpha.value / 255; - }; - appendChild = [va.label, va.inputColor, va.inputAlpha]; - } else if (va.type === 'checkbox') { - va.input = $element({tag: 'input', type: 'checkbox'}); - va.input.onchange = () => { - va.dirty = true; - va.value = String(Number(va.input.checked)); - }; - appendChild = [va.input, $element({tag: 'span', appendChild: va.label})]; - } else if (va.type === 'select') { - va.input = $element({ - tag: 'select', - appendChild: Object.keys(va.select).map(key => $element({ - tag: 'option', value: key, appendChild: va.select[key] - })) - }); - va.input.onchange = () => { - va.dirty = true; - va.value = va.input.value; - }; - appendChild = [va.label, va.input]; - } else { - va.input = $element({tag: 'input', type: 'text'}); - va.input.oninput = () => { - va.dirty = true; - va.value = va.input.value; - }; - appendChild = [va.label, va.input]; - } - labels.push($element({ - tag: 'label', - className: `config-${va.type}`, - appendChild - })); - } - drawValues(); - - function drawValues() { - for (const key of Object.keys(vars)) { - const va = vars[key]; - const value = va.value === null || va.value === undefined ? - va.default : va.value; - - if (va.type === 'color') { - va.inputColor.value = value.slice(0, -2); - va.inputAlpha.value = parseInt(value.slice(-2), 16); - va.inputColor.style.opacity = va.inputAlpha.value / 255; - } else if (va.type === 'checkbox') { - va.input.checked = Number(value); - } else { - va.input.value = value; - } - } - } - - function useDefault() { - for (const key of Object.keys(vars)) { - const va = vars[key]; - va.dirty = va.value !== null && va.value !== undefined && - va.value !== va.default; - va.value = null; - } - drawValues(); - } - - function getVars() { - return vars; - } - - return { - el: labels, - useDefault, - getVars - }; - } }, entryClicked(event) {