diff --git a/_locales/en/messages.json b/_locales/en/messages.json
index 84649365..4a88c107 100644
--- a/_locales/en/messages.json
+++ b/_locales/en/messages.json
@@ -1352,6 +1352,9 @@
"retrieveDropboxSync": {
"message": "Dropbox Import"
},
+ "saveAsTemplate": {
+ "message": "Save as template"
+ },
"search": {
"message": "Search",
"description": "Label before the search input field in the editor shown on Ctrl-F"
@@ -1812,10 +1815,6 @@
"usercssReplaceTemplateConfirmation": {
"message": "Replace the default template for new Usercss styles with the current code?"
},
- "usercssReplaceTemplateName": {
- "message": "Empty @name replaces the default template",
- "description": "The text shown after @name when creating a new Usercss style"
- },
"usercssReplaceTemplateSectionBody": {
"message": "Insert code here...",
"description": "The code placeholder comment in a new style created by clicking 'Write style' in the popup"
diff --git a/edit.html b/edit.html
index c74a4b66..b162045a 100644
--- a/edit.html
+++ b/edit.html
@@ -392,6 +392,9 @@
+
+
+
diff --git a/edit/edit.css b/edit/edit.css
index 11697142..0553f5bf 100644
--- a/edit/edit.css
+++ b/edit/edit.css
@@ -977,7 +977,7 @@ body.linter-disabled .hidden-unless-compact {
margin-top: 0;
}
-#options-wrapper .options-column:nth-child(2) {
+#options-wrapper .options-column:nth-child(n + 2) {
margin-top: .75rem;
}
@@ -1126,7 +1126,7 @@ body.linter-disabled .hidden-unless-compact {
.options-column > .usercss-only {
margin-bottom: 0;
}
- #options-wrapper .options-column:nth-child(2) {
+ #options-wrapper .options-column:nth-child(n + 2) {
margin-top: 0;
}
#options:not([open]),
diff --git a/edit/edit.js b/edit/edit.js
index 19a94ac7..eda76e9b 100644
--- a/edit/edit.js
+++ b/edit/edit.js
@@ -4,6 +4,7 @@
/* global SectionsEditor */
/* global SourceEditor */
/* global baseInit */
+/* global chromeSync */// storage-util.js
/* global clipString createHotkeyInput helpPopup */// util.js
/* global closeCurrentTab deepEqual sessionStore tryJSONparse */// toolbox.js
/* global cmFactory */
@@ -16,7 +17,10 @@
//#region init
baseInit.ready.then(async () => {
- await waitForSheet();
+ [editor.template] = await Promise.all([
+ editor.isUsercss && !editor.style.id && chromeSync.getLZValue(chromeSync.LZ_KEY.usercssTemplate),
+ waitForSheet(),
+ ]);
(editor.isUsercss ? SourceEditor : SectionsEditor)();
await editor.ready;
editor.ready = true;
diff --git a/edit/source-editor.js b/edit/source-editor.js
index 5683c519..b9ce6c66 100644
--- a/edit/source-editor.js
+++ b/edit/source-editor.js
@@ -17,20 +17,19 @@
function SourceEditor() {
const {style, /** @type DirtyReporter */dirty} = editor;
let savedGeneration;
- let placeholderName = '';
let prevMode = NaN;
$$remove('.sectioned-only');
$('#header').on('wheel', headerOnScroll);
$('#sections').textContent = '';
$('#sections').appendChild($create('.single-editor'));
-
- if (!style.id) setupNewStyle(style);
+ $('[i18n-text="saveAsTemplate"]').onclick = saveTemplate;
const cm = cmFactory.create($('.single-editor'));
const sectionFinder = MozSectionFinder(cm);
const sectionWidget = MozSectionWidget(cm, sectionFinder);
editor.livePreview.init(preprocess);
+ if (!style.id) setupNewStyle();
createMetaCompiler(meta => {
style.usercssData = meta;
style.name = meta.name;
@@ -75,13 +74,7 @@ function SourceEditor() {
}
showLog(res);
} catch (err) {
- const i = err.index;
- const isNameEmpty = i > 0 &&
- err.code === 'missingValue' &&
- sourceCode.slice(sourceCode.lastIndexOf('\n', i - 1), i).trim().endsWith('@name');
- return isNameEmpty
- ? saveTemplate(sourceCode)
- : showSaveError(err);
+ showSaveError(err);
}
},
scrollToEditor: () => {},
@@ -160,7 +153,7 @@ function SourceEditor() {
return name;
}
- async function setupNewStyle(style) {
+ function setupNewStyle() {
style.sections[0].code = ' '.repeat(prefs.get('editor.tabSize')) +
`/* ${t('usercssReplaceTemplateSectionBody')} */`;
let section = MozDocMapper.styleToCss(style);
@@ -177,17 +170,11 @@ function SourceEditor() {
@author Me
==/UserStyle== */
`.replace(/^\s+/gm, '');
-
- dirty.clear('sourceGeneration');
- style.sourceCode = '';
-
- placeholderName = `${style.name || t('usercssReplaceTemplateName')} - ${new Date().toLocaleString()}`;
- let code = await chromeSync.getLZValue(chromeSync.LZ_KEY.usercssTemplate);
- code = code || DEFAULT_CODE;
- code = code.replace(/@name(\s*)(?=[\r\n])/, (str, space) =>
- `${str}${space ? '' : ' '}${placeholderName}`);
+ style.name = [style.name, new Date().toLocaleString()].filter(Boolean).join(' - ');
// strip the last dummy section if any, add an empty line followed by the section
- style.sourceCode = code.replace(/\s*@-moz-document[^{]*{[^}]*}\s*$|\s+$/g, '') + '\n\n' + section;
+ style.sourceCode = (editor.template || DEFAULT_CODE)
+ .replace(/(@name)(?:([\t\x20]+).*|\n)/, (_, k, space) => `${k}${space || ' '}${style.name}`)
+ .replace(/\s*@-moz-document[^{]*{[^}]*}\s*$|\s+$/g, '') + '\n\n' + section;
cm.startOperation();
cm.setValue(style.sourceCode);
cm.clearHistory();
@@ -199,9 +186,7 @@ function SourceEditor() {
function updateMeta() {
const name = style.customName || style.name;
- if (name !== placeholderName) {
- $('#name').value = name;
- }
+ $('#name').value = name;
$('#enabled').checked = style.enabled;
$('#url').href = style.url;
editor.updateName();
@@ -236,9 +221,10 @@ function SourceEditor() {
}
}
- async function saveTemplate(code) {
+ async function saveTemplate() {
if (await messageBoxProxy.confirm(t('usercssReplaceTemplateConfirmation'))) {
const key = chromeSync.LZ_KEY.usercssTemplate;
+ const code = cm.getValue();
await chromeSync.setLZValue(key, code);
if (await chromeSync.getLZValue(key) !== code) {
messageBoxProxy.alert(t('syncStorageErrorSaving'));
diff --git a/js/localization.js b/js/localization.js
index b413e255..b34497eb 100644
--- a/js/localization.js
+++ b/js/localization.js
@@ -73,7 +73,6 @@ Object.assign(t, {
if (toInsert) {
node.insertBefore(toInsert, before || null);
}
- node.removeAttribute(name);
}
}
},