diff --git a/_locales/en/messages.json b/_locales/en/messages.json index ea18706f..51dddb1a 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -468,6 +468,12 @@ "message": "Import", "description": "Label for the button to import a style ('edit' page) or all styles ('manage' page)" }, + "importPreprocessor": { + "message": "Style with a @preprocessor won't work in the classic mode. You can switch the editor to Usercss mode: 1) open the style manager, 2) enable \"as Usercss\" checkbox, 3) click \"Write new style\"\n\nImport now anyway?" + }, + "importPreprocessorTitle": { + "message": "Potential problem due to @preprocessor" + }, "importReplaceLabel": { "message": "Overwrite style", "description": "Label for the button to import and overwrite current style" diff --git a/background/usercss-helper.js b/background/usercss-helper.js index 00b3a99b..58dc2233 100644 --- a/background/usercss-helper.js +++ b/background/usercss-helper.js @@ -8,6 +8,7 @@ const usercssHelper = (() => { API_METHODS.configUsercssVars = configUsercssVars; API_METHODS.buildUsercss = build; + API_METHODS.buildUsercssMeta = buildMeta; API_METHODS.findUsercss = find; function buildMeta(style) { diff --git a/edit/sections-editor-section.js b/edit/sections-editor-section.js index 5b07875e..dce07e93 100644 --- a/edit/sections-editor-section.js +++ b/edit/sections-editor-section.js @@ -176,10 +176,9 @@ function createSection({ }); cm.on('paste', (cm, event) => { const text = event.clipboardData.getData('text') || ''; - if ( - text.includes('@-moz-document') && - text.replace(/\/\*[\s\S]*?(?:\*\/|$)/g, '') - .match(/@-moz-document[\s\r\n]+(url|url-prefix|domain|regexp)\(/) + if (/@-moz-document/i.test(text) && + /@-moz-document\s+(url|url-prefix|domain|regexp)\(/i + .test(text.replace(/\/\*([^*]|\*(?!\/))*(\*\/|$)/g, '')) ) { event.preventDefault(); showMozillaFormatImport(text); diff --git a/edit/sections-editor.js b/edit/sections-editor.js index e2e3678d..567a5bc4 100644 --- a/edit/sections-editor.js +++ b/edit/sections-editor.js @@ -48,7 +48,7 @@ function createSectionsEditor(editorBase) { let sectionOrder = ''; let headerOffset; // in compact mode the header is at the top so it reduces the available height - const ready = initSections(style.sections, {isFirstInit: true}); + const ready = initSections(style.sections, {pristine: true}); const livePreview = createLivePreview(); livePreview.show(Boolean(style.id)); @@ -333,24 +333,37 @@ function createSectionsEditor(editorBase) { 'Shift-Ctrl-Enter': () => doImport({replaceOldStyle: true}), }; - function doImport({replaceOldStyle = false}) { + async function doImport({replaceOldStyle = false}) { lockPageUI(true); - API.parseCss({code: popup.codebox.getValue().trim()}) - .then(({sections, errors}) => { + try { + const code = popup.codebox.getValue().trim(); + if (!/==userstyle==/i.test(code) || + !await getPreprocessor(code) || + await messageBox.confirm( + t('importPreprocessor'), 'pre-line', + t('importPreprocessorTitle')) + ) { + const {sections, errors} = await API.parseCss({code}); // shouldn't happen but just in case if (!sections.length || errors.length) { throw errors; } - if (replaceOldStyle) { - return replaceSections(sections); - } - return initSections(sections, {focusOn: false}); - }) - .then(() => { + await initSections(sections, { + replace: replaceOldStyle, + focusOn: replaceOldStyle ? 0 : false, + }); $('.dismiss').dispatchEvent(new Event('click')); - }) - .catch(showError) - .then(() => lockPageUI(false)); + } + } catch (err) { + showError(err); + } + lockPageUI(false); + } + + async function getPreprocessor(code) { + try { + return (await API.buildUsercssMeta({sourceCode: code})).usercssData.preprocessor; + } catch (e) {} } function lockPageUI(locked) { @@ -451,8 +464,14 @@ function createSectionsEditor(editorBase) { function initSections(originalSections, { focusOn = 0, - isFirstInit, + replace = false, + pristine = false, } = {}) { + if (replace) { + sections.forEach(s => s.remove(true)); + sections.length = 0; + container.textContent = ''; + } let done; const total = originalSections.length; originalSections = originalSections.slice(); @@ -464,7 +483,7 @@ function createSectionsEditor(editorBase) { const t0 = performance.now(); while (originalSections.length && performance.now() - t0 < 100) { insertSectionAfter(originalSections.shift(), undefined, forceRefresh); - if (isFirstInit) dirty.clear(); + if (pristine) dirty.clear(); if (focusOn !== false && sections[focusOn]) { sections[focusOn].cm.focus(); focusOn = false; @@ -572,36 +591,21 @@ function createSectionsEditor(editorBase) { updateSectionOrder(); } - function replaceSections(...args) { - for (const section of sections) { - section.remove(true); - } - sections.length = 0; - container.textContent = ''; - return initSections(...args); - } - - function replaceStyle(newStyle, codeIsUpdated) { + async function replaceStyle(newStyle, codeIsUpdated) { dirty.clear('name'); // FIXME: avoid recreating all editors? - reinit().then(() => { - Object.assign(style, newStyle); - updateHeader(); - dirty.clear(); - // Go from new style URL to edit style URL - if (location.href.indexOf('id=') === -1 && style.id) { - history.replaceState({}, document.title, 'edit.html?id=' + style.id); - $('#heading').textContent = t('editStyleHeading'); - } - livePreview.show(Boolean(style.id)); - updateLivePreview(); - }); - - function reinit() { - if (codeIsUpdated !== false) { - return replaceSections(newStyle.sections, {isFirstInit: true}); - } - return Promise.resolve(); + if (codeIsUpdated !== false) { + await initSections(newStyle.sections, {replace: true, pristine: true}); } + Object.assign(style, newStyle); + updateHeader(); + dirty.clear(); + // Go from new style URL to edit style URL + if (location.href.indexOf('id=') === -1 && style.id) { + history.replaceState({}, document.title, 'edit.html?id=' + style.id); + $('#heading').textContent = t('editStyleHeading'); + } + livePreview.show(Boolean(style.id)); + updateLivePreview(); } } diff --git a/msgbox/msgbox.css b/msgbox/msgbox.css index bbc70622..4f349613 100644 --- a/msgbox/msgbox.css +++ b/msgbox/msgbox.css @@ -56,6 +56,10 @@ text-align: left; } +#message-box.pre-line #message-box-contents { + white-space: pre-line; +} + #message-box-title { font-weight: bold; background-color: rgb(145, 208, 198); diff --git a/msgbox/msgbox.js b/msgbox/msgbox.js index a537da32..6a409f9f 100644 --- a/msgbox/msgbox.js +++ b/msgbox/msgbox.js @@ -168,10 +168,12 @@ messageBox.alert = (contents, className, title) => /** * @param {String|Node|Array} contents * @param {String} [className] like 'pre' for monospace font + * @param {String} [title] * @returns {Promise} resolves to true when confirmed */ -messageBox.confirm = (contents, className) => +messageBox.confirm = (contents, className, title) => messageBox({ + title, contents, className: `center ${className || ''}`, buttons: [t('confirmYes'), t('confirmNo')]