diff --git a/edit.html b/edit.html index 984182e6..772fd162 100644 --- a/edit.html +++ b/edit.html @@ -193,6 +193,11 @@
+ + + + +
diff --git a/edit/edit.js b/edit/edit.js index 0775ede5..2307f395 100644 --- a/edit/edit.js +++ b/edit/edit.js @@ -1457,6 +1457,7 @@ function initHooks() { $('#sections-help').addEventListener('click', showSectionHelp, false); $('#keyMap-help').addEventListener('click', showKeyMapHelp, false); $('#cancel-button').addEventListener('click', goBackToManage); + $('#colorpicker-settings').addEventListener('click', configureColorpicker); setupOptionsExpand(); initLint(); @@ -1872,8 +1873,8 @@ function showHelp(title, body) { // avoid chaining on multiple showHelp() calls $('.dismiss', div).onclick = closeHelp; } - - div.style.display = 'block'; + // reset any inline styles + div.style = 'display: block'; return div; function closeHelp(e) { @@ -2078,6 +2079,7 @@ function onColorpickerReady() { '/vendor-overwrites/colorpicker/colorpicker.js', '/vendor-overwrites/colorpicker/colorview.js', ]; + prefs.subscribe(['editor.colorpicker.hotkey'], registerHotkey); prefs.subscribe(['editor.colorpicker'], colorpickerOnDemand); return prefs.get('editor.colorpicker') && colorpickerOnDemand(null, true); @@ -2087,21 +2089,83 @@ function onColorpickerReady() { } function setColorpickerOption(id, enabled) { - CodeMirror.defaults.colorpicker = enabled && { - forceUpdate: editors.length > 0, - tooltip: t('colorpickerTooltip'), - popupOptions: { - tooltipForSwitcher: t('colorpickerSwitchFormatTooltip'), - hexUppercase: prefs.get('editor.colorpicker.hexUppercase'), - hideDelay: 5000, - embedderCallback: state => { - if (state && state.hexUppercase !== prefs.get('editor.colorpicker.hexUppercase')) { - prefs.set('editor.colorpicker.hexUppercase', state.hexUppercase); - } + const defaults = CodeMirror.defaults; + const keyName = prefs.get('editor.colorpicker.hotkey'); + delete defaults.extraKeys[keyName]; + defaults.colorpicker = enabled; + if (enabled) { + if (keyName) { + CodeMirror.commands.colorpicker = invokeColorpicker; + defaults.extraKeys[keyName] = 'colorpicker'; + } + defaults.colorpicker = { + forceUpdate: editors.length > 0, + tooltip: t('colorpickerTooltip'), + popupOptions: { + tooltipForSwitcher: t('colorpickerSwitchFormatTooltip'), + hexUppercase: prefs.get('editor.colorpicker.hexUppercase'), + hideDelay: 5000, + embedderCallback: state => { + ['hexUppercase', 'color'] + .filter(name => state[name] !== prefs.get('editor.colorpicker.' + name)) + .forEach(name => prefs.set('editor.colorpicker.' + name, state[name])); + }, }, - }, - }; + }; + } // on page load runs before CodeMirror.setOption is defined - editors.forEach(cm => cm.setOption('colorpicker', CodeMirror.defaults.colorpicker)); + editors.forEach(cm => cm.setOption('colorpicker', defaults.colorpicker)); + } + + function registerHotkey(id, hotkey) { + const extraKeys = CodeMirror.defaults.extraKeys; + for (const key in extraKeys) { + if (extraKeys[key] === 'colorpicker') { + delete extraKeys[key]; + break; + } + } + if (hotkey) { + extraKeys[hotkey] = 'colorpicker'; + } + } + + function invokeColorpicker(cm) { + cm.state.colorpicker.openPopup(prefs.get('editor.colorpicker.color')); } } + +function configureColorpicker() { + const input = $element({ + tag: 'input', + type: 'search', + spellcheck: false, + value: prefs.get('editor.colorpicker.hotkey'), + onkeydown(event) { + const key = CodeMirror.keyName(event); + // ignore: [Shift?] characters, modifiers-only, [Shift?] Esc, Enter, [Shift?] Tab + if (/^(Enter|(Shift-)?(Esc|Tab|[!-~])|(Shift-?|Ctrl-?|Alt-?|Cmd-?)*)$/.test(key)) { + return; + } + event.preventDefault(); + event.stopPropagation(); + prefs.set('editor.colorpicker.hotkey', key); + this.value = key; + }, + oninput() { + // fired on pressing "x" to clear the field + prefs.set('editor.colorpicker.hotkey', ''); + }, + onpaste(event) { + event.preventDefault(); + } + }); + const popup = showHelp(t('helpKeyMapHotkey'), input); + if (this instanceof Element) { + const bounds = this.getBoundingClientRect(); + popup.style.left = bounds.right + 10 + 'px'; + popup.style.top = bounds.top - popup.clientHeight / 2 + 'px'; + popup.style.right = 'auto'; + } + input.focus(); +} diff --git a/js/prefs.js b/js/prefs.js index 1c93a06c..09260bbe 100644 --- a/js/prefs.js +++ b/js/prefs.js @@ -60,6 +60,10 @@ var prefs = new function Prefs() { 'editor.colorpicker': true, // #DEAD or #beef 'editor.colorpicker.hexUppercase': false, + // default hotkey + 'editor.colorpicker.hotkey': '', + // last color + 'editor.colorpicker.color': '', 'iconset': 0, // 0 = dark-themed icon // 1 = light-themed icon diff --git a/vendor-overwrites/colorpicker/colorpicker.js b/vendor-overwrites/colorpicker/colorpicker.js index c430d5cc..013f368f 100644 --- a/vendor-overwrites/colorpicker/colorpicker.js +++ b/vendor-overwrites/colorpicker/colorpicker.js @@ -203,7 +203,7 @@ CodeMirror.defineExtension('colorpicker', function () { options = PUBLIC_API.options = opt; prevFocusedElement = document.activeElement; userActivity = 0; - lastOutputColor = opt.color; + lastOutputColor = opt.color || ''; $formatChangeButton.title = opt.tooltipForSwitcher || ''; opt.hideDelay = Math.max(0, opt.hideDelay) || 2000; @@ -337,7 +337,7 @@ CodeMirror.defineExtension('colorpicker', function () { } function validateInput(el) { - const isAlpha = el.type === 'text'; + const isAlpha = el === $inputs[currentFormat][3]; let isValid = (isAlpha || el.value.trim()) && el.checkValidity(); if (!isAlpha && !isValid && currentFormat === 'rgb') { isValid = parseAs(el, parseInt); @@ -352,8 +352,9 @@ CodeMirror.defineExtension('colorpicker', function () { //endregion //region State-to-DOM - function setFromColor(color = '#FF0000') { + function setFromColor(color) { color = typeof color === 'string' ? stringToColor(color) : color; + color = color || stringToColor('#f00'); const newHSV = color.type === 'hsl' ? HSLtoHSV(color) : RGBtoHSV(color); if (Object.keys(newHSV).every(k => Math.abs(newHSV[k] - HSV[k]) < 1e-3)) { return; @@ -440,7 +441,7 @@ CodeMirror.defineExtension('colorpicker', function () { } } - function onSaturationMouseUp() { + function onSaturationMouseUp(event) { if (event.button === 0) { dragging.saturation = false; releaseMouse(); @@ -454,7 +455,7 @@ CodeMirror.defineExtension('colorpicker', function () { } } - function onOpacityKnobMouseDown() { + function onOpacityKnobMouseDown(event) { if (event.button === 0) { dragging.opacity = true; captureMouse(); @@ -517,6 +518,7 @@ CodeMirror.defineExtension('colorpicker', function () { if (!e.shiftKey && !e.ctrlKey && !e.altKey && !e.metaKey) { switch (e.which) { case 13: + setFromInputs(); colorpickerCallback(); // fallthrough to 27 case 27: @@ -647,7 +649,6 @@ CodeMirror.defineExtension('colorpicker', function () { str.match(/(..)/g).map(c => parseInt(c, 16)); return {type: 'hex', r, g, b, a: a === 255 ? undefined : a / 255}; } - return; } function RGBtoHSV({r, g, b, a}) { diff --git a/vendor-overwrites/colorpicker/colorview.js b/vendor-overwrites/colorpicker/colorview.js index 3fa3c554..93c7713d 100644 --- a/vendor-overwrites/colorpicker/colorview.js +++ b/vendor-overwrites/colorpicker/colorview.js @@ -400,10 +400,11 @@ openPopup(color) { let {line, ch} = this.cm.getCursor(); const lineText = this.cm.getLine(line); - ch -= (lineText.lastIndexOf('!important', ch) >= ch - '!important'.length) ? '!important'.length : 0; + const atImportant = lineText.lastIndexOf('!important', ch); + ch -= (atImportant >= Math.max(0, ch - '!important'.length)) ? '!important'.length : 0; const lineCache = this.cm.state.colorpicker.cache.get(lineText); - const data = {line, ch, color, isShortCut: true}; - for (const [start, {color, colorValue}] of lineCache && lineCache.entries() || []) { + const data = {line, ch, colorValue: color, isShortCut: true}; + for (const [start, {color, colorValue = color}] of lineCache && lineCache.entries() || []) { if (start <= ch && ch <= start + color.length) { Object.assign(data, {ch: start, color, colorValue}); break; @@ -419,7 +420,7 @@ top, left, cm: this.cm, - color: data.colorValue || data.color || '#fff', + color: data.colorValue || data.color, prevColor: data.color || '', isShortCut: false, callback: ColorMarker.popupOnChange, @@ -437,8 +438,8 @@ const {cm, line, ch, embedderCallback} = this; const to = {line, ch: ch + this.prevColor.length}; if (cm.getRange(this, to) !== newColor) { - this.prevColor = newColor; cm.replaceRange(newColor, this, to, '*colorpicker'); + this.prevColor = newColor; } if (typeof embedderCallback === 'function') { embedderCallback(this);