usercss editor: save as template when @name is empty
* reduced the flickering on page open * show * in title for new styles * align the values in the default template * don't ask to save an untouched template * don't spam the console with errors * trivial code refactor and cosmetics
This commit is contained in:
parent
b63449f299
commit
a58f42dee0
|
@ -889,6 +889,10 @@
|
||||||
"message": "As a security precaution, the browser prohibits extensions from affecting its built-in pages (like chrome://version, the standard new tab page as of Chrome 61, about:addons, and so on) as well as other extensions' pages. Each browser also restricts access to its own extensions gallery (like Chrome Web Store or AMO).",
|
"message": "As a security precaution, the browser prohibits extensions from affecting its built-in pages (like chrome://version, the standard new tab page as of Chrome 61, about:addons, and so on) as well as other extensions' pages. Each browser also restricts access to its own extensions gallery (like Chrome Web Store or AMO).",
|
||||||
"description": "Sub-note in the toolbar pop-up when on a URL Stylus can't affect"
|
"description": "Sub-note in the toolbar pop-up when on a URL Stylus can't affect"
|
||||||
},
|
},
|
||||||
|
"syncStorageErrorSaving": {
|
||||||
|
"message": "The value cannot be saved. Try reducing the amount of text.",
|
||||||
|
"description": "Displayed when trying to save an excessively big value via storage.sync API"
|
||||||
|
},
|
||||||
"toggleStyle": {
|
"toggleStyle": {
|
||||||
"message": "Toggle style",
|
"message": "Toggle style",
|
||||||
"description": "Label for the checkbox to enable/disable a style"
|
"description": "Label for the checkbox to enable/disable a style"
|
||||||
|
@ -958,6 +962,17 @@
|
||||||
"message": "Updates installed:",
|
"message": "Updates installed:",
|
||||||
"description": "Text that displays when an update is installed on options page. Followed by the number of currently installed updates."
|
"description": "Text that displays when an update is installed on options page. Followed by the number of currently installed updates."
|
||||||
},
|
},
|
||||||
|
"usercssEditorNamePlaceholder": {
|
||||||
|
"message": "Specify @name in the code",
|
||||||
|
"description": "Placeholder text for the empty name input field when creating a new Usercss style"
|
||||||
|
},
|
||||||
|
"usercssReplaceTemplateName": {
|
||||||
|
"message": "Empty @name replaces the default template",
|
||||||
|
"description": "The text shown after @name when creating a new Usercss style"
|
||||||
|
},
|
||||||
|
"usercssReplaceTemplateConfirmation": {
|
||||||
|
"message": "Replace the default template for new Usercss styles with the current code?"
|
||||||
|
},
|
||||||
"versionInvalidOlder": {
|
"versionInvalidOlder": {
|
||||||
"message": "The version is older than the installed style.",
|
"message": "The version is older than the installed style.",
|
||||||
"description": "Displayed when the version of style is older than the installed one"
|
"description": "Displayed when the version of style is older than the installed one"
|
||||||
|
|
15
edit.html
15
edit.html
|
@ -143,7 +143,7 @@
|
||||||
<h1 id="heading"> </h1> <!-- nbsp allocates the actual height which prevents page shift -->
|
<h1 id="heading"> </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" i18n-placeholder="styleMissingName" spellcheck="false">
|
<input id="name" class="style-contributor" spellcheck="false">
|
||||||
<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">
|
||||||
|
@ -160,7 +160,7 @@
|
||||||
<button id="beautify" i18n-text="styleBeautify"></button>
|
<button id="beautify" i18n-text="styleBeautify"></button>
|
||||||
<a href="manage.html"><button id="cancel-button" i18n-text="styleCancelEditLabel"></button></a>
|
<a href="manage.html"><button id="cancel-button" i18n-text="styleCancelEditLabel"></button></a>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div id="mozilla-format-container">
|
||||||
<h2 id="mozilla-format-heading" i18n-text="styleMozillaFormatHeading"><svg id="to-mozilla-help" class="svg-icon info"><use xlink:href="#svg-icon-help"/></svg></h2>
|
<h2 id="mozilla-format-heading" i18n-text="styleMozillaFormatHeading"><svg id="to-mozilla-help" class="svg-icon info"><use xlink:href="#svg-icon-help"/></svg></h2>
|
||||||
<button id="from-mozilla" i18n-text="importLabel"></button>
|
<button id="from-mozilla" i18n-text="importLabel"></button>
|
||||||
<button id="to-mozilla" i18n-text="exportLabel"></button>
|
<button id="to-mozilla" i18n-text="exportLabel"></button>
|
||||||
|
@ -199,6 +199,12 @@
|
||||||
</svg>
|
</svg>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="option usercss-only">
|
||||||
|
<input id="editor.appliesToLineWidget" type="checkbox">
|
||||||
|
<label for="editor.appliesToLineWidget"
|
||||||
|
i18n-text="appliesLineWidgetLabel"
|
||||||
|
i18n-title="appliesLineWidgetWarning"></label>
|
||||||
|
</div>
|
||||||
<div class="option aligned">
|
<div class="option aligned">
|
||||||
<label id="tabSize-label" for="editor.tabSize" i18n-text="cm_tabSize"></label>
|
<label id="tabSize-label" for="editor.tabSize" i18n-text="cm_tabSize"></label>
|
||||||
<input id="editor.tabSize" type="number" min="0">
|
<input id="editor.tabSize" type="number" min="0">
|
||||||
|
@ -246,6 +252,11 @@
|
||||||
</summary>
|
</summary>
|
||||||
<div></div>
|
<div></div>
|
||||||
</details>
|
</details>
|
||||||
|
<div id="footer">
|
||||||
|
<a href="https://github.com/openstyles/stylus/wiki/Usercss"
|
||||||
|
i18n-text="externalUsercssDocument"
|
||||||
|
target="_blank"></a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<section id="sections">
|
<section id="sections">
|
||||||
<h2><span id="sections-heading" i18n-text="styleSectionsTitle"></span><svg id="sections-help" class="svg-icon info"><use xlink:href="#svg-icon-help"/></svg></h2>
|
<h2><span id="sections-heading" i18n-text="styleSectionsTitle"></span><svg id="sections-help" class="svg-icon info"><use xlink:href="#svg-icon-help"/></svg></h2>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
/* global regExpTester debounce messageBox */
|
/* global regExpTester debounce messageBox CodeMirror */
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
function createAppliesToLineWidget(cm) {
|
function createAppliesToLineWidget(cm) {
|
||||||
|
@ -56,13 +56,19 @@ function createAppliesToLineWidget(cm) {
|
||||||
styleVariables.remove();
|
styleVariables.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
function onChange(cm, {from, to, origin}) {
|
function onChange(cm, event) {
|
||||||
|
const {from, to, origin} = event;
|
||||||
if (origin === 'appliesTo') {
|
if (origin === 'appliesTo') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const lastChanged = CodeMirror.changeEnd(event).line;
|
||||||
fromLine = Math.min(fromLine === null ? from.line : fromLine, from.line);
|
fromLine = Math.min(fromLine === null ? from.line : fromLine, from.line);
|
||||||
toLine = Math.max(toLine === null ? to.line : toLine, to.line);
|
toLine = Math.max(toLine === null ? lastChanged : toLine, to.line);
|
||||||
debounce(update, THROTTLE_DELAY);
|
if (origin === 'setValue') {
|
||||||
|
update();
|
||||||
|
} else {
|
||||||
|
debounce(update, THROTTLE_DELAY);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onOptionChange(cm, option) {
|
function onOptionChange(cm, option) {
|
||||||
|
@ -82,9 +88,9 @@ function createAppliesToLineWidget(cm) {
|
||||||
function update() {
|
function update() {
|
||||||
const changed = {fromLine, toLine};
|
const changed = {fromLine, toLine};
|
||||||
fromLine = Math.max(fromLine || 0, cm.display.viewFrom);
|
fromLine = Math.max(fromLine || 0, cm.display.viewFrom);
|
||||||
toLine = Math.min(toLine === null ? cm.doc.size : toLine, cm.display.viewTo);
|
toLine = Math.min(toLine === null ? cm.doc.size : toLine, cm.display.viewTo || toLine);
|
||||||
const visible = {fromLine, toLine};
|
const visible = {fromLine, toLine};
|
||||||
if (fromLine >= cm.display.viewFrom && toLine <= cm.display.viewTo) {
|
if (fromLine >= cm.display.viewFrom && toLine <= (cm.display.viewTo || toLine)) {
|
||||||
cm.operation(doUpdate);
|
cm.operation(doUpdate);
|
||||||
}
|
}
|
||||||
if (changed.fromLine !== visible.fromLine || changed.toLine !== visible.toLine) {
|
if (changed.fromLine !== visible.fromLine || changed.toLine !== visible.toLine) {
|
||||||
|
|
|
@ -547,6 +547,12 @@ body[data-match-highlight="selection"] .CodeMirror-selection-highlight-scrollbar
|
||||||
justify-items: normal;
|
justify-items: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
html:not(.usercss) .usercss-only,
|
||||||
|
.usercss #mozilla-format-container,
|
||||||
|
.usercss #sections > h2 {
|
||||||
|
display: none !important; /* hide during page init */
|
||||||
|
}
|
||||||
|
|
||||||
#sections .single-editor {
|
#sections .single-editor {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
@ -565,7 +571,6 @@ body[data-match-highlight="selection"] .CodeMirror-selection-highlight-scrollbar
|
||||||
color: #333;
|
color: #333;
|
||||||
transition: color .5s;
|
transition: color .5s;
|
||||||
text-decoration-skip: ink;
|
text-decoration-skip: ink;
|
||||||
animation: fadein 10s;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#footer a:hover {
|
#footer a:hover {
|
||||||
|
|
124
edit/edit.js
124
edit/edit.js
|
@ -8,14 +8,6 @@
|
||||||
/* global initColorpicker */
|
/* global initColorpicker */
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
onDOMready()
|
|
||||||
.then(() => Promise.all([
|
|
||||||
initColorpicker(),
|
|
||||||
initCollapsibles(),
|
|
||||||
initHooksCommon(),
|
|
||||||
]))
|
|
||||||
.then(init);
|
|
||||||
|
|
||||||
let styleId = null;
|
let styleId = null;
|
||||||
// only the actually dirty items here
|
// only the actually dirty items here
|
||||||
let dirty = {};
|
let dirty = {};
|
||||||
|
@ -31,25 +23,50 @@ const CssToProperty = {'url': 'urls', 'url-prefix': 'urlPrefixes', 'domain': 'do
|
||||||
|
|
||||||
let editor;
|
let editor;
|
||||||
|
|
||||||
// if background page hasn't been loaded yet, increase the chances it has before DOMContentLoaded
|
Promise.all([
|
||||||
onBackgroundReady();
|
initStyleData().then(style => {
|
||||||
|
styleId = style.id;
|
||||||
|
sessionStorage.justEditedStyleId = styleId;
|
||||||
|
// we set "usercss" class on <html> when <body> 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;
|
||||||
|
}),
|
||||||
|
onDOMready(),
|
||||||
|
onBackgroundReady(),
|
||||||
|
])
|
||||||
|
.then(([style]) => Promise.all([
|
||||||
|
style,
|
||||||
|
initColorpicker(),
|
||||||
|
initCollapsibles(),
|
||||||
|
initHooksCommon(),
|
||||||
|
]))
|
||||||
|
.then(([style]) => {
|
||||||
|
initCodeMirror();
|
||||||
|
|
||||||
|
const usercss = isUsercss(style);
|
||||||
|
$('#heading').textContent = t(styleId ? 'editStyleHeading' : 'addStyleTitle');
|
||||||
|
$('#name').placeholder = t(usercss ? 'usercssEditorNamePlaceholder' : 'styleMissingName');
|
||||||
|
$('#name').title = usercss ? t('usercssReplaceTemplateName') : '';
|
||||||
|
|
||||||
|
if (usercss) {
|
||||||
|
editor = createSourceEditor(style);
|
||||||
|
} else {
|
||||||
|
initWithSectionStyle({style});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// make querySelectorAll enumeration code readable
|
// make querySelectorAll enumeration code readable
|
||||||
['forEach', 'some', 'indexOf', 'map'].forEach(method => {
|
['forEach', 'some', 'indexOf', 'map'].forEach(method => {
|
||||||
NodeList.prototype[method] = Array.prototype[method];
|
NodeList.prototype[method] = Array.prototype[method];
|
||||||
});
|
});
|
||||||
|
|
||||||
// Chrome pre-34
|
|
||||||
Element.prototype.matches = Element.prototype.matches || Element.prototype.webkitMatchesSelector;
|
|
||||||
|
|
||||||
// Chrome pre-41 polyfill
|
|
||||||
Element.prototype.closest = Element.prototype.closest || function (selector) {
|
|
||||||
let e;
|
|
||||||
// eslint-disable-next-line no-empty
|
|
||||||
for (e = this; e && !e.matches(selector); e = e.parentElement) {}
|
|
||||||
return e;
|
|
||||||
};
|
|
||||||
|
|
||||||
// eslint-disable-next-line no-extend-native
|
// eslint-disable-next-line no-extend-native
|
||||||
Array.prototype.rotate = function (amount) {
|
Array.prototype.rotate = function (amount) {
|
||||||
// negative amount == rotate left
|
// negative amount == rotate left
|
||||||
|
@ -1317,54 +1334,25 @@ function beautify(event) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function init() {
|
function initStyleData() {
|
||||||
initCodeMirror();
|
const params = new URLSearchParams(location.search);
|
||||||
getStyle().then(style => {
|
const id = params.get('id');
|
||||||
styleId = style.id;
|
const createEmptyStyle = () => ({
|
||||||
sessionStorage.justEditedStyleId = styleId;
|
id: null,
|
||||||
|
name: '',
|
||||||
if (!isUsercss(style)) {
|
enabled: true,
|
||||||
initWithSectionStyle({style});
|
sections: [
|
||||||
} else {
|
Object.assign({code: ''},
|
||||||
editor = createSourceEditor(style);
|
...Object.keys(CssToProperty)
|
||||||
}
|
.map(name => ({
|
||||||
|
[CssToProperty[name]]: params.get(name) && [params.get(name)] || []
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
],
|
||||||
});
|
});
|
||||||
|
return !id ?
|
||||||
function getStyle() {
|
Promise.resolve(createEmptyStyle()) :
|
||||||
const id = new URLSearchParams(location.search).get('id');
|
getStylesSafe({id}).then(([style]) => style || createEmptyStyle());
|
||||||
if (!id) {
|
|
||||||
// match should be 2 - one for the whole thing, one for the parentheses
|
|
||||||
// This is an add
|
|
||||||
$('#heading').textContent = t('addStyleTitle');
|
|
||||||
return Promise.resolve(createEmptyStyle());
|
|
||||||
}
|
|
||||||
$('#heading').textContent = t('editStyleHeading');
|
|
||||||
// This is an edit
|
|
||||||
return getStylesSafe({id}).then(styles => {
|
|
||||||
let style = styles[0];
|
|
||||||
if (!style) {
|
|
||||||
style = createEmptyStyle();
|
|
||||||
history.replaceState({}, document.title, location.pathname);
|
|
||||||
}
|
|
||||||
return style;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function createEmptyStyle() {
|
|
||||||
const params = new URLSearchParams(location.search);
|
|
||||||
const style = {
|
|
||||||
id: null,
|
|
||||||
name: '',
|
|
||||||
enabled: true,
|
|
||||||
sections: [{code: ''}]
|
|
||||||
};
|
|
||||||
for (const i in CssToProperty) {
|
|
||||||
if (params.get(i)) {
|
|
||||||
style.sections[0][CssToProperty[i]] = [params.get(i)];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return style;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function setStyleMeta(style) {
|
function setStyleMeta(style) {
|
||||||
|
|
|
@ -9,20 +9,13 @@ function createSourceEditor(style) {
|
||||||
// a flag for isTouched()
|
// a flag for isTouched()
|
||||||
let hadBeenSaved = false;
|
let hadBeenSaved = false;
|
||||||
|
|
||||||
document.documentElement.classList.add('usercss');
|
|
||||||
$('#sections').textContent = '';
|
|
||||||
$('#name').disabled = true;
|
$('#name').disabled = true;
|
||||||
$('#mozilla-format-heading').parentNode.remove();
|
$('#mozilla-format-container').remove();
|
||||||
|
$('#sections').textContent = '';
|
||||||
$('#sections').appendChild(
|
$('#sections').appendChild(
|
||||||
$element({className: 'single-editor'})
|
$element({className: 'single-editor'})
|
||||||
);
|
);
|
||||||
|
|
||||||
$('#header').appendChild($element({
|
|
||||||
id: 'footer',
|
|
||||||
appendChild: makeLink('https://github.com/openstyles/stylus/wiki/Usercss', t('externalUsercssDocument'))
|
|
||||||
}));
|
|
||||||
|
|
||||||
const dirty = dirtyReporter();
|
const dirty = dirtyReporter();
|
||||||
dirty.onChange(() => {
|
dirty.onChange(() => {
|
||||||
const DIRTY = dirty.isDirty();
|
const DIRTY = dirty.isDirty();
|
||||||
|
@ -59,34 +52,8 @@ function createSourceEditor(style) {
|
||||||
function initAppliesToLineWidget() {
|
function initAppliesToLineWidget() {
|
||||||
const PREF_NAME = 'editor.appliesToLineWidget';
|
const PREF_NAME = 'editor.appliesToLineWidget';
|
||||||
const widget = createAppliesToLineWidget(cm);
|
const widget = createAppliesToLineWidget(cm);
|
||||||
const optionEl = buildOption();
|
|
||||||
|
|
||||||
$('#options').insertBefore(optionEl, $('#options > .option.aligned'));
|
|
||||||
widget.toggle(prefs.get(PREF_NAME));
|
widget.toggle(prefs.get(PREF_NAME));
|
||||||
prefs.subscribe([PREF_NAME], (key, value) => {
|
prefs.subscribe([PREF_NAME], (key, value) => widget.toggle(value));
|
||||||
widget.toggle(value);
|
|
||||||
optionEl.checked = value;
|
|
||||||
});
|
|
||||||
optionEl.addEventListener('change', e => {
|
|
||||||
prefs.set(PREF_NAME, e.target.checked);
|
|
||||||
});
|
|
||||||
|
|
||||||
function buildOption() {
|
|
||||||
return $element({className: 'option', appendChild: [
|
|
||||||
$element({
|
|
||||||
tag: 'input',
|
|
||||||
type: 'checkbox',
|
|
||||||
id: PREF_NAME,
|
|
||||||
checked: prefs.get(PREF_NAME)
|
|
||||||
}),
|
|
||||||
$element({
|
|
||||||
tag: 'label',
|
|
||||||
htmlFor: PREF_NAME,
|
|
||||||
textContent: ' ' + t('appliesLineWidgetLabel'),
|
|
||||||
title: t('appliesLineWidgetWarning')
|
|
||||||
})
|
|
||||||
]});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function initLinterSwitch() {
|
function initLinterSwitch() {
|
||||||
|
@ -123,18 +90,27 @@ function createSourceEditor(style) {
|
||||||
section = mozParser.format(style);
|
section = mozParser.format(style);
|
||||||
}
|
}
|
||||||
|
|
||||||
const sourceCode = `/* ==UserStyle==
|
const DEFAULT_CODE = `
|
||||||
@name New Style - ${Date.now()}
|
/* ==UserStyle==
|
||||||
@namespace github.com/openstyles/stylus
|
@name ${t('usercssReplaceTemplateName') + ' - ' + new Date().toLocaleString()}
|
||||||
@version 0.1.0
|
@namespace github.com/openstyles/stylus
|
||||||
@description A new userstyle
|
@version 0.1.0
|
||||||
@author Me
|
@description A new userstyle
|
||||||
==/UserStyle== */
|
@author Me
|
||||||
|
==/UserStyle== */
|
||||||
|
|
||||||
${section}
|
${section}
|
||||||
`;
|
`.replace(/^\s+/gm, '');
|
||||||
dirty.modify('source', '', sourceCode);
|
dirty.clear('source');
|
||||||
style.sourceCode = sourceCode;
|
style.sourceCode = '';
|
||||||
|
BG.chromeSync.getLZValue('usercssTemplate').then(code => {
|
||||||
|
style.sourceCode = code || DEFAULT_CODE;
|
||||||
|
cm.startOperation();
|
||||||
|
cm.setValue(style.sourceCode);
|
||||||
|
cm.clearHistory();
|
||||||
|
cm.markClean();
|
||||||
|
cm.endOperation();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function initHooks() {
|
function initHooks() {
|
||||||
|
@ -187,11 +163,10 @@ ${section}
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateTitle() {
|
function updateTitle() {
|
||||||
// title depends on dirty and style meta
|
const newTitle = (dirty.isDirty() ? '* ' : '') +
|
||||||
if (!style.id) {
|
(style.id ? t('editStyleTitle', [style.name]) : t('addStyleTitle'));
|
||||||
document.title = t('addStyleTitle');
|
if (document.title !== newTitle) {
|
||||||
} else {
|
document.title = newTitle;
|
||||||
document.title = (dirty.isDirty() ? '* ' : '') + t('editStyleTitle', [style.name]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -241,6 +216,17 @@ ${section}
|
||||||
hadBeenSaved = true;
|
hadBeenSaved = true;
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
|
if (err.message === t('styleMissingMeta', 'name')) {
|
||||||
|
messageBox.confirm(t('usercssReplaceTemplateConfirmation')).then(ok => ok &&
|
||||||
|
BG.chromeSync.setLZValue('usercssTemplate', style.sourceCode)
|
||||||
|
.then(() => BG.chromeSync.getLZValue('usercssTemplate'))
|
||||||
|
.then(saved => {
|
||||||
|
if (saved !== style.sourceCode) {
|
||||||
|
messageBox.alert(t('syncStorageErrorSaving'));
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
return;
|
||||||
|
}
|
||||||
const contents = [String(err)];
|
const contents = [String(err)];
|
||||||
if (Number.isInteger(err.index)) {
|
if (Number.isInteger(err.index)) {
|
||||||
const pos = cm.posFromIndex(err.index);
|
const pos = cm.posFromIndex(err.index);
|
||||||
|
@ -250,7 +236,6 @@ ${section}
|
||||||
textContent: drawLinePointer(pos)
|
textContent: drawLinePointer(pos)
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
console.error(err);
|
|
||||||
messageBox.alert(contents);
|
messageBox.alert(contents);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -96,6 +96,9 @@ var usercss = (() => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const RX_NUMBER = /^-?\d+(\.\d+)?\s*/y;
|
||||||
|
const RX_WHITESPACE = /\s*/y;
|
||||||
|
|
||||||
function getMetaSource(source) {
|
function getMetaSource(source) {
|
||||||
const commentRe = /\/\*[\s\S]*?\*\//g;
|
const commentRe = /\/\*[\s\S]*?\*\//g;
|
||||||
const metaRe = /==userstyle==[\s\S]*?==\/userstyle==/i;
|
const metaRe = /==userstyle==[\s\S]*?==\/userstyle==/i;
|
||||||
|
@ -307,7 +310,8 @@ var usercss = (() => {
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseNumber(state) {
|
function parseNumber(state) {
|
||||||
const match = state.slice(state.re.lastIndex).match(/^-?\d+(\.\d+)?\s*/);
|
RX_NUMBER.lastIndex = state.re.lastIndex;
|
||||||
|
const match = RX_NUMBER.exec(state.text);
|
||||||
if (!match) {
|
if (!match) {
|
||||||
throw new Error('invalid number');
|
throw new Error('invalid number');
|
||||||
}
|
}
|
||||||
|
@ -316,19 +320,20 @@ var usercss = (() => {
|
||||||
}
|
}
|
||||||
|
|
||||||
function eatWhitespace(state) {
|
function eatWhitespace(state) {
|
||||||
const match = state.text.slice(state.re.lastIndex).match(/\s*/);
|
RX_WHITESPACE.lastIndex = state.re.lastIndex;
|
||||||
state.re.lastIndex += match[0].length;
|
state.re.lastIndex += RX_WHITESPACE.exec(state.text)[0].length;
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseStringToEnd(state) {
|
function parseStringToEnd(state) {
|
||||||
const match = state.text.slice(state.re.lastIndex).match(/.+/);
|
const EOL = state.text.indexOf('\n', state.re.lastIndex);
|
||||||
state.value = unquote(match[0].trim());
|
const match = state.text.slice(state.re.lastIndex, EOL >= 0 ? EOL : undefined);
|
||||||
state.re.lastIndex += match[0].length;
|
state.value = unquote(match.trim());
|
||||||
|
state.re.lastIndex += match.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
function unquote(s) {
|
function unquote(s) {
|
||||||
const q = s[0];
|
const q = s[0];
|
||||||
if (q === s[s.length - 1] && /['"`]/.test(q)) {
|
if (q === s[s.length - 1] && (q === '"' || q === "'")) {
|
||||||
// http://www.json.org/
|
// http://www.json.org/
|
||||||
return s.slice(1, -1).replace(
|
return s.slice(1, -1).replace(
|
||||||
new RegExp(`\\\\([${q}\\\\/bfnrt]|u[0-9a-fA-F]{4})`, 'g'),
|
new RegExp(`\\\\([${q}\\\\/bfnrt]|u[0-9a-fA-F]{4})`, 'g'),
|
||||||
|
@ -368,6 +373,10 @@ var usercss = (() => {
|
||||||
if (!(state.key in METAS)) {
|
if (!(state.key in METAS)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (text[re.lastIndex - 1] === '\n') {
|
||||||
|
// an empty value should point to EOL
|
||||||
|
re.lastIndex--;
|
||||||
|
}
|
||||||
if (state.key === 'var' || state.key === 'advanced') {
|
if (state.key === 'var' || state.key === 'advanced') {
|
||||||
if (state.key === 'advanced') {
|
if (state.key === 'advanced') {
|
||||||
state.maybeUSO = true;
|
state.maybeUSO = true;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user