Add: colorParser
This commit is contained in:
		
							parent
							
								
									1f44898475
								
							
						
					
					
						commit
						4e0f4b34bb
					
				| 
						 | 
					@ -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) {
 | 
					  function getMetaSource(source) {
 | 
				
			||||||
    const commentRe = /\/\*[\s\S]*?\*\//g;
 | 
					    const commentRe = /\/\*[\s\S]*?\*\//g;
 | 
				
			||||||
    const metaRe = /==userstyle==[\s\S]*?==\/userstyle==/i;
 | 
					    const metaRe = /==userstyle==[\s\S]*?==\/userstyle==/i;
 | 
				
			||||||
| 
						 | 
					@ -219,5 +262,5 @@ var usercss = (function () {
 | 
				
			||||||
    // FIXME: validate variable formats
 | 
					    // FIXME: validate variable formats
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return {buildMeta, buildCode};
 | 
					  return {buildMeta, buildCode, colorParser};
 | 
				
			||||||
})();
 | 
					})();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -139,6 +139,8 @@
 | 
				
			||||||
  <script src="manage/filters.js"></script>
 | 
					  <script src="manage/filters.js"></script>
 | 
				
			||||||
  <script src="manage/updater-ui.js"></script>
 | 
					  <script src="manage/updater-ui.js"></script>
 | 
				
			||||||
  <script src="manage/object-diff.js"></script>
 | 
					  <script src="manage/object-diff.js"></script>
 | 
				
			||||||
 | 
					  <script src="js/usercss.js"></script>
 | 
				
			||||||
 | 
					  <script src="manage/config-dialog.js"></script>
 | 
				
			||||||
  <script src="manage/manage.js"></script>
 | 
					  <script src="manage/manage.js"></script>
 | 
				
			||||||
</head>
 | 
					</head>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										121
									
								
								manage/config-dialog.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										121
									
								
								manage/config-dialog.js
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -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
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										123
									
								
								manage/manage.js
									
									
									
									
									
								
							
							
						
						
									
										123
									
								
								manage/manage.js
									
									
									
									
									
								
							| 
						 | 
					@ -2,6 +2,7 @@
 | 
				
			||||||
/* global filtersSelector, filterAndAppend */
 | 
					/* global filtersSelector, filterAndAppend */
 | 
				
			||||||
/* global checkUpdate, handleUpdateInstalled */
 | 
					/* global checkUpdate, handleUpdateInstalled */
 | 
				
			||||||
/* global objectDiff */
 | 
					/* global objectDiff */
 | 
				
			||||||
 | 
					/* global configDialog */
 | 
				
			||||||
'use strict';
 | 
					'use strict';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let installed;
 | 
					let installed;
 | 
				
			||||||
| 
						 | 
					@ -282,128 +283,20 @@ Object.assign(handleEvent, {
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  config(event, {styleMeta: style}) {
 | 
					  config(event, {styleMeta: style}) {
 | 
				
			||||||
    const form = buildConfigForm();
 | 
					    configDialog(style).then(vars => {
 | 
				
			||||||
 | 
					      if (!vars) {
 | 
				
			||||||
    messageBox({
 | 
					        return;
 | 
				
			||||||
      title: `Configure ${style.name}`,
 | 
					      }
 | 
				
			||||||
      className: 'config-dialog',
 | 
					      const keys = Object.keys(vars).filter(k => vars[k].dirty);
 | 
				
			||||||
      contents: form.el,
 | 
					      if (!keys.length) {
 | 
				
			||||||
      buttons: [
 | 
					 | 
				
			||||||
        t('confirmSave'),
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
          textContent: t('confirmDefault'),
 | 
					 | 
				
			||||||
          onclick: form.useDefault
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        t('confirmCancel')
 | 
					 | 
				
			||||||
      ]
 | 
					 | 
				
			||||||
    }).then(result => {
 | 
					 | 
				
			||||||
      if (result.button !== 0 && !result.enter) {
 | 
					 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      style.reason = 'config';
 | 
					      style.reason = 'config';
 | 
				
			||||||
      const vars = form.getVars();
 | 
					      for (const key of keys) {
 | 
				
			||||||
      let dirty = false;
 | 
					 | 
				
			||||||
      for (const key of Object.keys(vars)) {
 | 
					 | 
				
			||||||
        if (vars[key].dirty) {
 | 
					 | 
				
			||||||
          dirty = true;
 | 
					 | 
				
			||||||
        style.vars[key].value = vars[key].value;
 | 
					        style.vars[key].value = vars[key].value;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      if (!dirty) {
 | 
					 | 
				
			||||||
        return;
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      saveStyleSafe(style);
 | 
					      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) {
 | 
					  entryClicked(event) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user