diff --git a/.eslintrc b/.eslintrc index 6eb52b33..bcfa68e3 100644 --- a/.eslintrc +++ b/.eslintrc @@ -48,8 +48,10 @@ globals: tHTML: false tNodeList: false tDocLoader: false + tWordBreak: false # dom.js onDOMready: false + onDOMscriptReady: false scrollElementIntoView: false enforceInputRange: false animateElement: false diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 77666693..36344fa9 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -105,7 +105,11 @@ }, "configureStyle": { "message": "Configure", - "description": "Label for the button to configure userstyle" + "description": "Label for the button to configure usercss userstyle" + }, + "configureStyleOnHomepage": { + "message": "Configure on homepage", + "description": "Label for the button to configure userstyles.org userstyle" }, "checkForUpdate": { "message": "Check for update", diff --git a/edit.html b/edit.html index c1966108..476c571d 100644 --- a/edit.html +++ b/edit.html @@ -33,6 +33,7 @@ + @@ -67,8 +68,11 @@ + + + + - diff --git a/edit/codemirror-default.js b/edit/codemirror-default.js index e68d9f35..3a619cc3 100644 --- a/edit/codemirror-default.js +++ b/edit/codemirror-default.js @@ -25,12 +25,12 @@ styleActiveLine: true, theme: 'default', keyMap: prefs.get('editor.keyMap'), - extraKeys: { + extraKeys: Object.assign(CodeMirror.defaults.extraKeys || {}, { // independent of current keyMap 'Alt-Enter': 'toggleStyle', 'Alt-PageDown': 'nextEditor', 'Alt-PageUp': 'prevEditor' - }, + }), maxHighlightLength: 100e3, }; diff --git a/edit/codemirror-editing-hooks.js b/edit/codemirror-editing-hooks.js index 46550b8e..5f2d16c0 100644 --- a/edit/codemirror-editing-hooks.js +++ b/edit/codemirror-editing-hooks.js @@ -5,7 +5,8 @@ global save toggleStyle setupAutocomplete makeSectionVisible getSectionForChild */ 'use strict'; -onDOMready().then(() => { +onDOMscriptReady('/codemirror.js').then(() => { + CodeMirror.defaults.lint = linterConfig.getForCodeMirror(); const COMMANDS = { @@ -46,11 +47,16 @@ onDOMready().then(() => { // cm.state.search for last used 'find' let searchState; - // N.B. the event listener should be registered before setupLivePrefs() - $('#options').addEventListener('change', onOptionElementChanged); - buildOptionsElements(); - setupLivePrefs(); - rerouteHotkeys(true); + onDOMready().then(() => { + prefs.subscribe(['editor.keyMap'], showKeyInSaveButtonTooltip); + showKeyInSaveButtonTooltip(); + + // N.B. the event listener should be registered before setupLivePrefs() + $('#options').addEventListener('change', onOptionElementChanged); + buildOptionsElements(); + + rerouteHotkeys(true); + }); return; @@ -677,4 +683,23 @@ onDOMready().then(() => { }); }); } + + function showKeyInSaveButtonTooltip(prefName, value) { + $('#save-button').title = findKeyForCommand('save', value); + } + + function findKeyForCommand(command, mapName = CodeMirror.defaults.keyMap) { + const map = CodeMirror.keyMap[mapName]; + let key = Object.keys(map).find(k => map[k] === command); + if (key) { + return key; + } + for (const ft of Array.isArray(map.fallthrough) ? map.fallthrough : [map.fallthrough]) { + key = ft && findKeyForCommand(command, ft); + if (key) { + return key; + } + } + return ''; + } }); diff --git a/edit/colorpicker-helper.js b/edit/colorpicker-helper.js index ef53452a..03714092 100644 --- a/edit/colorpicker-helper.js +++ b/edit/colorpicker-helper.js @@ -1,34 +1,23 @@ /* global CodeMirror loadScript editors showHelp */ 'use strict'; -// eslint-disable-next-line no-var -var initColorpicker = () => { +onDOMscriptReady('/colorview.js').then(() => { initOverlayHooks(); onDOMready().then(() => { $('#colorpicker-settings').onclick = configureColorpicker; }); - const scripts = [ - '/vendor-overwrites/colorpicker/colorpicker.css', - '/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); - - function colorpickerOnDemand(id, enabled) { - return loadScript(enabled && scripts) - .then(() => setColorpickerOption(id, enabled)); - } + prefs.subscribe(['editor.colorpicker'], setColorpickerOption); + setColorpickerOption(null, prefs.get('editor.colorpicker')); function setColorpickerOption(id, enabled) { 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 = defaults.extraKeys || {}; defaults.extraKeys[keyName] = 'colorpicker'; } defaults.colorpicker = { @@ -45,6 +34,11 @@ var initColorpicker = () => { }, }, }; + } else { + CodeMirror.modeExtensions.css.unregisterColorviewHooks(); + if (defaults.extraKeys) { + delete defaults.extraKeys[keyName]; + } } // on page load runs before CodeMirror.setOption is defined editors.forEach(cm => cm.setOption('colorpicker', defaults.colorpicker)); @@ -162,4 +156,4 @@ var initColorpicker = () => { return style; } } -}; +}); diff --git a/edit/edit.css b/edit/edit.css index 9f8b7f4f..6f6645e1 100644 --- a/edit/edit.css +++ b/edit/edit.css @@ -576,9 +576,6 @@ html:not(.usercss) .applies-to li:last-child .add-applies-to { top: 1em; margin: 1ex 0; } -.firefox .beautify-options > label input { - top: 1px; -} .beautify-options:after { clear: both; display: block; @@ -808,10 +805,4 @@ html:not(.usercss) .usercss-only, flex-wrap: wrap; white-space: normal; } -} - -@supports (-moz-appearance: none) { - #header button { - padding: 0 3px 2px; - } -} +} \ No newline at end of file diff --git a/edit/edit.js b/edit/edit.js index 0e95c192..873841a3 100644 --- a/edit/edit.js +++ b/edit/edit.js @@ -3,8 +3,6 @@ global CodeMirror parserlib loadScript global CSSLint initLint linterConfig updateLintReport renderLintReport updateLinter global mozParser createSourceEditor global closeCurrentTab regExpTester messageBox -global initColorpicker -global initCollapsibles global setupCodeMirror global beautify global initWithSectionStyle addSections removeSection getSectionsHashes @@ -17,8 +15,6 @@ let dirty = {}; // array of all CodeMirror instances const editors = []; let saveSizeOnClose; -// use browser history back when 'back to manage' is clicked -let useHistoryBack; // direct & reverse mapping of @-moz-document keywords and internal property names const propertyToCss = {urls: 'url', urlPrefixes: 'url-prefix', domains: 'domain', regexps: 'regexp'}; @@ -26,40 +22,27 @@ const CssToProperty = {'url': 'urls', 'url-prefix': 'urlPrefixes', 'domain': 'do let editor; -preinit(); window.onbeforeunload = beforeUnload; chrome.runtime.onMessage.addListener(onRuntimeMessage); +preinit(); + Promise.all([ - initStyleData().then(style => { - styleId = style.id; - sessionStorage.justEditedStyleId = styleId; - // we set "usercss" class on when
is empty - // so there'll be no flickering of the elements that depend on it - if (isUsercss(style)) { - document.documentElement.classList.add('usercss'); - } - // strip URL parameters when invoked for a non-existent id - if (!styleId) { - history.replaceState({}, document.title, location.pathname); - } - return style; - }), + initStyleData(), onDOMready(), - onBackgroundReady(), ]) -.then(([style]) => Promise.all([ - style, - initColorpicker(), - initCollapsibles(), - initHooksCommon(), -])) .then(([style]) => { + setupLivePrefs(); + const usercss = isUsercss(style); $('#heading').textContent = t(styleId ? 'editStyleHeading' : 'addStyleTitle'); $('#name').placeholder = t(usercss ? 'usercssEditorNamePlaceholder' : 'styleMissingName'); $('#name').title = usercss ? t('usercssReplaceTemplateName') : ''; + + $('#beautify').onclick = beautify; $('#lint').addEventListener('scroll', hideLintHeaderOnScroll, {passive: true}); + window.addEventListener('resize', () => debounce(rememberWindowSize, 100)); + if (usercss) { editor = createSourceEditor(style); } else { @@ -99,30 +82,6 @@ function preinit() { 'vendor/codemirror/theme/' + prefs.get('editor.theme') + '.css' })); - // forcefully break long labels in aligned options to prevent the entire block layout from breaking - onDOMready().then(() => new Promise(requestAnimationFrame)).then(() => { - const maxWidth2ndChild = $$('#options .aligned > :nth-child(2)') - .sort((a, b) => b.offsetWidth - a.offsetWidth)[0].offsetWidth; - const widthFor1stChild = $('#options').offsetWidth - maxWidth2ndChild; - if (widthFor1stChild > 50) { - for (const el of $$('#options .aligned > :nth-child(1)')) { - if (el.offsetWidth > widthFor1stChild) { - el.style.cssText = 'word-break: break-all; hyphens: auto;'; - } - } - } else { - const width = $('#options').clientWidth; - document.head.appendChild($create('style', ` - #options .aligned > nth-child(1) { - max-width: 70px; - } - #options .aligned > nth-child(2) { - max-width: ${width - 70}px; - } - `)); - } - }); - if (chrome.windows) { queryTabs({currentWindow: true}).then(tabs => { const windowId = tabs[0].windowId; @@ -151,7 +110,18 @@ function preinit() { getOwnTab().then(tab => { const ownTabId = tab.id; - useHistoryBack = sessionStorageHash('manageStylesHistory').value[ownTabId] === location.href; + + // use browser history back when 'back to manage' is clicked + if (sessionStorageHash('manageStylesHistory').value[ownTabId] === location.href) { + onDOMready().then(() => { + $('#cancel-button').onclick = event => { + event.stopPropagation(); + event.preventDefault(); + history.back(); + }; + }); + } + // no windows on android if (!chrome.windows) { return; } @@ -258,9 +228,20 @@ function initStyleData() { ) ], }); - return !id ? - Promise.resolve(createEmptyStyle()) : - getStylesSafe({id}).then(([style]) => style || createEmptyStyle()); + return getStylesSafe({id: id || -1}) + .then(([style = createEmptyStyle()]) => { + styleId = sessionStorage.justEditedStyleId = style.id; + // we set "usercss" class on when is empty + // so there'll be no flickering of the elements that depend on it + if (isUsercss(style)) { + document.documentElement.classList.add('usercss'); + } + // strip URL parameters when invoked for a non-existent id + if (!styleId) { + history.replaceState({}, document.title, location.pathname); + } + return style; + }); } function initHooks() { @@ -293,40 +274,6 @@ function initHooks() { } // common for usercss and classic -function initHooksCommon() { - $('#cancel-button').addEventListener('click', goBackToManage); - $('#beautify').addEventListener('click', beautify); - - prefs.subscribe(['editor.keyMap'], showKeyInSaveButtonTooltip); - showKeyInSaveButtonTooltip(); - - window.addEventListener('resize', () => debounce(rememberWindowSize, 100)); - - function goBackToManage(event) { - if (useHistoryBack) { - event.stopPropagation(); - event.preventDefault(); - history.back(); - } - } - function showKeyInSaveButtonTooltip(prefName, value) { - $('#save-button').title = findKeyForCommand('save', value); - } - function findKeyForCommand(command, mapName = CodeMirror.defaults.keyMap) { - const map = CodeMirror.keyMap[mapName]; - let key = Object.keys(map).find(k => map[k] === command); - if (key) { - return key; - } - for (const ft of Array.isArray(map.fallthrough) ? map.fallthrough : [map.fallthrough]) { - key = ft && findKeyForCommand(command, ft); - if (key) { - return key; - } - } - return ''; - } -} function onChange(event) { const node = event.target; diff --git a/global.css b/global.css index 577d2253..7bef2323 100644 --- a/global.css +++ b/global.css @@ -1,3 +1,39 @@ +button { + -webkit-appearance: none; + -moz-appearance: none; + user-select: none; + padding: 2px 7px; + border: 1px solid hsl(0, 0%, 62%); + font: 400 13.3333px Arial; + color: #000; + background-color: hsl(0, 0%, 100%); + background: url(../images/button.png)repeat-x; + background-size: 100% 100%; + transition: background-color .25s, border-color .25s; +} + +button:hover { + background-color: hsl(0, 0%, 95%); + border-color: hsl(0, 0%, 52%); +} + +/* For some odd reason these hovers appear lighter than all other button hovers in every browser */ +#message-box-buttons button:hover { + background-color: hsl(0, 0%, 90%); + border-color: hsl(0, 0%, 50%); +} + +input:not([type]) { + background: #fff; + color: #000; + height: 22px; + min-height: 22px!important; + line-height: 22px; + padding: 0 3px; + font: 400 13.3333px Arial; + border: 1px solid hsl(0, 0%, 66%); +} + .svg-icon.checked { position: absolute; height: 8px; @@ -13,12 +49,11 @@ input[type="checkbox"]:not(.slider):checked + .svg-icon.checked { } input[type="checkbox"]:not(.slider) { + -webkit-appearance: none; + -moz-appearance: none; position: absolute; left: 0; top: 0; - -moz-appearance: none; - -webkit-appearance: none; - pointer-events: none; border: 1px solid hsl(0, 0%, 46%); height: 12px; width: 12px; @@ -63,17 +98,14 @@ select { -moz-appearance: none; -webkit-appearance: none; height: 22px; - color: currentColor; + font: 400 13.3333px Arial; + color: #000; background-color: transparent; border: 1px solid hsl(0, 0%, 66%); padding: 0 20px 0 6px; transition: color .5s; } -.firefox select { - padding: 0 20px 0 2px; -} - .select-resizer { display: inline-flex!important; cursor: default; @@ -86,7 +118,7 @@ select { display: inline-flex; height: 14px; width: 14px; - fill: currentColor; + fill: #000; position: absolute; top: 4px; right: 4px; @@ -101,12 +133,38 @@ select { .moz-appearance-bug input[type="checkbox"] { -moz-appearance: checkbox !important; } - - ::-moz-focus-inner { - border: 0; + + .firefox select { + font-size: 13px; + padding: 0 20px 0 2px; + line-height: 22px!important; } svg { transform: scale(1); /* de-blur */ } + +/* We can customize everything about number inputs except arrows. They're horrible in Linux FF, so we'll hide them unless hovered or focused. */ + .firefox.non-windows input[type=number] { + -moz-appearance: textfield; + background: #fff; + color: #000; + border: 1px solid hsl(0, 0%, 66%); + } + + .firefox.non-windows input[type="number"]:hover, + .firefox.non-windows input[type="number"]:focus { + -moz-appearance: number-input; + } + +/* Firefox cannot handle fractions in font-size */ + .firefox button:not(.install) { + font-size: 13px; + line-height: 13px; + padding: 3px 7px; + } + + .firefox.moz-appearance-bug button:not(.install) { + padding: 2px 4px; + } } diff --git a/images/button.png b/images/button.png new file mode 100644 index 00000000..19d23892 Binary files /dev/null and b/images/button.png differ diff --git a/js/dom.js b/js/dom.js index c9a6246f..fc0abbbe 100644 --- a/js/dom.js +++ b/js/dom.js @@ -55,6 +55,7 @@ $$.remove = (selector, base = document) => { onDOMready().then(() => { $.remove('#firefox-transitions-bug-suppressor'); + initCollapsibles(); }); if (!chrome.app && chrome.windows) { @@ -278,9 +279,13 @@ function $createLink(href = '', content) { } +// makes