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
This commit is contained in:
parent
8128100cef
commit
906508832b
|
@ -378,8 +378,8 @@
|
||||||
"editorCodeLabel": {
|
"editorCodeLabel": {
|
||||||
"message": "Code"
|
"message": "Code"
|
||||||
},
|
},
|
||||||
"editorSettingLabel": {
|
"editorSettings": {
|
||||||
"message": "Settings"
|
"message": "Editor settings"
|
||||||
},
|
},
|
||||||
"enableStyleLabel": {
|
"enableStyleLabel": {
|
||||||
"message": "Enable",
|
"message": "Enable",
|
||||||
|
@ -1449,6 +1449,10 @@
|
||||||
"message": "Sections",
|
"message": "Sections",
|
||||||
"description": "Header for the table of contents block listing style section names in the left panel of the classic editor"
|
"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": {
|
"shortcuts": {
|
||||||
"message": "Shortcuts",
|
"message": "Shortcuts",
|
||||||
"description": "Go to shortcut configuration"
|
"description": "Go to shortcut configuration"
|
||||||
|
@ -1620,6 +1624,10 @@
|
||||||
"message": "Save",
|
"message": "Save",
|
||||||
"description": "Label for save button for style editing"
|
"description": "Label for save button for style editing"
|
||||||
},
|
},
|
||||||
|
"styleSettings": {
|
||||||
|
"message": "Style settings",
|
||||||
|
"description": "Label/title for style settings dialog"
|
||||||
|
},
|
||||||
"styleToMozillaFormatHelp": {
|
"styleToMozillaFormatHelp": {
|
||||||
"message": "The Mozilla format of the code can be submitted to userstyles.org and used with the classic Stylish for Firefox",
|
"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"
|
"description": "Help info for the Mozilla format header section that converts the code to/from Mozilla format"
|
||||||
|
|
102
edit.html
102
edit.html
|
@ -17,7 +17,6 @@
|
||||||
<script src="content/apply.js"></script>
|
<script src="content/apply.js"></script>
|
||||||
|
|
||||||
<script src="js/sections-util.js"></script>
|
<script src="js/sections-util.js"></script>
|
||||||
<script src="js/event-emitter.js"></script>
|
|
||||||
<script src="edit/codemirror-themes.js"></script> <!-- must precede base.js -->
|
<script src="edit/codemirror-themes.js"></script> <!-- must precede base.js -->
|
||||||
<script src="edit/base.js"></script>
|
<script src="edit/base.js"></script>
|
||||||
|
|
||||||
|
@ -62,7 +61,6 @@
|
||||||
<script src="edit/sections-editor-section.js"></script>
|
<script src="edit/sections-editor-section.js"></script>
|
||||||
<script src="edit/sections-editor.js"></script>
|
<script src="edit/sections-editor.js"></script>
|
||||||
<script src="edit/usw-integration.js"></script>
|
<script src="edit/usw-integration.js"></script>
|
||||||
<script src="edit/settings.js"></script>
|
|
||||||
<script src="edit/edit.js"></script>
|
<script src="edit/edit.js"></script>
|
||||||
|
|
||||||
<template data-id="appliesTo">
|
<template data-id="appliesTo">
|
||||||
|
@ -216,11 +214,11 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template data-id="keymapHelp">
|
<template data-id="keymapHelp">
|
||||||
<table class="keymap-list">
|
<table class="keymap-list can-close-on-esc">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th><input i18n-placeholder="helpKeyMapHotkey" type="search" class="can-close-on-esc"></th>
|
<th><input i18n-placeholder="helpKeyMapHotkey" type="search"></th>
|
||||||
<th><input i18n-placeholder="helpKeyMapCommand" type="search" class="can-close-on-esc" spellcheck="false"></th>
|
<th><input i18n-placeholder="helpKeyMapCommand" type="search"></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
@ -232,6 +230,44 @@
|
||||||
</table>
|
</table>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<template data-id="styleSettings">
|
||||||
|
<div>
|
||||||
|
<fieldset class="style-settings can-close-on-esc">
|
||||||
|
<label i18n-text="styleUpdateUrlLabel">
|
||||||
|
<input id="ss-update-url" type="text">
|
||||||
|
</label>
|
||||||
|
<div>
|
||||||
|
<div i18n-text="installPreferSchemeLabel"></div>
|
||||||
|
<label i18n-text-append="installPreferSchemeNone">
|
||||||
|
<input name="ss-scheme" type="radio" value="none">
|
||||||
|
</label>
|
||||||
|
<label i18n-text-append="installPreferSchemeDark">
|
||||||
|
<input name="ss-scheme" type="radio" value="dark">
|
||||||
|
</label>
|
||||||
|
<label i18n-text-append="installPreferSchemeLight">
|
||||||
|
<input name="ss-scheme" type="radio" value="light">
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<label i18n-text="styleIncludeLabel">
|
||||||
|
<textarea id="ss-inclusions" spellcheck="false"
|
||||||
|
placeholder="*://site1.com/* *://site2.com/*"></textarea>
|
||||||
|
</label>
|
||||||
|
<label i18n-text="styleExcludeLabel">
|
||||||
|
<textarea id="ss-exclusions" spellcheck="false"
|
||||||
|
placeholder="*://site1.com/* *://site2.com/*"></textarea>
|
||||||
|
</label>
|
||||||
|
</fieldset>
|
||||||
|
<div class="buttons">
|
||||||
|
<button id="ss-save" i18n-text="confirmSave" disabled></button>
|
||||||
|
<label i18n-title="configOnChangeTooltip" i18n-text-append="configOnChange">
|
||||||
|
<input id="config.autosave" type="checkbox">
|
||||||
|
<svg class="svg-icon checked"><use xlink:href="#svg-icon-checked"/></svg>
|
||||||
|
</label>
|
||||||
|
<button id="ss-close" i18n-text="confirmClose"></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<link href="vendor/codemirror/lib/codemirror.css" rel="stylesheet">
|
<link href="vendor/codemirror/lib/codemirror.css" rel="stylesheet">
|
||||||
<link href="vendor/codemirror/addon/dialog/dialog.css" rel="stylesheet">
|
<link href="vendor/codemirror/addon/dialog/dialog.css" rel="stylesheet">
|
||||||
<link href="vendor/codemirror/addon/fold/foldgutter.css" rel="stylesheet">
|
<link href="vendor/codemirror/addon/fold/foldgutter.css" rel="stylesheet">
|
||||||
|
@ -241,8 +277,6 @@
|
||||||
<link href="js/color/color-picker.css" rel="stylesheet">
|
<link href="js/color/color-picker.css" rel="stylesheet">
|
||||||
<link href="edit/codemirror-default.css" rel="stylesheet">
|
<link href="edit/codemirror-default.css" rel="stylesheet">
|
||||||
<link href="edit/edit.css" rel="stylesheet">
|
<link href="edit/edit.css" rel="stylesheet">
|
||||||
<link rel="stylesheet" href="edit/tab.css">
|
|
||||||
<link rel="stylesheet" href="edit/settings.css">
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body id="stylus-edit">
|
<body id="stylus-edit">
|
||||||
|
@ -278,7 +312,8 @@
|
||||||
<div>
|
<div>
|
||||||
<button id="save-button" i18n-text="styleSaveLabel" data-hotkey-tooltip="save" disabled></button>
|
<button id="save-button" i18n-text="styleSaveLabel" data-hotkey-tooltip="save" disabled></button>
|
||||||
<button id="beautify" i18n-text="styleBeautify"></button>
|
<button id="beautify" i18n-text="styleBeautify"></button>
|
||||||
<a href="manage.html" tabindex="-1"><button id="cancel-button" i18n-text="styleCancelEditLabel"></button></a>
|
<button id="style-settings-btn" i18n-text="settings"></button>
|
||||||
|
<button id="cancel-button" i18n-title="styleCancelEditLabel">↩</button>
|
||||||
</div>
|
</div>
|
||||||
<div id="mozilla-format-buttons" class="sectioned-only">
|
<div id="mozilla-format-buttons" class="sectioned-only">
|
||||||
<button id="from-mozilla" i18n-text="importLabel"></button>
|
<button id="from-mozilla" i18n-text="importLabel"></button>
|
||||||
|
@ -291,7 +326,7 @@
|
||||||
</section>
|
</section>
|
||||||
<div id="details-wrapper">
|
<div id="details-wrapper">
|
||||||
<details id="options" data-pref="editor.options.expanded" class="ignore-pref-if-compact">
|
<details id="options" data-pref="editor.options.expanded" class="ignore-pref-if-compact">
|
||||||
<summary><h2 id="options-heading" i18n-text="optionsHeading"></h2></summary>
|
<summary><h2 id="options-heading" i18n-text="editorSettings"></h2></summary>
|
||||||
<div id="options-wrapper">
|
<div id="options-wrapper">
|
||||||
<div class="options-column">
|
<div class="options-column">
|
||||||
<div class="option">
|
<div class="option">
|
||||||
|
@ -437,53 +472,7 @@
|
||||||
target="_blank"></a>
|
target="_blank"></a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="main tab-container">
|
<section id="sections"></section>
|
||||||
<div class="tab-bar">
|
|
||||||
<div class="tab-bar-item active" i18n-text="editorCodeLabel"></div>
|
|
||||||
<div class="tab-bar-item" i18n-text="editorSettingLabel"></div>
|
|
||||||
</div>
|
|
||||||
<div class="tab-panel">
|
|
||||||
<section id="sections" class="active"></section>
|
|
||||||
<fieldset class="style-settings" disabled>
|
|
||||||
<!-- <label class="style-origin">
|
|
||||||
<span class="form-label" i18n-text="styleOriginLabel"></span>
|
|
||||||
<input id="styleOrigin" type="text">
|
|
||||||
</label> -->
|
|
||||||
<label class="form-group style-update-url">
|
|
||||||
<span class="form-label" i18n-text="styleUpdateUrlLabel"></span>
|
|
||||||
<input type="text">
|
|
||||||
</label>
|
|
||||||
<div class="form-group style-prefer-scheme radio-group">
|
|
||||||
<!-- FIXME: should we use a different message from install page? -->
|
|
||||||
<span class="form-label" i18n-text="installPreferSchemeLabel"></span>
|
|
||||||
<label class="radio-item">
|
|
||||||
<input type="radio" name="preferScheme" value="none">
|
|
||||||
<span class="radio-label" i18n-text="installPreferSchemeNone"></span>
|
|
||||||
</label>
|
|
||||||
<label class="radio-item">
|
|
||||||
<input type="radio" name="preferScheme" value="dark">
|
|
||||||
<span class="radio-label" i18n-text="installPreferSchemeDark"></span>
|
|
||||||
</label>
|
|
||||||
<label class="radio-item">
|
|
||||||
<input type="radio" name="preferScheme" value="light">
|
|
||||||
<span class="radio-label" i18n-text="installPreferSchemeLight"></span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<label class="form-group style-include">
|
|
||||||
<span class="form-label" i18n-text="styleIncludeLabel"></span>
|
|
||||||
<textarea spellcheck="false" placeholder="*://site1.com/* *://site2.com/*"></textarea>
|
|
||||||
</label>
|
|
||||||
<label class="form-group style-exclude">
|
|
||||||
<span class="form-label" i18n-text="styleExcludeLabel"></span>
|
|
||||||
<textarea spellcheck="false" placeholder="*://site1.com/* *://site2.com/*"></textarea>
|
|
||||||
</label>
|
|
||||||
<!-- <label class="style-always-important">
|
|
||||||
<input type="checkbox">
|
|
||||||
<span class="form-label" i18n-text="styleAlwaysImportantLabel"></span>
|
|
||||||
</label> -->
|
|
||||||
</fieldset>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="help-popup">
|
<div id="help-popup">
|
||||||
<div class="title"></div><svg id="sections-help" class="svg-icon dismiss"><use xlink:href="#svg-icon-close"/></svg>
|
<div class="title"></div><svg id="sections-help" class="svg-icon dismiss"><use xlink:href="#svg-icon-close"/></svg>
|
||||||
<div class="contents"></div>
|
<div class="contents"></div>
|
||||||
|
@ -528,6 +517,5 @@
|
||||||
</symbol>
|
</symbol>
|
||||||
|
|
||||||
</svg>
|
</svg>
|
||||||
<script src="edit/tab.js"></script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
21
edit/base.js
21
edit/base.js
|
@ -14,14 +14,13 @@
|
||||||
tryJSONparse
|
tryJSONparse
|
||||||
tryURL
|
tryURL
|
||||||
*/// toolbox.js
|
*/// toolbox.js
|
||||||
/* global EventEmitter */
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type Editor
|
* @type Editor
|
||||||
* @namespace Editor
|
* @namespace Editor
|
||||||
*/
|
*/
|
||||||
const editor = Object.assign(EventEmitter(), {
|
const editor = {
|
||||||
style: null,
|
style: null,
|
||||||
dirty: DirtyReporter(),
|
dirty: DirtyReporter(),
|
||||||
isUsercss: false,
|
isUsercss: false,
|
||||||
|
@ -36,7 +35,9 @@ const editor = Object.assign(EventEmitter(), {
|
||||||
previewDelay: 200, // Chrome devtools uses 200
|
previewDelay: 200, // Chrome devtools uses 200
|
||||||
scrollInfo: null,
|
scrollInfo: null,
|
||||||
|
|
||||||
onStyleUpdated() {
|
cancel: () => location.assign('/manage.html'),
|
||||||
|
|
||||||
|
updateClass() {
|
||||||
document.documentElement.classList.toggle('is-new-style', !editor.style.id);
|
document.documentElement.classList.toggle('is-new-style', !editor.style.id);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -48,7 +49,7 @@ const editor = Object.assign(EventEmitter(), {
|
||||||
customName || name || t('styleMissingName')
|
customName || name || t('styleMissingName')
|
||||||
} - Stylus`; // the suffix enables external utilities to process our windows e.g. pin on top
|
} - Stylus`; // the suffix enables external utilities to process our windows e.g. pin on top
|
||||||
},
|
},
|
||||||
});
|
};
|
||||||
|
|
||||||
//#region pre-init
|
//#region pre-init
|
||||||
|
|
||||||
|
@ -90,7 +91,7 @@ const baseInit = (() => {
|
||||||
// switching the mode here to show the correct page ASAP, usually before DOMContentLoaded
|
// switching the mode here to show the correct page ASAP, usually before DOMContentLoaded
|
||||||
editor.isUsercss = Boolean(style.usercssData || !style.id && prefs.get('newStyleAsUsercss'));
|
editor.isUsercss = Boolean(style.usercssData || !style.id && prefs.get('newStyleAsUsercss'));
|
||||||
editor.style = style;
|
editor.style = style;
|
||||||
editor.onStyleUpdated();
|
editor.updateClass();
|
||||||
editor.updateTitle(false);
|
editor.updateTitle(false);
|
||||||
document.documentElement.classList.toggle('usercss', editor.isUsercss);
|
document.documentElement.classList.toggle('usercss', editor.isUsercss);
|
||||||
sessionStore.justEditedStyleId = style.id || '';
|
sessionStore.justEditedStyleId = style.id || '';
|
||||||
|
@ -292,16 +293,10 @@ baseInit.ready.then(() => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getOwnTab().then(async tab => {
|
getOwnTab().then(tab => {
|
||||||
ownTabId = tab.id;
|
ownTabId = tab.id;
|
||||||
// use browser history back when 'back to manage' is clicked
|
|
||||||
if (sessionStore['manageStylesHistory' + ownTabId] === location.href) {
|
if (sessionStore['manageStylesHistory' + ownTabId] === location.href) {
|
||||||
await baseInit.domReady;
|
editor.cancel = () => history.back();
|
||||||
$('#cancel-button').onclick = event => {
|
|
||||||
event.stopPropagation();
|
|
||||||
event.preventDefault();
|
|
||||||
history.back();
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -65,7 +65,7 @@ function beautifyEditor(cm, options, ui) {
|
||||||
window.scrollTo(scrollX, scrollY);
|
window.scrollTo(scrollX, scrollY);
|
||||||
cm.beautifyChange[cm.changeGeneration()] = true;
|
cm.beautifyChange[cm.changeGeneration()] = true;
|
||||||
if (ui) {
|
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'),
|
$create('span', t('styleBeautifyHint') + '\u00A0'),
|
||||||
createHotkeyInput('editor.beautify.hotkey', {
|
createHotkeyInput('editor.beautify.hotkey', {
|
||||||
buttons: false,
|
buttons: false,
|
||||||
onDone: () => moveFocus($('#help-popup'), 0),
|
onDone: () => moveFocus(helpPopup.div, 0),
|
||||||
}),
|
}),
|
||||||
]),
|
]),
|
||||||
$create('.buttons', [
|
$create('.buttons', [
|
||||||
|
@ -113,9 +113,10 @@ function createBeautifyUI(scope, options) {
|
||||||
},
|
},
|
||||||
}, t(scope.length === 1 ? 'undo' : 'undoGlobal')),
|
}, t(scope.length === 1 ? 'undo' : 'undoGlobal')),
|
||||||
]),
|
]),
|
||||||
]));
|
]),
|
||||||
|
{
|
||||||
$('#help-popup').className = 'wide';
|
className: 'wide',
|
||||||
|
});
|
||||||
|
|
||||||
$('.beautify-options').onchange = ({target}) => {
|
$('.beautify-options').onchange = ({target}) => {
|
||||||
const value = target.type === 'checkbox' ? target.checked : target.selectedIndex > 0;
|
const value = target.type === 'checkbox' ? target.checked : target.selectedIndex > 0;
|
||||||
|
|
|
@ -34,6 +34,7 @@ a:hover {
|
||||||
}
|
}
|
||||||
|
|
||||||
html.is-new-style #preview-label,
|
html.is-new-style #preview-label,
|
||||||
|
html.is-new-style #style-settings-btn,
|
||||||
html.is-new-style #publish,
|
html.is-new-style #publish,
|
||||||
.hidden {
|
.hidden {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
|
@ -105,11 +106,8 @@ label {
|
||||||
#header h1 {
|
#header h1 {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
.main {
|
|
||||||
padding-left: 280px;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
#sections {
|
#sections {
|
||||||
|
padding-left: 280px;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
@ -284,10 +282,6 @@ input:invalid {
|
||||||
margin: 0 .2rem .5rem 0;
|
margin: 0 .2rem .5rem 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#actions #cancel-button {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#options:not([open]) + #lint h2 {
|
#options:not([open]) + #lint h2 {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
|
@ -417,10 +411,6 @@ input:invalid {
|
||||||
.edit-actions button {
|
.edit-actions button {
|
||||||
margin-right: .2rem;
|
margin-right: .2rem;
|
||||||
}
|
}
|
||||||
.dirty > label::before {
|
|
||||||
content: "*";
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
#sections {
|
#sections {
|
||||||
counter-reset: codebox;
|
counter-reset: codebox;
|
||||||
}
|
}
|
||||||
|
@ -736,6 +726,9 @@ body:not(.find-open) [data-match-highlight-count="1"] .CodeMirror-selection-high
|
||||||
}
|
}
|
||||||
/************ help popup ************/
|
/************ help popup ************/
|
||||||
#help-popup {
|
#help-popup {
|
||||||
|
--pad-x: 1.5rem;
|
||||||
|
--pad-y: 1rem;
|
||||||
|
--pad-y2: calc(var(--pad-y) / 1.5);
|
||||||
top: 3rem;
|
top: 3rem;
|
||||||
right: 3rem;
|
right: 3rem;
|
||||||
max-width: 50vw;
|
max-width: 50vw;
|
||||||
|
@ -743,7 +736,7 @@ body:not(.find-open) [data-match-highlight-count="1"] .CodeMirror-selection-high
|
||||||
display: none;
|
display: none;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
box-shadow: 3px 3px 30px rgba(0, 0, 0, 0.5);
|
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;
|
z-index: 99;
|
||||||
}
|
}
|
||||||
#help-popup.big,
|
#help-popup.big,
|
||||||
|
@ -761,22 +754,19 @@ body:not(.find-open) [data-match-highlight-count="1"] .CodeMirror-selection-high
|
||||||
#help-popup .title {
|
#help-popup .title {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
background-color: rgba(0,0,0,0.05);
|
background-color: rgba(0,0,0,0.05);
|
||||||
margin: -0.5rem -0.5rem 0.5rem;
|
margin: calc(-1 * var(--pad-y)) calc(-1 * var(--pad-x)) 0;
|
||||||
padding: .5rem 32px .5rem .5rem;
|
padding: var(--pad-y2) var(--pad-x);
|
||||||
}
|
}
|
||||||
#help-popup .contents {
|
#help-popup .contents {
|
||||||
max-height: calc(100vh - 8rem);
|
max-height: calc(100vh - 8rem);
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
padding: var(--pad-y) 0;
|
||||||
#help-popup .settings {
|
|
||||||
min-width: 500px;
|
|
||||||
min-height: 200px;
|
|
||||||
max-width: 48vw;
|
|
||||||
}
|
}
|
||||||
#help-popup .dismiss {
|
#help-popup .dismiss {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 4px;
|
right: 0;
|
||||||
top: .5em;
|
top: 0;
|
||||||
|
padding: var(--pad-y2) .5em;
|
||||||
}
|
}
|
||||||
#help-popup input[type="search"],
|
#help-popup input[type="search"],
|
||||||
#help-popup .CodeMirror {
|
#help-popup .CodeMirror {
|
||||||
|
@ -804,12 +794,14 @@ body:not(.find-open) [data-match-highlight-count="1"] .CodeMirror-selection-high
|
||||||
}
|
}
|
||||||
|
|
||||||
#help-popup .buttons {
|
#help-popup .buttons {
|
||||||
text-align: center;
|
display: flex;
|
||||||
margin-top: .75em;
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
margin: var(--pad-y2) 0 calc(var(--pad-y2) - var(--pad-y)) 0;
|
||||||
}
|
}
|
||||||
.non-windows #help-popup .buttons {
|
.non-windows #help-popup .buttons {
|
||||||
direction: rtl;
|
direction: rtl;
|
||||||
text-align: right;
|
justify-content: start;
|
||||||
}
|
}
|
||||||
#help-popup button[name^="import"] {
|
#help-popup button[name^="import"] {
|
||||||
line-height: 1.5rem;
|
line-height: 1.5rem;
|
||||||
|
@ -831,8 +823,8 @@ body:not(.find-open) [data-match-highlight-count="1"] .CodeMirror-selection-high
|
||||||
#help-popup .rules p {
|
#help-popup .rules p {
|
||||||
margin: .25em 0;
|
margin: .25em 0;
|
||||||
}
|
}
|
||||||
#help-popup .buttons button:nth-child(n + 2) {
|
#help-popup .buttons > :nth-child(n + 2) {
|
||||||
margin-left: .5em;
|
margin-inline: .5em 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/************ lint ************/
|
/************ lint ************/
|
||||||
|
@ -1180,16 +1172,13 @@ body.linter-disabled .hidden-unless-compact {
|
||||||
#lint:not([open]) + #footer {
|
#lint:not([open]) + #footer {
|
||||||
margin: .25em 0 -1em .25em;
|
margin: .25em 0 -1em .25em;
|
||||||
}
|
}
|
||||||
.main {
|
#sections {
|
||||||
height: unset !important;
|
height: unset !important;
|
||||||
padding-left: 0;
|
padding-left: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
.tab-bar {
|
|
||||||
margin-top: var(--fixed-height);
|
|
||||||
}
|
|
||||||
#sections > :not(.single-editor) {
|
#sections > :not(.single-editor) {
|
||||||
margin: 0 .5rem;
|
margin: 0 .5rem;
|
||||||
padding: .5rem 0;
|
padding: .5rem 0;
|
||||||
|
|
46
edit/edit.js
46
edit/edit.js
|
@ -1,5 +1,5 @@
|
||||||
/* global $ $create messageBoxProxy waitForSheet */// dom.js
|
/* global $ $create messageBoxProxy waitForSheet */// dom.js
|
||||||
/* global msg API */// msg.js
|
/* global API msg */// msg.js
|
||||||
/* global CodeMirror */
|
/* global CodeMirror */
|
||||||
/* global SectionsEditor */
|
/* global SectionsEditor */
|
||||||
/* global SourceEditor */
|
/* global SourceEditor */
|
||||||
|
@ -11,7 +11,6 @@
|
||||||
/* global linterMan */
|
/* global linterMan */
|
||||||
/* global prefs */
|
/* global prefs */
|
||||||
/* global t */// localization.js
|
/* global t */// localization.js
|
||||||
/* global StyleSettings */// settings.js
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
//#region init
|
//#region init
|
||||||
|
@ -19,7 +18,6 @@
|
||||||
baseInit.ready.then(async () => {
|
baseInit.ready.then(async () => {
|
||||||
await waitForSheet();
|
await waitForSheet();
|
||||||
(editor.isUsercss ? SourceEditor : SectionsEditor)();
|
(editor.isUsercss ? SourceEditor : SectionsEditor)();
|
||||||
StyleSettings(editor);
|
|
||||||
await editor.ready;
|
await editor.ready;
|
||||||
editor.ready = true;
|
editor.ready = true;
|
||||||
editor.dirty.onChange(editor.updateDirty);
|
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
|
// enabling after init to prevent flash of validation failure on an empty name
|
||||||
$('#name').required = !editor.isUsercss;
|
$('#name').required = !editor.isUsercss;
|
||||||
$('#save-button').onclick = editor.save;
|
$('#save-button').onclick = editor.save;
|
||||||
|
$('#cancel-button').onclick = editor.cancel;
|
||||||
|
|
||||||
const elSec = $('#sections-list');
|
const elSec = $('#sections-list');
|
||||||
// editor.toc.expanded pref isn't saved in compact-layout so prefs.subscribe won't work
|
// 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());
|
require(['/edit/linter-dialogs'], () => linterMan.showLintConfig());
|
||||||
$('#lint-help').onclick = () =>
|
$('#lint-help').onclick = () =>
|
||||||
require(['/edit/linter-dialogs'], () => linterMan.showLintHelp());
|
require(['/edit/linter-dialogs'], () => linterMan.showLintHelp());
|
||||||
|
$('#style-settings-btn').onclick = () => require([
|
||||||
|
'/edit/settings.css',
|
||||||
|
'/edit/settings', /* global StyleSettings */
|
||||||
|
], () => StyleSettings());
|
||||||
|
|
||||||
require([
|
require([
|
||||||
'/edit/autocomplete',
|
'/edit/autocomplete',
|
||||||
'/edit/global-search',
|
'/edit/global-search',
|
||||||
|
@ -70,14 +74,7 @@ msg.onExtension(request => {
|
||||||
switch (request.method) {
|
switch (request.method) {
|
||||||
case 'styleUpdated':
|
case 'styleUpdated':
|
||||||
if (editor.style.id === style.id && !IGNORE_UPDATE_REASONS.includes(request.reason)) {
|
if (editor.style.id === style.id && !IGNORE_UPDATE_REASONS.includes(request.reason)) {
|
||||||
if (request.reason === 'toggle') {
|
handleExternalUpdate(request);
|
||||||
editor.emit('styleToggled', request.style);
|
|
||||||
} else {
|
|
||||||
API.styles.get(request.style.id)
|
|
||||||
.then(style => {
|
|
||||||
editor.emit('styleChange', style, request.reason);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'styleDeleted':
|
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 => {
|
window.on('beforeunload', e => {
|
||||||
let pos;
|
let pos;
|
||||||
if (editor.isWindowed &&
|
if (editor.isWindowed &&
|
||||||
|
@ -169,7 +191,7 @@ window.on('beforeunload', e => {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
toggleStyle(enabled = style.enabled) {
|
toggleStyle(enabled = !style.enabled) {
|
||||||
$('#enabled').checked = enabled;
|
$('#enabled').checked = enabled;
|
||||||
editor.updateEnabledness(enabled);
|
editor.updateEnabledness(enabled);
|
||||||
},
|
},
|
||||||
|
|
|
@ -23,7 +23,7 @@ function SectionsEditor() {
|
||||||
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
|
||||||
let cmExtrasHeight; // resize grip + borders
|
let cmExtrasHeight; // resize grip + borders
|
||||||
|
|
||||||
updateHeader();
|
updateMeta();
|
||||||
rerouteHotkeys.toggle(true); // enabled initially because we don't always focus a CodeMirror
|
rerouteHotkeys.toggle(true); // enabled initially because we don't always focus a CodeMirror
|
||||||
editor.livePreview.init();
|
editor.livePreview.init();
|
||||||
container.classList.add('section-editor');
|
container.classList.add('section-editor');
|
||||||
|
@ -44,6 +44,7 @@ function SectionsEditor() {
|
||||||
|
|
||||||
closestVisible,
|
closestVisible,
|
||||||
updateLivePreview,
|
updateLivePreview,
|
||||||
|
updateMeta,
|
||||||
|
|
||||||
getEditors() {
|
getEditors() {
|
||||||
return sections.filter(s => !s.removed).map(s => s.cm);
|
return sections.filter(s => !s.removed).map(s => s.cm);
|
||||||
|
@ -89,8 +90,8 @@ function SectionsEditor() {
|
||||||
// FIXME: avoid recreating all editors?
|
// FIXME: avoid recreating all editors?
|
||||||
await initSections(newStyle.sections, {replace: true});
|
await initSections(newStyle.sections, {replace: true});
|
||||||
Object.assign(style, newStyle);
|
Object.assign(style, newStyle);
|
||||||
editor.onStyleUpdated();
|
editor.updateClass();
|
||||||
updateHeader();
|
updateMeta();
|
||||||
// Go from new style URL to edit style URL
|
// Go from new style URL to edit style URL
|
||||||
if (style.id && !/[&?]id=/.test(location.search)) {
|
if (style.id && !/[&?]id=/.test(location.search)) {
|
||||||
history.replaceState({}, document.title, `${location.pathname}?id=${style.id}`);
|
history.replaceState({}, document.title, `${location.pathname}?id=${style.id}`);
|
||||||
|
@ -108,9 +109,6 @@ function SectionsEditor() {
|
||||||
}
|
}
|
||||||
newStyle = await API.styles.editSave(newStyle);
|
newStyle = await API.styles.editSave(newStyle);
|
||||||
destroyRemovedSections();
|
destroyRemovedSections();
|
||||||
if (!style.id) {
|
|
||||||
editor.emit('styleChange', newStyle, 'new');
|
|
||||||
}
|
|
||||||
sessionStore.justEditedStyleId = newStyle.id;
|
sessionStore.justEditedStyleId = newStyle.id;
|
||||||
editor.replaceStyle(newStyle, false);
|
editor.replaceStyle(newStyle, false);
|
||||||
},
|
},
|
||||||
|
@ -128,28 +126,6 @@ function SectionsEditor() {
|
||||||
|
|
||||||
editor.ready = initSections(style.sections);
|
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 */
|
/** @param {EditorSection} section */
|
||||||
function fitToContent(section) {
|
function fitToContent(section) {
|
||||||
const {cm, cm: {display: {wrapper, sizer}}} = section;
|
const {cm, cm: {display: {wrapper, sizer}}} = section;
|
||||||
|
@ -489,7 +465,7 @@ function SectionsEditor() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateHeader() {
|
function updateMeta() {
|
||||||
$('#name').value = style.customName || style.name || '';
|
$('#name').value = style.customName || style.name || '';
|
||||||
$('#enabled').checked = style.enabled !== false;
|
$('#enabled').checked = style.enabled !== false;
|
||||||
$('#url').href = style.url || '';
|
$('#url').href = style.url || '';
|
||||||
|
|
|
@ -1,46 +1,43 @@
|
||||||
|
#help-popup.style-settings-popup.dirty .title::after {
|
||||||
|
content: ' *';
|
||||||
|
}
|
||||||
|
.compact-layout #help-popup.style-settings-popup {
|
||||||
|
width: 90%;
|
||||||
|
}
|
||||||
.style-settings {
|
.style-settings {
|
||||||
padding: 0.7rem 1.7rem;
|
padding: 0;
|
||||||
border: 0;
|
border: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
.form-group {
|
.style-settings > * {
|
||||||
display: block;
|
display: block;
|
||||||
margin: .6em 0;
|
margin: 1rem 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
.form-label {
|
.style-settings > :first-child {
|
||||||
display: inline-block;
|
margin-top: 0;
|
||||||
margin: .3em 0;
|
|
||||||
}
|
}
|
||||||
[disabled] .form-label {
|
.style-settings > :last-child {
|
||||||
opacity: 0.4;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
.form-group input[type=text],
|
.style-settings input[type=radio] {
|
||||||
.form-group input[type=number],
|
margin-left: -.5em; /* compensate for label's 16px margin in edit.css */
|
||||||
.form-group select,
|
}
|
||||||
.form-group textarea {
|
.style-settings input[type=text],
|
||||||
|
.style-settings input[type=number],
|
||||||
|
.style-settings select,
|
||||||
|
.style-settings textarea {
|
||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
margin-top: .25em;
|
||||||
box-sizing: border-box;
|
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 {
|
.style-settings textarea {
|
||||||
resize: vertical;
|
resize: vertical;
|
||||||
|
min-width: 33vw;
|
||||||
min-height: 2.5em;
|
min-height: 2.5em;
|
||||||
max-height: 50vh;
|
max-height: 50vh;
|
||||||
}
|
}
|
||||||
|
.style-settings textarea:not(:placeholder-shown) {
|
||||||
|
min-width: 50vw;
|
||||||
|
}
|
||||||
|
|
131
edit/settings.js
131
edit/settings.js
|
@ -1,84 +1,97 @@
|
||||||
/* global $ $$ */// dom.js
|
/* global $ $$ moveFocus setupLivePrefs */// dom.js
|
||||||
/* global API */// msg.js
|
/* global API */// msg.js
|
||||||
|
/* global editor */
|
||||||
|
/* global helpPopup */// util.js
|
||||||
|
/* global t */// localization.js
|
||||||
|
/* global debounce */// toolbox.js
|
||||||
/* exported StyleSettings */
|
/* exported StyleSettings */
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
function StyleSettings(editor) {
|
function StyleSettings() {
|
||||||
let {style} = editor;
|
const AUTOSAVE_DELAY = 500; // same as config-dialog.js
|
||||||
|
const SS_ID = 'styleSettings';
|
||||||
const inputs = [
|
const {style} = editor;
|
||||||
createInput('.style-update-url input', () => style.updateUrl || '',
|
const ui = t.template[SS_ID].cloneNode(true);
|
||||||
e => API.styles.config(style.id, 'updateUrl', e.target.value)),
|
const elAuto = $('[id="config.autosave"]', ui);
|
||||||
createRadio('.style-prefer-scheme input', () => style.preferScheme || 'none',
|
const elSave = $('#ss-save', ui);
|
||||||
e => API.styles.config(style.id, 'preferScheme', e.target.value)),
|
const pendingSetters = new Map();
|
||||||
...[
|
const updaters = [
|
||||||
['.style-include', 'inclusions'],
|
initInput('#ss-update-url', () => style.updateUrl || '',
|
||||||
['.style-exclude', 'exclusions'],
|
val => API.styles.config(style.id, 'updateUrl', val)),
|
||||||
].map(createArea),
|
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);
|
function autosave(el, setter) {
|
||||||
|
pendingSetters.set(el, setter);
|
||||||
editor.on('styleChange', update);
|
helpPopup.div.classList.add('dirty');
|
||||||
|
elSave.disabled = false;
|
||||||
function textToList(text) {
|
if (elAuto.checked) debounce(save, AUTOSAVE_DELAY);
|
||||||
const list = text.split(/\s*\r?\n\s*/g);
|
|
||||||
return list.filter(Boolean);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function update(newStyle, reason) {
|
function initArea(type) {
|
||||||
if (!newStyle.id) return;
|
const selector = `#ss-${type}`;
|
||||||
if (reason === 'editSave') return;
|
const el = $(selector, ui);
|
||||||
style = newStyle;
|
el.oninput = () => {
|
||||||
$('.style-settings').disabled = false;
|
|
||||||
inputs.forEach(i => i.update());
|
|
||||||
}
|
|
||||||
|
|
||||||
function createArea([parentSel, type]) {
|
|
||||||
const sel = parentSel + ' textarea';
|
|
||||||
const el = $(sel);
|
|
||||||
el.on('input', () => {
|
|
||||||
const val = el.value;
|
const val = el.value;
|
||||||
el.rows = val.match(/^/gm).length + !val.endsWith('\n');
|
el.rows = val.match(/^/gm).length + !val.endsWith('\n');
|
||||||
});
|
};
|
||||||
return createInput(sel,
|
return initInput(selector,
|
||||||
() => {
|
() => {
|
||||||
const list = style[type] || [];
|
const list = style[type] || [];
|
||||||
const text = list.join('\n');
|
const text = list.join('\n');
|
||||||
el.rows = (list.length || 1) + 1;
|
el.rows = (list.length || 1) + 1;
|
||||||
return text;
|
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) {
|
function initInput(selector, getter, setter) {
|
||||||
const els = $$(selector);
|
const el = $(selector, ui);
|
||||||
for (const el of els) {
|
el.oninput = () => autosave(el, setter);
|
||||||
el.addEventListener('change', e => {
|
return () => {
|
||||||
if (el.checked) {
|
const val = getter();
|
||||||
setter(e);
|
// Skipping if unchanged to preserve the Undo history of the input
|
||||||
}
|
if (el.value !== val) el.value = val;
|
||||||
});
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
update() {
|
|
||||||
for (const el of els) {
|
|
||||||
if (el.value === getter()) {
|
|
||||||
el.checked = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function createInput(selector, getter, setter) {
|
function initRadio(name, getter, setter) {
|
||||||
const el = $(selector);
|
for (const el of $$(`[name="${name}"]`, ui)) {
|
||||||
el.addEventListener('change', setter);
|
el.onchange = () => {
|
||||||
return {
|
if (el.checked) autosave(el, setter);
|
||||||
update() {
|
};
|
||||||
el.value = getter();
|
}
|
||||||
},
|
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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,6 +45,7 @@ function SourceEditor() {
|
||||||
sections: sectionFinder.sections,
|
sections: sectionFinder.sections,
|
||||||
replaceStyle,
|
replaceStyle,
|
||||||
updateLivePreview,
|
updateLivePreview,
|
||||||
|
updateMeta,
|
||||||
closestVisible: () => cm,
|
closestVisible: () => cm,
|
||||||
getEditors: () => [cm],
|
getEditors: () => [cm],
|
||||||
getEditorTitle: () => '',
|
getEditorTitle: () => '',
|
||||||
|
@ -70,9 +71,6 @@ function SourceEditor() {
|
||||||
messageBoxProxy.alert(t('usercssAvoidOverwriting'), 'danger', t('genericError'));
|
messageBoxProxy.alert(t('usercssAvoidOverwriting'), 'danger', t('genericError'));
|
||||||
} else {
|
} else {
|
||||||
res = await API.usercss.editSave({customName, enabled, id, sourceCode});
|
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`
|
// Awaiting inside `try` so that exceptions go to our `catch`
|
||||||
await replaceStyle(res.style);
|
await replaceStyle(res.style);
|
||||||
}
|
}
|
||||||
|
@ -116,26 +114,6 @@ function SourceEditor() {
|
||||||
if (!$isTextInput(document.activeElement)) {
|
if (!$isTextInput(document.activeElement)) {
|
||||||
cm.focus();
|
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) {
|
async function preprocess(style) {
|
||||||
const res = await API.usercss.build({
|
const res = await API.usercss.build({
|
||||||
|
@ -231,7 +209,7 @@ function SourceEditor() {
|
||||||
cm.setPreprocessor((style.usercssData || {}).preprocessor);
|
cm.setPreprocessor((style.usercssData || {}).preprocessor);
|
||||||
}
|
}
|
||||||
|
|
||||||
function replaceStyle(newStyle) {
|
async function replaceStyle(newStyle) {
|
||||||
dirty.clear('name');
|
dirty.clear('name');
|
||||||
const sameCode = newStyle.sourceCode === cm.getValue();
|
const sameCode = newStyle.sourceCode === cm.getValue();
|
||||||
if (sameCode) {
|
if (sameCode) {
|
||||||
|
@ -243,8 +221,8 @@ function SourceEditor() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Promise.resolve(messageBoxProxy.confirm(t('styleUpdateDiscardChanges'))).then(ok => {
|
// TODO: also confirm in sections-editor?
|
||||||
if (!ok) return;
|
if (await messageBoxProxy.confirm(t('styleUpdateDiscardChanges'))) {
|
||||||
updateEnvironment();
|
updateEnvironment();
|
||||||
if (!sameCode) {
|
if (!sameCode) {
|
||||||
const cursor = cm.getCursor();
|
const cursor = cm.getCursor();
|
||||||
|
@ -257,7 +235,7 @@ function SourceEditor() {
|
||||||
updateLivePreview();
|
updateLivePreview();
|
||||||
}
|
}
|
||||||
dirty.clear();
|
dirty.clear();
|
||||||
});
|
}
|
||||||
|
|
||||||
function updateEnvironment() {
|
function updateEnvironment() {
|
||||||
if (style.id !== newStyle.id) {
|
if (style.id !== newStyle.id) {
|
||||||
|
@ -265,7 +243,7 @@ function SourceEditor() {
|
||||||
}
|
}
|
||||||
sessionStore.justEditedStyleId = newStyle.id;
|
sessionStore.justEditedStyleId = newStyle.id;
|
||||||
Object.assign(style, newStyle);
|
Object.assign(style, newStyle);
|
||||||
editor.onStyleUpdated();
|
editor.updateClass();
|
||||||
updateMeta();
|
updateMeta();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
25
edit/util.js
25
edit/util.js
|
@ -7,20 +7,28 @@
|
||||||
|
|
||||||
const helpPopup = {
|
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 div = $('#help-popup');
|
||||||
const contents = $('.contents', div);
|
const contents = $('.contents', div);
|
||||||
|
div.style = '';
|
||||||
div.className = '';
|
div.className = '';
|
||||||
contents.textContent = '';
|
contents.textContent = '';
|
||||||
|
Object.assign(div, props);
|
||||||
if (body) {
|
if (body) {
|
||||||
contents.appendChild(typeof body === 'string' ? t.HTML(body) : body);
|
contents.appendChild(typeof body === 'string' ? t.HTML(body) : body);
|
||||||
}
|
}
|
||||||
$('.title', div).textContent = title;
|
$('.title', div).textContent = title;
|
||||||
$('.dismiss', div).onclick = helpPopup.close;
|
$('.dismiss', div).onclick = helpPopup.close;
|
||||||
window.on('keydown', helpPopup.close, true);
|
window.on('keydown', helpPopup.close, true);
|
||||||
// reset any inline styles
|
div.style.display = 'block';
|
||||||
div.style = 'display: block';
|
|
||||||
helpPopup.originalFocus = document.activeElement;
|
helpPopup.originalFocus = document.activeElement;
|
||||||
|
helpPopup.div = div;
|
||||||
return div;
|
return div;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -31,11 +39,13 @@ const helpPopup = {
|
||||||
getEventKeyName(event) === 'Escape' &&
|
getEventKeyName(event) === 'Escape' &&
|
||||||
!$('.CodeMirror-hints, #message-box') && (
|
!$('.CodeMirror-hints, #message-box') && (
|
||||||
!document.activeElement ||
|
!document.activeElement ||
|
||||||
!document.activeElement.closest('#search-replace-dialog') &&
|
!document.activeElement.closest('#search-replace-dialog') && (
|
||||||
document.activeElement.matches(':not(input), .can-close-on-esc')
|
document.activeElement.tagName !== 'INPUT' ||
|
||||||
|
document.activeElement.closest('.can-close-on-esc')
|
||||||
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
const div = $('#help-popup');
|
const {div} = helpPopup;
|
||||||
if (!canClose || !div) {
|
if (!canClose || !div) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -170,8 +180,7 @@ function createHotkeyInput(prefId, {buttons = true, onDone}) {
|
||||||
|
|
||||||
/* exported showCodeMirrorPopup */
|
/* exported showCodeMirrorPopup */
|
||||||
function showCodeMirrorPopup(title, html, options) {
|
function showCodeMirrorPopup(title, html, options) {
|
||||||
const popup = helpPopup.show(title, html);
|
const popup = helpPopup.show(title, html, {className: 'big'});
|
||||||
popup.classList.add('big');
|
|
||||||
|
|
||||||
let cm = popup.codebox = CodeMirror($('.contents', popup), Object.assign({
|
let cm = popup.codebox = CodeMirror($('.contents', popup), Object.assign({
|
||||||
mode: 'css',
|
mode: 'css',
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user