fix local name customization for usercss/legacy

This commit is contained in:
tophf 2020-10-11 18:12:06 +03:00
parent 34f899fc45
commit 2e1a903cc7
15 changed files with 192 additions and 210 deletions

View File

@ -250,6 +250,13 @@
"message": "Copy to clipboard", "message": "Copy to clipboard",
"description": "Tooltip for elements which can be copied" "description": "Tooltip for elements which can be copied"
}, },
"customNameHint": {
"message": "Enter a custom name here to rename the style in UI without breaking its updates"
},
"customNameResetHint": {
"message": "Stop using customized name, switch to the style's own name",
"description": "Tooltip of 'x' button shown in editor when changing the name input of a) styles updated from a URL i.e. not locally created, b) UserCSS styles"
},
"dateInstalled": { "dateInstalled": {
"message": "Date installed", "message": "Date installed",
"description": "Option text for the user to sort the style by install date" "description": "Option text for the user to sort the style by install date"

View File

@ -57,7 +57,7 @@
continue; continue;
} }
for (const part in PARTS) { for (const part in PARTS) {
const text = style[part]; const text = part === 'name' ? style.customName || style.name : style[part];
if (text && PARTS[part](text, rx, words, icase)) { if (text && PARTS[part](text, rx, words, icase)) {
results.push(id); results.push(id);
break; break;

View File

@ -61,6 +61,8 @@ const styleManager = (() => {
username: '' username: ''
}; };
const DELETE_IF_NULL = ['id', 'customName'];
handleLivePreviewConnections(); handleLivePreviewConnections();
return Object.assign({ return Object.assign({
@ -387,8 +389,10 @@ const styleManager = (() => {
if (!style.name) { if (!style.name) {
throw new Error('style name is empty'); throw new Error('style name is empty');
} }
if (style.id == null) { for (const key of DELETE_IF_NULL) {
delete style.id; if (style[key] == null) {
delete style[key];
}
} }
if (!style._id) { if (!style._id) {
style._id = uuidv4(); style._id = uuidv4();
@ -569,6 +573,16 @@ const styleManager = (() => {
touched = true; touched = true;
} }
} }
// upgrade the old way of customizing local names
const {originalName} = style;
if (originalName) {
touched = true;
if (originalName !== style.name) {
style.customName = style.name;
style.name = originalName;
}
delete style.originalName;
}
return touched; return touched;
} }
} }

View File

@ -116,7 +116,7 @@
} }
function reportSuccess(saved) { function reportSuccess(saved) {
log(STATES.UPDATED + ` #${style.id} ${style.name}`); log(STATES.UPDATED + ` #${style.id} ${style.customName || style.name}`);
const info = {updated: true, style: saved}; const info = {updated: true, style: saved};
if (port) port.postMessage(info); if (port) port.postMessage(info);
return info; return info;
@ -139,7 +139,7 @@
if (typeof error === 'object' && error.message) { if (typeof error === 'object' && error.message) {
error = error.message; error = error.message;
} }
log(STATES.SKIPPED + ` (${error}) #${style.id} ${style.name}`); log(STATES.SKIPPED + ` (${error}) #${style.id} ${style.customName || style.name}`);
const info = {error, STATES, style: getStyleWithNoCode(style)}; const info = {error, STATES, style: getStyleWithNoCode(style)};
if (port) port.postMessage(info); if (port) port.postMessage(info);
return info; return info;
@ -207,13 +207,6 @@
// keep current state // keep current state
delete json.enabled; delete json.enabled;
// keep local name customizations
if (style.originalName !== style.name && style.name !== json.name) {
delete json.name;
} else {
json.originalName = json.name;
}
const newStyle = Object.assign({}, style, json); const newStyle = Object.assign({}, style, json);
if (styleSectionsEqual(json, style, {checkSource: true})) { if (styleSectionsEqual(json, style, {checkSource: true})) {
// update digest even if save === false as there might be just a space added etc. // update digest even if save === false as there might be just a space added etc.

View File

@ -284,7 +284,13 @@
<h1 id="heading">&nbsp;</h1> <!-- nbsp allocates the actual height which prevents page shift --> <h1 id="heading">&nbsp;</h1> <!-- nbsp allocates the actual height which prevents page shift -->
<section id="basic-info"> <section id="basic-info">
<div id="basic-info-name"> <div id="basic-info-name">
<input id="name" class="style-contributor" spellcheck="false" required> <input id="name" class="style-contributor" spellcheck="false">
<a id="reset-name" href="#" i18n-title="customNameResetHint" tabindex="0" hidden>
<svg class="svg-icon" viewBox="0 0 20 20">
<polygon points="16.2,5.5 14.5,3.8 10,8.3 5.5,3.8 3.8,5.5 8.3,10 3.8,14.5
5.5,16.2 10,11.7 14.5,16.2 16.2,14.5 11.7,10 "/>
</svg>
</a>
<a id="url" target="_blank"><svg class="svg-icon"><use xlink:href="#svg-icon-external-link"/></svg></a> <a id="url" target="_blank"><svg class="svg-icon"><use xlink:href="#svg-icon-external-link"/></svg></a>
</div> </div>
<div id="basic-info-enabled"> <div id="basic-info-enabled">

View File

@ -315,7 +315,7 @@ CodeMirror.hint && (() => {
} }
// USO vars in usercss mode editor // USO vars in usercss mode editor
const vars = editor.getStyle().usercssData.vars; const vars = editor.style.usercssData.vars;
const list = vars ? const list = vars ?
Object.keys(vars).filter(name => name.startsWith(leftPart)) : []; Object.keys(vars).filter(name => name.startsWith(leftPart)) : [];
return { return {
@ -343,7 +343,7 @@ CodeMirror.hint && (() => {
string[start + 3] === '[' && string[start + 3] === '[' &&
string[pos - 3] === ']' && string[pos - 3] === ']' &&
string[pos - 4] === ']') { string[pos - 4] === ']') {
const vars = typeof editor !== 'undefined' && (editor.getStyle().usercssData || {}).vars; const vars = typeof editor !== 'undefined' && (editor.style.usercssData || {}).vars;
const name = vars && string.slice(start + 4, pos - 4); const name = vars && string.slice(start + 4, pos - 4);
if (vars && Object.hasOwnProperty.call(vars, name.endsWith('-rgb') ? name.slice(0, -4) : name)) { if (vars && Object.hasOwnProperty.call(vars, name.endsWith('-rgb') ? name.slice(0, -4) : name)) {
token[0] = USO_VALID_VAR; token[0] = USO_VALID_VAR;

View File

@ -88,6 +88,9 @@ label {
display: flex; display: flex;
align-items: center; align-items: center;
} }
#reset-name {
margin: 0 .25em 0 .5em;
}
#url { #url {
margin-left: 0.25rem; margin-left: 0.25rem;
} }
@ -813,11 +816,6 @@ body.linter-disabled .hidden-unless-compact {
margin-top: .75rem; margin-top: .75rem;
} }
.usercss #name {
background-color: #eee;
color: #888;
}
.single-editor { .single-editor {
height: 100%; height: 100%;
} }

View File

@ -1,7 +1,7 @@
/* global CodeMirror onDOMready prefs setupLivePrefs $ $$ $create t tHTML /* global CodeMirror onDOMready prefs setupLivePrefs $ $$ $create t tHTML
createSourceEditor sessionStorageHash getOwnTab FIREFOX API tryCatch createSourceEditor sessionStorageHash getOwnTab FIREFOX API tryCatch
closeCurrentTab messageBox debounce workerUtil closeCurrentTab messageBox debounce workerUtil
initBeautifyButton ignoreChromeError initBeautifyButton ignoreChromeError dirtyReporter
moveFocus msg createSectionsEditor rerouteHotkeys CODEMIRROR_THEMES */ moveFocus msg createSectionsEditor rerouteHotkeys CODEMIRROR_THEMES */
/* exported showCodeMirrorPopup editorWorker toggleContextMenuDelete */ /* exported showCodeMirrorPopup editorWorker toggleContextMenuDelete */
'use strict'; 'use strict';
@ -30,46 +30,72 @@ msg.onExtension(onRuntimeMessage);
preinit(); preinit();
(() => { (async function init() {
onDOMready().then(() => { const [style] = await Promise.all([
prefs.subscribe(['editor.keyMap'], showHotkeyInTooltip); initStyleData(),
addEventListener('showHotkeyInTooltip', showHotkeyInTooltip); onDOMready(),
showHotkeyInTooltip(); prefs.initializing,
]);
const usercss = isUsercss(style);
const dirty = dirtyReporter();
let wasDirty = false;
let nameTarget;
buildThemeElement(); prefs.subscribe(['editor.keyMap'], showHotkeyInTooltip);
buildKeymapElement(); addEventListener('showHotkeyInTooltip', showHotkeyInTooltip);
showHotkeyInTooltip();
setupLivePrefs(); buildThemeElement();
buildKeymapElement();
setupLivePrefs();
initNameArea(style, usercss);
initBeautifyButton($('#beautify'), () => editor.getEditors());
initResizeListener();
detectLayout();
updateTitle();
$('#heading').textContent = t(style.id ? 'editStyleHeading' : 'addStyleTitle');
$('#preview-label').classList.toggle('hidden', !style.id);
editor = (usercss ? createSourceEditor : createSectionsEditor)({
style,
dirty,
updateName,
toggleStyle,
}); });
dirty.onChange(updateDirty);
await editor.ready;
initEditor(); // enabling after init to prevent flash of validation failure on an empty name
$('#name').required = !usercss;
$('#save-button').onclick = editor.save;
function getCodeMirrorThemes() { function initNameArea(style, usercss) {
if (!chrome.runtime.getPackageDirectoryEntry) { const nameEl = $('#name');
const themes = [ const resetEl = $('#reset-name');
chrome.i18n.getMessage('defaultTheme'), const isCustomName = style.updateUrl || usercss;
...CODEMIRROR_THEMES nameTarget = isCustomName ? 'customName' : 'name';
]; nameEl.placeholder = t(usercss ? 'usercssEditorNamePlaceholder' : 'styleMissingName');
localStorage.codeMirrorThemes = themes.join(' '); nameEl.title = isCustomName ? t('customNameHint') : '';
return Promise.resolve(themes); nameEl.addEventListener('input', () => {
} updateName();
return new Promise(resolve => { resetEl.hidden = false;
chrome.runtime.getPackageDirectoryEntry(rootDir => {
rootDir.getDirectory('vendor/codemirror/theme', {create: false}, themeDir => {
themeDir.createReader().readEntries(entries => {
const themes = [
chrome.i18n.getMessage('defaultTheme')
].concat(
entries.filter(entry => entry.isFile)
.sort((a, b) => (a.name < b.name ? -1 : 1))
.map(entry => entry.name.replace(/\.css$/, ''))
);
localStorage.codeMirrorThemes = themes.join(' ');
resolve(themes);
});
});
});
}); });
resetEl.hidden = !style.customName;
resetEl.onclick = () => {
const style = editor.style;
nameEl.focus();
nameEl.select();
// trying to make it undoable via Ctrl-Z
if (!document.execCommand('insertText', false, style.name)) {
nameEl.value = style.name;
updateName();
}
style.customName = null; // to delete it from db
resetEl.hidden = true;
};
const enabledEl = $('#enabled');
enabledEl.onchange = () => updateEnabledness(enabledEl.checked);
} }
function findKeyForCommand(command, map) { function findKeyForCommand(command, map) {
@ -88,27 +114,8 @@ preinit();
} }
function buildThemeElement() { function buildThemeElement() {
const themeElement = $('#editor.theme'); CODEMIRROR_THEMES.unshift(chrome.i18n.getMessage('defaultTheme'));
const themeList = localStorage.codeMirrorThemes; $('#editor.theme').append(...CODEMIRROR_THEMES.map(s => $create('option', s)));
const optionsFromArray = options => {
const fragment = document.createDocumentFragment();
options.forEach(opt => fragment.appendChild($create('option', opt)));
themeElement.appendChild(fragment);
};
if (themeList) {
optionsFromArray(themeList.split(/\s+/));
} else {
// Chrome is starting up and shows our edit.html, but the background page isn't loaded yet
const theme = prefs.get('editor.theme');
optionsFromArray([theme === 'default' ? t('defaultTheme') : theme]);
getCodeMirrorThemes().then(() => {
const themes = (localStorage.codeMirrorThemes || '').split(/\s+/);
optionsFromArray(themes);
themeElement.selectedIndex = Math.max(0, themes.indexOf(theme));
});
}
} }
function buildKeymapElement() { function buildKeymapElement() {
@ -159,59 +166,56 @@ preinit();
} }
} }
function initEditor() { function initResizeListener() {
return Promise.all([ const {onBoundsChanged} = chrome.windows || {};
initStyleData(), if (onBoundsChanged) {
onDOMready(), // * movement is reported even if the window wasn't resized
prefs.initializing, // * fired just once when done so debounce is not needed
]) onBoundsChanged.addListener(wnd => {
.then(([style]) => { // getting the current window id as it may change if the user attached/detached the tab
const usercss = isUsercss(style); chrome.windows.getCurrent(ownWnd => {
$('#heading').textContent = t(style.id ? 'editStyleHeading' : 'addStyleTitle'); if (wnd.id === ownWnd.id) rememberWindowSize();
$('#name').placeholder = t(usercss ? 'usercssEditorNamePlaceholder' : 'styleMissingName');
$('#name').title = usercss ? t('usercssReplaceTemplateName') : '';
$('#preview-label').classList.toggle('hidden', !style.id);
initBeautifyButton($('#beautify'), () => editor.getEditors());
const {onBoundsChanged} = chrome.windows || {};
if (onBoundsChanged) {
// * movement is reported even if the window wasn't resized
// * fired just once when done so debounce is not needed
onBoundsChanged.addListener(wnd => {
// getting the current window id as it may change if the user attached/detached the tab
chrome.windows.getCurrent(ownWnd => {
if (wnd.id === ownWnd.id) rememberWindowSize();
});
});
}
window.addEventListener('resize', () => {
if (!onBoundsChanged) debounce(rememberWindowSize, 100);
detectLayout();
}); });
detectLayout();
editor = (usercss ? createSourceEditor : createSectionsEditor)({
style,
onTitleChanged: updateTitle
});
editor.dirty.onChange(updateDirty);
return Promise.resolve(editor.ready && editor.ready())
.then(updateDirty);
}); });
}
window.addEventListener('resize', () => {
if (!onBoundsChanged) debounce(rememberWindowSize, 100);
detectLayout();
});
} }
function updateTitle() { function toggleStyle() {
if (editor) { $('#enabled').checked = !style.enabled;
const styleName = editor.getStyle().name; updateEnabledness(!style.enabled);
const isDirty = editor.dirty.isDirty();
document.title = (isDirty ? '* ' : '') + styleName;
}
} }
function updateDirty() { function updateDirty() {
const isDirty = editor.dirty.isDirty(); const isDirty = dirty.isDirty();
document.body.classList.toggle('dirty', isDirty); if (wasDirty !== isDirty) {
$('#save-button').disabled = !isDirty; wasDirty = isDirty;
document.body.classList.toggle('dirty', isDirty);
$('#save-button').disabled = !isDirty;
}
updateTitle(); updateTitle();
} }
function updateEnabledness(enabled) {
dirty.modify('enabled', style.enabled, enabled);
style.enabled = enabled;
editor.updateLivePreview();
}
function updateName() {
if (!editor) return;
const {value} = $('#name');
dirty.modify('name', style[nameTarget] || style.name, value);
style[nameTarget] = value;
updateTitle({});
}
function updateTitle() {
document.title = `${dirty.isDirty() ? '* ' : ''}${style.customName || style.name}`;
}
})(); })();
function preinit() { function preinit() {
@ -295,7 +299,7 @@ function onRuntimeMessage(request) {
switch (request.method) { switch (request.method) {
case 'styleUpdated': case 'styleUpdated':
if ( if (
editor.getStyleId() === request.style.id && editor.style.id === request.style.id &&
!['editPreview', 'editPreviewEnd', 'editSave', 'config'] !['editPreview', 'editPreviewEnd', 'editSave', 'config']
.includes(request.reason) .includes(request.reason)
) { ) {
@ -309,7 +313,7 @@ function onRuntimeMessage(request) {
} }
break; break;
case 'styleDeleted': case 'styleDeleted':
if (editor.getStyleId() === request.style.id) { if (editor.style.id === request.style.id) {
document.removeEventListener('visibilitychange', beforeUnload); document.removeEventListener('visibilitychange', beforeUnload);
document.removeEventListener('beforeunload', beforeUnload); document.removeEventListener('beforeunload', beforeUnload);
closeCurrentTab(); closeCurrentTab();

View File

@ -1,41 +1,24 @@
/* global dirtyReporter showHelp toggleContextMenuDelete createSection /* global showHelp toggleContextMenuDelete createSection
CodeMirror linter createLivePreview showCodeMirrorPopup CodeMirror linter createLivePreview showCodeMirrorPopup
sectionsToMozFormat messageBox clipString sectionsToMozFormat messageBox clipString
rerouteHotkeys $ $$ $create t FIREFOX API $ $$ $create t FIREFOX API
debounce */ debounce */
/* exported createSectionsEditor */ /* exported createSectionsEditor */
'use strict'; 'use strict';
function createSectionsEditor({style, onTitleChanged}) { function createSectionsEditor(editorBase) {
const {style, dirty} = editorBase;
let INC_ID = 0; // an increment id that is used by various object to track the order let INC_ID = 0; // an increment id that is used by various object to track the order
const dirty = dirtyReporter();
const container = $('#sections'); const container = $('#sections');
const sections = []; const sections = [];
container.classList.add('section-editor'); container.classList.add('section-editor');
const nameEl = $('#name');
nameEl.addEventListener('input', () => {
dirty.modify('name', style.name, nameEl.value);
style.name = nameEl.value;
onTitleChanged();
});
const enabledEl = $('#enabled');
enabledEl.addEventListener('change', () => {
dirty.modify('enabled', style.enabled, enabledEl.checked);
style.enabled = enabledEl.checked;
updateLivePreview();
});
updateHeader(); updateHeader();
rerouteHotkeys(true);
$('#to-mozilla').addEventListener('click', showMozillaFormat); $('#to-mozilla').addEventListener('click', showMozillaFormat);
$('#to-mozilla-help').addEventListener('click', showToMozillaHelp); $('#to-mozilla-help').addEventListener('click', showToMozillaHelp);
$('#from-mozilla').addEventListener('click', () => showMozillaFormatImport()); $('#from-mozilla').addEventListener('click', () => showMozillaFormatImport());
$('#save-button').addEventListener('click', saveStyle);
document.addEventListener('wheel', scrollEntirePageOnCtrlShift, {passive: false}); document.addEventListener('wheel', scrollEntirePageOnCtrlShift, {passive: false});
CodeMirror.defaults.extraKeys['Shift-Ctrl-Wheel'] = 'scrollWindow'; CodeMirror.defaults.extraKeys['Shift-Ctrl-Wheel'] = 'scrollWindow';
@ -65,30 +48,27 @@ function createSectionsEditor({style, onTitleChanged}) {
let sectionOrder = ''; let sectionOrder = '';
let headerOffset; // in compact mode the header is at the top so it reduces the available height let headerOffset; // in compact mode the header is at the top so it reduces the available height
const initializing = initSections(style.sections.slice()); const ready = initSections(style.sections.slice());
const livePreview = createLivePreview(); const livePreview = createLivePreview();
livePreview.show(Boolean(style.id)); livePreview.show(Boolean(style.id));
return { return Object.assign({}, editorBase, {
ready: () => initializing, ready,
replaceStyle, replaceStyle,
dirty,
getStyle: () => style,
getEditors, getEditors,
scrollToEditor, scrollToEditor,
getStyleId: () => style.id,
getEditorTitle: cm => { getEditorTitle: cm => {
const index = sections.filter(s => !s.isRemoved()).findIndex(s => s.cm === cm); const index = sections.filter(s => !s.isRemoved()).findIndex(s => s.cm === cm);
return `${t('sectionCode')} ${index + 1}`; return `${t('sectionCode')} ${index + 1}`;
}, },
save: saveStyle, save,
toggleStyle,
nextEditor, nextEditor,
prevEditor, prevEditor,
closestVisible, closestVisible,
getSearchableInputs, getSearchableInputs,
}; updateLivePreview,
});
function fitToContent(section) { function fitToContent(section) {
const {cm, cm: {display: {wrapper, sizer}}} = section; const {cm, cm: {display: {wrapper, sizer}}} = section;
@ -246,14 +226,6 @@ function createSectionsEditor({style, onTitleChanged}) {
return sections.filter(s => !s.isRemoved()).map(s => s.cm); return sections.filter(s => !s.isRemoved()).map(s => s.cm);
} }
function toggleStyle() {
const newValue = !style.enabled;
dirty.modify('enabled', style.enabled, newValue);
style.enabled = newValue;
enabledEl.checked = newValue;
updateLivePreview();
}
function nextEditor(cm, cycle = true) { function nextEditor(cm, cycle = true) {
if (!cycle && findLast(sections, s => !s.isRemoved()).cm === cm) { if (!cycle && findLast(sections, s => !s.isRemoved()).cm === cm) {
return; return;
@ -417,7 +389,7 @@ function createSectionsEditor({style, onTitleChanged}) {
} }
function validate() { function validate() {
if (!nameEl.reportValidity()) { if (!$('#name').reportValidity()) {
messageBox.alert(t('styleMissingName')); messageBox.alert(t('styleMissingName'));
return false; return false;
} }
@ -435,7 +407,7 @@ function createSectionsEditor({style, onTitleChanged}) {
return true; return true;
} }
function saveStyle() { function save() {
if (!dirty.isDirty()) { if (!dirty.isDirty()) {
return; return;
} }
@ -464,10 +436,10 @@ function createSectionsEditor({style, onTitleChanged}) {
} }
function updateHeader() { function updateHeader() {
nameEl.value = style.name || ''; $('#name').value = style.customName || style.name || '';
enabledEl.checked = style.enabled !== false; $('#enabled').checked = style.enabled !== false;
$('#url').href = style.url || ''; $('#url').href = style.url || '';
onTitleChanged(); editorBase.updateName();
} }
function updateLivePreview() { function updateLivePreview() {
@ -609,6 +581,7 @@ function createSectionsEditor({style, onTitleChanged}) {
} }
function replaceStyle(newStyle, codeIsUpdated) { function replaceStyle(newStyle, codeIsUpdated) {
dirty.clear('name');
// FIXME: avoid recreating all editors? // FIXME: avoid recreating all editors?
reinit().then(() => { reinit().then(() => {
Object.assign(style, newStyle); Object.assign(style, newStyle);

View File

@ -1,4 +1,4 @@
/* global dirtyReporter /* global
createAppliesToLineWidget messageBox createAppliesToLineWidget messageBox
sectionsToMozFormat sectionsToMozFormat
createMetaCompiler linter createLivePreview cmFactory $ $create API prefs t createMetaCompiler linter createLivePreview cmFactory $ $create API prefs t
@ -6,17 +6,14 @@
/* exported createSourceEditor */ /* exported createSourceEditor */
'use strict'; 'use strict';
function createSourceEditor({style, onTitleChanged}) { function createSourceEditor(editorBase) {
$('#name').disabled = true; const {style, dirty} = editorBase;
$('#save-button').disabled = true;
$('#mozilla-format-container').remove(); $('#mozilla-format-container').remove();
$('#save-button').onclick = save;
$('#header').addEventListener('wheel', headerOnScroll); $('#header').addEventListener('wheel', headerOnScroll);
$('#sections').textContent = ''; $('#sections').textContent = '';
$('#sections').appendChild($create('.single-editor')); $('#sections').appendChild($create('.single-editor'));
const dirty = dirtyReporter();
// normalize style // normalize style
if (!style.id) setupNewStyle(style); if (!style.id) setupNewStyle(style);
@ -28,13 +25,6 @@ function createSourceEditor({style, onTitleChanged}) {
const livePreview = createLivePreview(preprocess); const livePreview = createLivePreview(preprocess);
livePreview.show(Boolean(style.id)); livePreview.show(Boolean(style.id));
$('#enabled').onchange = function () {
const value = this.checked;
dirty.modify('enabled', style.enabled, value);
style.enabled = value;
updateLivePreview();
};
cm.on('changes', () => { cm.on('changes', () => {
dirty.modify('sourceGeneration', savedGeneration, cm.changeGeneration()); dirty.modify('sourceGeneration', savedGeneration, cm.changeGeneration());
updateLivePreview(); updateLivePreview();
@ -162,14 +152,15 @@ function createSourceEditor({style, onTitleChanged}) {
} }
function updateMeta() { function updateMeta() {
$('#name').value = style.name; $('#name').value = style.customName || style.name;
$('#enabled').checked = style.enabled; $('#enabled').checked = style.enabled;
$('#url').href = style.url; $('#url').href = style.url;
onTitleChanged(); editorBase.updateName();
return cm.setPreprocessor((style.usercssData || {}).preprocessor); return cm.setPreprocessor((style.usercssData || {}).preprocessor);
} }
function replaceStyle(newStyle, codeIsUpdated) { function replaceStyle(newStyle, codeIsUpdated) {
dirty.clear('name');
const sameCode = newStyle.sourceCode === cm.getValue(); const sameCode = newStyle.sourceCode === cm.getValue();
if (sameCode) { if (sameCode) {
savedGeneration = cm.changeGeneration(); savedGeneration = cm.changeGeneration();
@ -210,14 +201,6 @@ function createSourceEditor({style, onTitleChanged}) {
} }
} }
function toggleStyle() {
const value = !style.enabled;
dirty.modify('enabled', style.enabled, value);
style.enabled = value;
updateMeta();
$('#enabled').dispatchEvent(new Event('change', {bubbles: true}));
}
function save() { function save() {
if (!dirty.isDirty()) return; if (!dirty.isDirty()) return;
const code = cm.getValue(); const code = cm.getValue();
@ -226,6 +209,7 @@ function createSourceEditor({style, onTitleChanged}) {
id: style.id, id: style.id,
enabled: style.enabled, enabled: style.enabled,
sourceCode: code, sourceCode: code,
customName: style.customName,
})) }))
.then(replaceStyle) .then(replaceStyle)
.catch(err => { .catch(err => {
@ -372,19 +356,17 @@ function createSourceEditor({style, onTitleChanged}) {
(mode.helperType || ''); (mode.helperType || '');
} }
return { return Object.assign({}, editorBase, {
ready: Promise.resolve(),
replaceStyle, replaceStyle,
dirty,
getStyle: () => style,
getEditors: () => [cm], getEditors: () => [cm],
scrollToEditor: () => {}, scrollToEditor: () => {},
getStyleId: () => style.id,
getEditorTitle: () => '', getEditorTitle: () => '',
save, save,
toggleStyle,
prevEditor: cm => nextPrevMozDocument(cm, -1), prevEditor: cm => nextPrevMozDocument(cm, -1),
nextEditor: cm => nextPrevMozDocument(cm, 1), nextEditor: cm => nextPrevMozDocument(cm, 1),
closestVisible: () => cm, closestVisible: () => cm,
getSearchableInputs: () => [] getSearchableInputs: () => [],
}; updateLivePreview,
});
} }

View File

@ -243,7 +243,7 @@
(!dup ? (!dup ?
Promise.resolve(true) : Promise.resolve(true) :
messageBox.confirm(t('styleInstallOverwrite', [ messageBox.confirm(t('styleInstallOverwrite', [
data.name, data.name + (dup.customName ? ` (${dup.customName})` : ''),
dupData.version, dupData.version,
data.version, data.version,
])) ]))

View File

@ -23,7 +23,7 @@ function configDialog(style) {
vars.forEach(renderValueState); vars.forEach(renderValueState);
return messageBox({ return messageBox({
title: `${style.name} v${data.version}`, title: `${style.customName || style.name} v${data.version}`,
className: 'config-dialog' + (isPopup ? ' stylus-popup' : ''), className: 'config-dialog' + (isPopup ? ' stylus-popup' : ''),
contents: [ contents: [
$create('.config-heading', data.supportURL && $create('.config-heading', data.supportURL &&

View File

@ -137,10 +137,14 @@ function initGlobalEvents() {
function showStyles(styles = [], matchUrlIds) { function showStyles(styles = [], matchUrlIds) {
const sorted = sorter.sort({ const sorted = sorter.sort({
styles: styles.map(style => ({ styles: styles.map(style => {
style, const name = style.customName || style.name || '';
name: (style.name || '').toLocaleLowerCase() + '\n' + style.name, return {
})), style,
// sort case-insensitively the whole list then sort dupes like `Foo` and `foo` case-sensitively
name: name.toLocaleLowerCase() + '\n' + name,
};
}),
}); });
let index = 0; let index = 0;
let firstRun = true; let firstRun = true;
@ -187,7 +191,7 @@ function showStyles(styles = [], matchUrlIds) {
} }
function createStyleElement({style, name}) { function createStyleElement({style, name: nameLC}) {
// query the sub-elements just once, then reuse the references // query the sub-elements just once, then reuse the references
if ((createStyleElement.parts || {}).newUI !== newUI.enabled) { if ((createStyleElement.parts || {}).newUI !== newUI.enabled) {
const entry = template[`style${newUI.enabled ? 'Compact' : ''}`]; const entry = template[`style${newUI.enabled ? 'Compact' : ''}`];
@ -216,8 +220,9 @@ function createStyleElement({style, name}) {
} }
const parts = createStyleElement.parts; const parts = createStyleElement.parts;
const configurable = style.usercssData && style.usercssData.vars && Object.keys(style.usercssData.vars).length > 0; const configurable = style.usercssData && style.usercssData.vars && Object.keys(style.usercssData.vars).length > 0;
const name = style.customName || style.name;
parts.checker.checked = style.enabled; parts.checker.checked = style.enabled;
parts.nameLink.textContent = tWordBreak(style.name); parts.nameLink.textContent = tWordBreak(name);
parts.nameLink.href = parts.editLink.href = parts.editHrefBase + style.id; parts.nameLink.href = parts.editLink.href = parts.editHrefBase + style.id;
parts.homepage.href = parts.homepage.title = style.url || ''; parts.homepage.href = parts.homepage.title = style.url || '';
if (!newUI.enabled) { if (!newUI.enabled) {
@ -234,7 +239,7 @@ function createStyleElement({style, name}) {
const entry = parts.entry.cloneNode(true); const entry = parts.entry.cloneNode(true);
entry.id = ENTRY_ID_PREFIX_RAW + style.id; entry.id = ENTRY_ID_PREFIX_RAW + style.id;
entry.styleId = style.id; entry.styleId = style.id;
entry.styleNameLowerCase = name || style.name.toLocaleLowerCase(); entry.styleNameLowerCase = nameLC || name.toLocaleLowerCase() + '\n' + name;
entry.styleMeta = style; entry.styleMeta = style;
entry.className = parts.entryClassBase + ' ' + entry.className = parts.entryClassBase + ' ' +
(style.enabled ? 'enabled' : 'disabled') + (style.enabled ? 'enabled' : 'disabled') +
@ -437,7 +442,7 @@ Object.assign(handleEvent, {
animateElement(entry); animateElement(entry);
messageBox({ messageBox({
title: t('deleteStyleConfirm'), title: t('deleteStyleConfirm'),
contents: entry.styleMeta.name, contents: entry.styleMeta.customName || entry.styleMeta.name,
className: 'danger center', className: 'danger center',
buttons: [t('confirmDelete'), t('confirmCancel')], buttons: [t('confirmDelete'), t('confirmCancel')],
}) })

View File

@ -130,7 +130,7 @@ const sorter = (() => {
const sorted = sort({ const sorted = sort({
styles: current.map(entry => ({ styles: current.map(entry => ({
entry, entry,
name: entry.styleNameLowerCase + '\n' + entry.styleMeta.name, name: entry.styleNameLowerCase,
style: entry.styleMeta, style: entry.styleMeta,
})) }))
}); });

View File

@ -310,7 +310,7 @@ function sortStyles(entries) {
return entries.sort(({styleMeta: a}, {styleMeta: b}) => return entries.sort(({styleMeta: a}, {styleMeta: b}) =>
Boolean(a.frameUrl) - Boolean(b.frameUrl) || Boolean(a.frameUrl) - Boolean(b.frameUrl) ||
enabledFirst && Boolean(b.enabled) - Boolean(a.enabled) || enabledFirst && Boolean(b.enabled) - Boolean(a.enabled) ||
a.name.localeCompare(b.name)); (a.customName || a.name).localeCompare(b.customName || b.name));
} }
function showStyles(frameResults) { function showStyles(frameResults) {
@ -408,7 +408,7 @@ function createStyleElement(style) {
$('.checker', entry).checked = style.enabled; $('.checker', entry).checked = style.enabled;
const styleName = $('.style-name', entry); const styleName = $('.style-name', entry);
styleName.lastChild.textContent = style.name; styleName.lastChild.textContent = style.customName || style.name;
setTimeout(() => { setTimeout(() => {
styleName.title = entry.styleMeta.sloppy ? styleName.title = entry.styleMeta.sloppy ?
t('styleNotAppliedRegexpProblemTooltip') : t('styleNotAppliedRegexpProblemTooltip') :