From 906508832b981db6c3d27367a156fcc46edb70e3 Mon Sep 17 00:00:00 2001 From: tophf Date: Wed, 29 Dec 2021 22:57:22 +0300 Subject: [PATCH] show style settings in a dialog (#1374) + simplify css/html + save button and autosave checkbox just like in config-dialog + generalize can-close-on-esc + add `props` parameter to helpPopup.show + deduplicate usage of #help-popup id + uniform padding in popups + disambiguate style settings from editor options --- _locales/en/messages.json | 12 +++- edit.html | 102 +++++++++++++---------------- edit/base.js | 21 +++--- edit/beautify.js | 11 ++-- edit/edit.css | 51 ++++++--------- edit/edit.js | 46 +++++++++---- edit/sections-editor.js | 34 ++-------- edit/settings.css | 53 ++++++++------- edit/settings.js | 131 +++++++++++++++++++++----------------- edit/source-editor.js | 34 ++-------- edit/util.js | 25 +++++--- js/event-emitter.js | 37 ----------- 12 files changed, 248 insertions(+), 309 deletions(-) delete mode 100644 js/event-emitter.js diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 2e5a5a32..a6c4d3c2 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -378,8 +378,8 @@ "editorCodeLabel": { "message": "Code" }, - "editorSettingLabel": { - "message": "Settings" + "editorSettings": { + "message": "Editor settings" }, "enableStyleLabel": { "message": "Enable", @@ -1449,6 +1449,10 @@ "message": "Sections", "description": "Header for the table of contents block listing style section names in the left panel of the classic editor" }, + "settings": { + "message": "Settings", + "description": "Generic label/title for settings" + }, "shortcuts": { "message": "Shortcuts", "description": "Go to shortcut configuration" @@ -1620,6 +1624,10 @@ "message": "Save", "description": "Label for save button for style editing" }, + "styleSettings": { + "message": "Style settings", + "description": "Label/title for style settings dialog" + }, "styleToMozillaFormatHelp": { "message": "The Mozilla format of the code can be submitted to userstyles.org and used with the classic Stylish for Firefox", "description": "Help info for the Mozilla format header section that converts the code to/from Mozilla format" diff --git a/edit.html b/edit.html index dfc741cc..60efff60 100644 --- a/edit.html +++ b/edit.html @@ -17,7 +17,6 @@ - @@ -62,7 +61,6 @@ - + + @@ -241,8 +277,6 @@ - - @@ -278,7 +312,8 @@
- + +
@@ -291,7 +326,7 @@
-

+

@@ -437,53 +472,7 @@ target="_blank">
-
-
-
-
-
-
-
-
- - -
- - - - - -
- - - -
-
-
+
@@ -528,6 +517,5 @@ - diff --git a/edit/base.js b/edit/base.js index f3d4261e..0764d8d9 100644 --- a/edit/base.js +++ b/edit/base.js @@ -14,14 +14,13 @@ tryJSONparse tryURL */// toolbox.js -/* global EventEmitter */ 'use strict'; /** * @type Editor * @namespace Editor */ -const editor = Object.assign(EventEmitter(), { +const editor = { style: null, dirty: DirtyReporter(), isUsercss: false, @@ -36,7 +35,9 @@ const editor = Object.assign(EventEmitter(), { previewDelay: 200, // Chrome devtools uses 200 scrollInfo: null, - onStyleUpdated() { + cancel: () => location.assign('/manage.html'), + + updateClass() { document.documentElement.classList.toggle('is-new-style', !editor.style.id); }, @@ -48,7 +49,7 @@ const editor = Object.assign(EventEmitter(), { customName || name || t('styleMissingName') } - Stylus`; // the suffix enables external utilities to process our windows e.g. pin on top }, -}); +}; //#region pre-init @@ -90,7 +91,7 @@ const baseInit = (() => { // switching the mode here to show the correct page ASAP, usually before DOMContentLoaded editor.isUsercss = Boolean(style.usercssData || !style.id && prefs.get('newStyleAsUsercss')); editor.style = style; - editor.onStyleUpdated(); + editor.updateClass(); editor.updateTitle(false); document.documentElement.classList.toggle('usercss', editor.isUsercss); sessionStore.justEditedStyleId = style.id || ''; @@ -292,16 +293,10 @@ baseInit.ready.then(() => { } } - getOwnTab().then(async tab => { + getOwnTab().then(tab => { ownTabId = tab.id; - // use browser history back when 'back to manage' is clicked if (sessionStore['manageStylesHistory' + ownTabId] === location.href) { - await baseInit.domReady; - $('#cancel-button').onclick = event => { - event.stopPropagation(); - event.preventDefault(); - history.back(); - }; + editor.cancel = () => history.back(); } }); diff --git a/edit/beautify.js b/edit/beautify.js index cdc271d1..e264acbe 100644 --- a/edit/beautify.js +++ b/edit/beautify.js @@ -65,7 +65,7 @@ function beautifyEditor(cm, options, ui) { window.scrollTo(scrollX, scrollY); cm.beautifyChange[cm.changeGeneration()] = true; if (ui) { - $('#help-popup button[role="close"]').disabled = false; + $('button[role="close"]', helpPopup.div).disabled = false; } } } @@ -87,7 +87,7 @@ function createBeautifyUI(scope, options) { $create('span', t('styleBeautifyHint') + '\u00A0'), createHotkeyInput('editor.beautify.hotkey', { buttons: false, - onDone: () => moveFocus($('#help-popup'), 0), + onDone: () => moveFocus(helpPopup.div, 0), }), ]), $create('.buttons', [ @@ -113,9 +113,10 @@ function createBeautifyUI(scope, options) { }, }, t(scope.length === 1 ? 'undo' : 'undoGlobal')), ]), - ])); - - $('#help-popup').className = 'wide'; + ]), + { + className: 'wide', + }); $('.beautify-options').onchange = ({target}) => { const value = target.type === 'checkbox' ? target.checked : target.selectedIndex > 0; diff --git a/edit/edit.css b/edit/edit.css index 157056ba..8921377e 100644 --- a/edit/edit.css +++ b/edit/edit.css @@ -34,6 +34,7 @@ a:hover { } html.is-new-style #preview-label, +html.is-new-style #style-settings-btn, html.is-new-style #publish, .hidden { display: none !important; @@ -105,11 +106,8 @@ label { #header h1 { margin-top: 0; } -.main { - padding-left: 280px; - height: 100%; -} #sections { + padding-left: 280px; min-height: 0; height: 100%; } @@ -284,10 +282,6 @@ input:invalid { margin: 0 .2rem .5rem 0; } -#actions #cancel-button { - margin: 0; -} - #options:not([open]) + #lint h2 { margin-top: 0; } @@ -417,10 +411,6 @@ input:invalid { .edit-actions button { margin-right: .2rem; } -.dirty > label::before { - content: "*"; - font-weight: bold; -} #sections { counter-reset: codebox; } @@ -736,6 +726,9 @@ body:not(.find-open) [data-match-highlight-count="1"] .CodeMirror-selection-high } /************ help popup ************/ #help-popup { + --pad-x: 1.5rem; + --pad-y: 1rem; + --pad-y2: calc(var(--pad-y) / 1.5); top: 3rem; right: 3rem; max-width: 50vw; @@ -743,7 +736,7 @@ body:not(.find-open) [data-match-highlight-count="1"] .CodeMirror-selection-high display: none; background-color: white; box-shadow: 3px 3px 30px rgba(0, 0, 0, 0.5); - padding: 0.5rem; + padding: var(--pad-y) var(--pad-x) 0; z-index: 99; } #help-popup.big, @@ -761,22 +754,19 @@ body:not(.find-open) [data-match-highlight-count="1"] .CodeMirror-selection-high #help-popup .title { font-weight: bold; background-color: rgba(0,0,0,0.05); - margin: -0.5rem -0.5rem 0.5rem; - padding: .5rem 32px .5rem .5rem; + margin: calc(-1 * var(--pad-y)) calc(-1 * var(--pad-x)) 0; + padding: var(--pad-y2) var(--pad-x); } #help-popup .contents { max-height: calc(100vh - 8rem); overflow-y: auto; -} -#help-popup .settings { - min-width: 500px; - min-height: 200px; - max-width: 48vw; + padding: var(--pad-y) 0; } #help-popup .dismiss { position: absolute; - right: 4px; - top: .5em; + right: 0; + top: 0; + padding: var(--pad-y2) .5em; } #help-popup input[type="search"], #help-popup .CodeMirror { @@ -804,12 +794,14 @@ body:not(.find-open) [data-match-highlight-count="1"] .CodeMirror-selection-high } #help-popup .buttons { - text-align: center; - margin-top: .75em; + display: flex; + justify-content: center; + align-items: center; + margin: var(--pad-y2) 0 calc(var(--pad-y2) - var(--pad-y)) 0; } .non-windows #help-popup .buttons { direction: rtl; - text-align: right; + justify-content: start; } #help-popup button[name^="import"] { line-height: 1.5rem; @@ -831,8 +823,8 @@ body:not(.find-open) [data-match-highlight-count="1"] .CodeMirror-selection-high #help-popup .rules p { margin: .25em 0; } -#help-popup .buttons button:nth-child(n + 2) { - margin-left: .5em; +#help-popup .buttons > :nth-child(n + 2) { + margin-inline: .5em 0; } /************ lint ************/ @@ -1180,16 +1172,13 @@ body.linter-disabled .hidden-unless-compact { #lint:not([open]) + #footer { margin: .25em 0 -1em .25em; } - .main { + #sections { height: unset !important; padding-left: 0; display: flex; flex-direction: column; flex: 1; } - .tab-bar { - margin-top: var(--fixed-height); - } #sections > :not(.single-editor) { margin: 0 .5rem; padding: .5rem 0; diff --git a/edit/edit.js b/edit/edit.js index 123eea60..da4771f8 100644 --- a/edit/edit.js +++ b/edit/edit.js @@ -1,5 +1,5 @@ /* global $ $create messageBoxProxy waitForSheet */// dom.js -/* global msg API */// msg.js +/* global API msg */// msg.js /* global CodeMirror */ /* global SectionsEditor */ /* global SourceEditor */ @@ -11,7 +11,6 @@ /* global linterMan */ /* global prefs */ /* global t */// localization.js -/* global StyleSettings */// settings.js 'use strict'; //#region init @@ -19,7 +18,6 @@ baseInit.ready.then(async () => { await waitForSheet(); (editor.isUsercss ? SourceEditor : SectionsEditor)(); - StyleSettings(editor); await editor.ready; editor.ready = true; editor.dirty.onChange(editor.updateDirty); @@ -32,6 +30,7 @@ baseInit.ready.then(async () => { // enabling after init to prevent flash of validation failure on an empty name $('#name').required = !editor.isUsercss; $('#save-button').onclick = editor.save; + $('#cancel-button').onclick = editor.cancel; const elSec = $('#sections-list'); // editor.toc.expanded pref isn't saved in compact-layout so prefs.subscribe won't work @@ -48,6 +47,11 @@ baseInit.ready.then(async () => { require(['/edit/linter-dialogs'], () => linterMan.showLintConfig()); $('#lint-help').onclick = () => require(['/edit/linter-dialogs'], () => linterMan.showLintHelp()); + $('#style-settings-btn').onclick = () => require([ + '/edit/settings.css', + '/edit/settings', /* global StyleSettings */ + ], () => StyleSettings()); + require([ '/edit/autocomplete', '/edit/global-search', @@ -70,14 +74,7 @@ msg.onExtension(request => { switch (request.method) { case 'styleUpdated': if (editor.style.id === style.id && !IGNORE_UPDATE_REASONS.includes(request.reason)) { - if (request.reason === 'toggle') { - editor.emit('styleToggled', request.style); - } else { - API.styles.get(request.style.id) - .then(style => { - editor.emit('styleChange', style, request.reason); - }); - } + handleExternalUpdate(request); } break; case 'styleDeleted': @@ -91,6 +88,31 @@ msg.onExtension(request => { } }); +async function handleExternalUpdate({style, reason}) { + if (reason === 'toggle') { + if (editor.dirty.isDirty()) { + editor.toggleStyle(style.enabled); + } else { + Object.assign(editor.style, style); + } + editor.updateMeta(); + editor.updateLivePreview(); + return; + } + style = await API.styles.get(style.id); + if (reason === 'config') { + delete style.sourceCode; + delete style.sections; + delete style.name; + delete style.enabled; + Object.assign(editor.style, style); + editor.updateLivePreview(); + } else { + await editor.replaceStyle(style); + } + window.dispatchEvent(new Event('styleSettings')); +} + window.on('beforeunload', e => { let pos; if (editor.isWindowed && @@ -169,7 +191,7 @@ window.on('beforeunload', e => { } }, - toggleStyle(enabled = style.enabled) { + toggleStyle(enabled = !style.enabled) { $('#enabled').checked = enabled; editor.updateEnabledness(enabled); }, diff --git a/edit/sections-editor.js b/edit/sections-editor.js index 306babb5..deba1520 100644 --- a/edit/sections-editor.js +++ b/edit/sections-editor.js @@ -23,7 +23,7 @@ function SectionsEditor() { let headerOffset; // in compact mode the header is at the top so it reduces the available height let cmExtrasHeight; // resize grip + borders - updateHeader(); + updateMeta(); rerouteHotkeys.toggle(true); // enabled initially because we don't always focus a CodeMirror editor.livePreview.init(); container.classList.add('section-editor'); @@ -44,6 +44,7 @@ function SectionsEditor() { closestVisible, updateLivePreview, + updateMeta, getEditors() { return sections.filter(s => !s.removed).map(s => s.cm); @@ -89,8 +90,8 @@ function SectionsEditor() { // FIXME: avoid recreating all editors? await initSections(newStyle.sections, {replace: true}); Object.assign(style, newStyle); - editor.onStyleUpdated(); - updateHeader(); + editor.updateClass(); + updateMeta(); // Go from new style URL to edit style URL if (style.id && !/[&?]id=/.test(location.search)) { history.replaceState({}, document.title, `${location.pathname}?id=${style.id}`); @@ -108,9 +109,6 @@ function SectionsEditor() { } newStyle = await API.styles.editSave(newStyle); destroyRemovedSections(); - if (!style.id) { - editor.emit('styleChange', newStyle, 'new'); - } sessionStore.justEditedStyleId = newStyle.id; editor.replaceStyle(newStyle, false); }, @@ -128,28 +126,6 @@ function SectionsEditor() { editor.ready = initSections(style.sections); - editor.on('styleToggled', newStyle => { - if (!dirty.isDirty()) { - Object.assign(style, newStyle); - } else { - editor.toggleStyle(newStyle.enabled); - } - updateHeader(); - updateLivePreview(); - }); - editor.on('styleChange', (newStyle, reason) => { - if (reason === 'new') return; // nothing is new for us - if (reason === 'config') { - delete newStyle.sections; - delete newStyle.name; - delete newStyle.enabled; - Object.assign(style, newStyle); - updateLivePreview(); - return; - } - editor.replaceStyle(newStyle); - }); - /** @param {EditorSection} section */ function fitToContent(section) { const {cm, cm: {display: {wrapper, sizer}}} = section; @@ -489,7 +465,7 @@ function SectionsEditor() { } } - function updateHeader() { + function updateMeta() { $('#name').value = style.customName || style.name || ''; $('#enabled').checked = style.enabled !== false; $('#url').href = style.url || ''; diff --git a/edit/settings.css b/edit/settings.css index 3b33eba8..32397434 100644 --- a/edit/settings.css +++ b/edit/settings.css @@ -1,46 +1,43 @@ +#help-popup.style-settings-popup.dirty .title::after { + content: ' *'; +} +.compact-layout #help-popup.style-settings-popup { + width: 90%; +} .style-settings { - padding: 0.7rem 1.7rem; + padding: 0; border: 0; margin: 0; } -.form-group { +.style-settings > * { display: block; - margin: .6em 0; + margin: 1rem 0; padding: 0; } -.form-label { - display: inline-block; - margin: .3em 0; +.style-settings > :first-child { + margin-top: 0; } -[disabled] .form-label { - opacity: 0.4; +.style-settings > :last-child { + margin-bottom: 0; } -.form-group input[type=text], -.form-group input[type=number], -.form-group select, -.form-group textarea { +.style-settings input[type=radio] { + margin-left: -.5em; /* compensate for label's 16px margin in edit.css */ +} +.style-settings input[type=text], +.style-settings input[type=number], +.style-settings select, +.style-settings textarea { display: block; width: 100%; + margin-top: .25em; box-sizing: border-box; } -.radio-group .form-label { - display: block; -} -.radio-item { - display: flex; - margin: 0.3em 0 .3em; - padding: 0; - align-items: center; - width: max-content; -} -.radio-item input { - margin: 0 0.6em 0 0; -} -[disabled] .radio-label { - opacity: 0.4; -} .style-settings textarea { resize: vertical; + min-width: 33vw; min-height: 2.5em; max-height: 50vh; } +.style-settings textarea:not(:placeholder-shown) { + min-width: 50vw; +} diff --git a/edit/settings.js b/edit/settings.js index b54d28fe..29d0e176 100644 --- a/edit/settings.js +++ b/edit/settings.js @@ -1,84 +1,97 @@ -/* global $ $$ */// dom.js +/* global $ $$ moveFocus setupLivePrefs */// dom.js /* global API */// msg.js +/* global editor */ +/* global helpPopup */// util.js +/* global t */// localization.js +/* global debounce */// toolbox.js /* exported StyleSettings */ 'use strict'; -function StyleSettings(editor) { - let {style} = editor; - - const inputs = [ - createInput('.style-update-url input', () => style.updateUrl || '', - e => API.styles.config(style.id, 'updateUrl', e.target.value)), - createRadio('.style-prefer-scheme input', () => style.preferScheme || 'none', - e => API.styles.config(style.id, 'preferScheme', e.target.value)), - ...[ - ['.style-include', 'inclusions'], - ['.style-exclude', 'exclusions'], - ].map(createArea), +function StyleSettings() { + const AUTOSAVE_DELAY = 500; // same as config-dialog.js + const SS_ID = 'styleSettings'; + const {style} = editor; + const ui = t.template[SS_ID].cloneNode(true); + const elAuto = $('[id="config.autosave"]', ui); + const elSave = $('#ss-save', ui); + const pendingSetters = new Map(); + const updaters = [ + initInput('#ss-update-url', () => style.updateUrl || '', + val => API.styles.config(style.id, 'updateUrl', val)), + initRadio('ss-scheme', () => style.preferScheme || 'none', + val => API.styles.config(style.id, 'preferScheme', val)), + initArea('inclusions'), + initArea('exclusions'), ]; + update(); + window.on(SS_ID, update); + window.on('closeHelp', () => window.off(SS_ID, update), {once: true}); + helpPopup.show(t(SS_ID), ui, { + className: 'style-settings-popup', + }); + elSave.onclick = save; + $('#ss-close', ui).onclick = helpPopup.close; + setupLivePrefs([elAuto.id]); + moveFocus(ui, 0); - update(style); - - editor.on('styleChange', update); - - function textToList(text) { - const list = text.split(/\s*\r?\n\s*/g); - return list.filter(Boolean); + function autosave(el, setter) { + pendingSetters.set(el, setter); + helpPopup.div.classList.add('dirty'); + elSave.disabled = false; + if (elAuto.checked) debounce(save, AUTOSAVE_DELAY); } - function update(newStyle, reason) { - if (!newStyle.id) return; - if (reason === 'editSave') return; - style = newStyle; - $('.style-settings').disabled = false; - inputs.forEach(i => i.update()); - } - - function createArea([parentSel, type]) { - const sel = parentSel + ' textarea'; - const el = $(sel); - el.on('input', () => { + function initArea(type) { + const selector = `#ss-${type}`; + const el = $(selector, ui); + el.oninput = () => { const val = el.value; el.rows = val.match(/^/gm).length + !val.endsWith('\n'); - }); - return createInput(sel, + }; + return initInput(selector, () => { const list = style[type] || []; const text = list.join('\n'); el.rows = (list.length || 1) + 1; return text; }, - () => API.styles.config(style.id, type, textToList(el.value)) + val => API.styles.config(style.id, type, textToList(val)) ); } - function createRadio(selector, getter, setter) { - const els = $$(selector); - for (const el of els) { - el.addEventListener('change', e => { - if (el.checked) { - setter(e); - } - }); - } - return { - update() { - for (const el of els) { - if (el.value === getter()) { - el.checked = true; - } - } - }, + function initInput(selector, getter, setter) { + const el = $(selector, ui); + el.oninput = () => autosave(el, setter); + return () => { + const val = getter(); + // Skipping if unchanged to preserve the Undo history of the input + if (el.value !== val) el.value = val; }; } - function createInput(selector, getter, setter) { - const el = $(selector); - el.addEventListener('change', setter); - return { - update() { - el.value = getter(); - }, + function initRadio(name, getter, setter) { + for (const el of $$(`[name="${name}"]`, ui)) { + el.onchange = () => { + if (el.checked) autosave(el, setter); + }; + } + return () => { + $(`[name="${name}"][value="${getter()}"]`, ui).checked = true; }; } + + function save() { + pendingSetters.forEach((fn, el) => fn(el.value)); + pendingSetters.clear(); + helpPopup.div.classList.remove('dirty'); + elSave.disabled = true; + } + + function textToList(text) { + return text.split(/\n/).map(s => s.trim()).filter(Boolean); + } + + function update() { + updaters.forEach(fn => fn()); + } } diff --git a/edit/source-editor.js b/edit/source-editor.js index ead9e378..9c7a1415 100644 --- a/edit/source-editor.js +++ b/edit/source-editor.js @@ -45,6 +45,7 @@ function SourceEditor() { sections: sectionFinder.sections, replaceStyle, updateLivePreview, + updateMeta, closestVisible: () => cm, getEditors: () => [cm], getEditorTitle: () => '', @@ -70,9 +71,6 @@ function SourceEditor() { messageBoxProxy.alert(t('usercssAvoidOverwriting'), 'danger', t('genericError')); } else { res = await API.usercss.editSave({customName, enabled, id, sourceCode}); - if (!id) { - editor.emit('styleChange', res.style, 'new'); - } // Awaiting inside `try` so that exceptions go to our `catch` await replaceStyle(res.style); } @@ -116,26 +114,6 @@ function SourceEditor() { if (!$isTextInput(document.activeElement)) { cm.focus(); } - editor.on('styleToggled', newStyle => { - if (dirty.isDirty()) { - editor.toggleStyle(newStyle.enabled); - } else { - style.enabled = newStyle.enabled; - } - updateMeta(); - updateLivePreview(); - }); - editor.on('styleChange', (newStyle, reason) => { - if (reason === 'new') return; - if (reason === 'config') { - delete newStyle.sourceCode; - delete newStyle.name; - Object.assign(style, newStyle); - updateLivePreview(); - return; - } - replaceStyle(newStyle); - }); async function preprocess(style) { const res = await API.usercss.build({ @@ -231,7 +209,7 @@ function SourceEditor() { cm.setPreprocessor((style.usercssData || {}).preprocessor); } - function replaceStyle(newStyle) { + async function replaceStyle(newStyle) { dirty.clear('name'); const sameCode = newStyle.sourceCode === cm.getValue(); if (sameCode) { @@ -243,8 +221,8 @@ function SourceEditor() { return; } - Promise.resolve(messageBoxProxy.confirm(t('styleUpdateDiscardChanges'))).then(ok => { - if (!ok) return; + // TODO: also confirm in sections-editor? + if (await messageBoxProxy.confirm(t('styleUpdateDiscardChanges'))) { updateEnvironment(); if (!sameCode) { const cursor = cm.getCursor(); @@ -257,7 +235,7 @@ function SourceEditor() { updateLivePreview(); } dirty.clear(); - }); + } function updateEnvironment() { if (style.id !== newStyle.id) { @@ -265,7 +243,7 @@ function SourceEditor() { } sessionStore.justEditedStyleId = newStyle.id; Object.assign(style, newStyle); - editor.onStyleUpdated(); + editor.updateClass(); updateMeta(); } } diff --git a/edit/util.js b/edit/util.js index 028da6c1..ddc5f30f 100644 --- a/edit/util.js +++ b/edit/util.js @@ -7,20 +7,28 @@ const helpPopup = { - show(title = '', body) { + /** + * @param {string} title - plain text + * @param {string|Node} body - Node, html or plain text + * @param {Node} [props] - DOM props for the popup element + * @returns {Element} the popup + */ + show(title = '', body, props) { const div = $('#help-popup'); const contents = $('.contents', div); + div.style = ''; div.className = ''; contents.textContent = ''; + Object.assign(div, props); if (body) { contents.appendChild(typeof body === 'string' ? t.HTML(body) : body); } $('.title', div).textContent = title; $('.dismiss', div).onclick = helpPopup.close; window.on('keydown', helpPopup.close, true); - // reset any inline styles - div.style = 'display: block'; + div.style.display = 'block'; helpPopup.originalFocus = document.activeElement; + helpPopup.div = div; return div; }, @@ -31,11 +39,13 @@ const helpPopup = { getEventKeyName(event) === 'Escape' && !$('.CodeMirror-hints, #message-box') && ( !document.activeElement || - !document.activeElement.closest('#search-replace-dialog') && - document.activeElement.matches(':not(input), .can-close-on-esc') + !document.activeElement.closest('#search-replace-dialog') && ( + document.activeElement.tagName !== 'INPUT' || + document.activeElement.closest('.can-close-on-esc') + ) ) ); - const div = $('#help-popup'); + const {div} = helpPopup; if (!canClose || !div) { return; } @@ -170,8 +180,7 @@ function createHotkeyInput(prefId, {buttons = true, onDone}) { /* exported showCodeMirrorPopup */ function showCodeMirrorPopup(title, html, options) { - const popup = helpPopup.show(title, html); - popup.classList.add('big'); + const popup = helpPopup.show(title, html, {className: 'big'}); let cm = popup.codebox = CodeMirror($('.contents', popup), Object.assign({ mode: 'css', diff --git a/js/event-emitter.js b/js/event-emitter.js deleted file mode 100644 index df4b6916..00000000 --- a/js/event-emitter.js +++ /dev/null @@ -1,37 +0,0 @@ -/* exported EventEmitter */ -'use strict'; - -function EventEmitter() { - const listeners = new Map(); - return { - on(ev, cb, opt) { - if (!listeners.has(ev)) { - listeners.set(ev, new Map()); - } - listeners.get(ev).set(cb, opt); - if (opt && opt.runNow) { - cb(); - } - }, - off(ev, cb) { - const cbs = listeners.get(ev); - if (cbs) { - cbs.delete(cb); - } - }, - emit(ev, ...args) { - const cbs = listeners.get(ev); - if (!cbs) return; - for (const [cb, opt] of cbs.entries()) { - try { - cb(...args); - } catch (err) { - console.error(err); - } - if (opt && opt.once) { - cbs.delete(cb); - } - } - }, - }; -}