add a terse invocation syntax for $element and rename it to $create
This commit is contained in:
parent
6e142a7444
commit
c0c60fb7a2
|
@ -55,7 +55,8 @@ globals:
|
|||
animateElement: false
|
||||
$: false
|
||||
$$: false
|
||||
$element: false
|
||||
$create: false
|
||||
$createLink: false
|
||||
# prefs.js
|
||||
prefs: false
|
||||
setupLivePrefs: false
|
||||
|
@ -236,7 +237,7 @@ rules:
|
|||
one-var: [0]
|
||||
operator-assignment: [2, always]
|
||||
operator-linebreak: [2, after, overrides: {"?": ignore, ":": ignore, "&&": ignore, "||": ignore}]
|
||||
padded-blocks: [2, never]
|
||||
padded-blocks: [0]
|
||||
prefer-numeric-literals: [2]
|
||||
prefer-rest-params: [0]
|
||||
prefer-const: [1, {destructuring: any, ignoreReadBeforeAssign: true}]
|
||||
|
|
|
@ -25,53 +25,25 @@ function createAppliesToLineWidget(cm) {
|
|||
|
||||
TPL = {
|
||||
container:
|
||||
$element({className: 'applies-to', appendChild: [
|
||||
$element({tag: 'label', appendChild: t('appliesLabel')}),
|
||||
$element({tag: 'ul', className: 'applies-to-list'}),
|
||||
]}),
|
||||
listItem: $element({
|
||||
tag: 'li',
|
||||
className: 'applies-to-item',
|
||||
appendChild: [
|
||||
$element({
|
||||
tag: 'select',
|
||||
className: 'applies-type',
|
||||
appendChild: [
|
||||
[t('appliesUrlOption'), 'url'],
|
||||
[t('appliesUrlPrefixOption'), 'url-prefix'],
|
||||
[t('appliesDomainOption'), 'domain'],
|
||||
[t('appliesRegexpOption'), 'regexp']
|
||||
].map(([textContent, value]) => $element({
|
||||
tag: 'option',
|
||||
value,
|
||||
textContent,
|
||||
})),
|
||||
}),
|
||||
$element({
|
||||
tag: 'input',
|
||||
className: 'applies-value',
|
||||
}),
|
||||
$element({
|
||||
tag: 'button',
|
||||
className: 'test-regexp',
|
||||
textContent: t('styleRegexpTestButton'),
|
||||
}),
|
||||
$element({
|
||||
tag: 'button',
|
||||
className: 'remove-applies-to',
|
||||
textContent: t('appliesRemove'),
|
||||
}),
|
||||
$element({
|
||||
tag: 'button',
|
||||
className: 'add-applies-to',
|
||||
textContent: t('appliesAdd'),
|
||||
})
|
||||
]}),
|
||||
appliesToEverything: $element({
|
||||
tag: 'li',
|
||||
className: 'applies-to-everything',
|
||||
textContent: t('appliesToEverything'),
|
||||
}),
|
||||
$create('div.applies-to', [
|
||||
$create('label', t('appliesLabel')),
|
||||
$create('ul.applies-to-list'),
|
||||
]),
|
||||
listItem:
|
||||
$create('li.applies-to-item', [
|
||||
$create('select.applies-type', [
|
||||
$create('option', {value: 'url'}, t('appliesUrlOption')),
|
||||
$create('option', {value: 'url-prefix'}, t('appliesUrlPrefixOption')),
|
||||
$create('option', {value: 'domain'}, t('appliesDomainOption')),
|
||||
$create('option', {value: 'regexp'}, t('appliesRegexpOption')),
|
||||
]),
|
||||
$create('input.applies-value'),
|
||||
$create('button.test-regexp', t('styleRegexpTestButton')),
|
||||
$create('button.remove-applies-to', t('appliesRemove')),
|
||||
$create('button.add-applies-to', t('appliesAdd')),
|
||||
]),
|
||||
appliesToEverything:
|
||||
$create('li.applies-to-everything', t('appliesToEverything')),
|
||||
};
|
||||
|
||||
CLICK_ROUTE = {
|
||||
|
@ -164,7 +136,7 @@ function createAppliesToLineWidget(cm) {
|
|||
}
|
||||
};
|
||||
|
||||
styleVariables = $element({tag: 'style'});
|
||||
styleVariables = $create('style');
|
||||
fromLine = 0;
|
||||
toLine = cm.doc.size;
|
||||
|
||||
|
|
|
@ -115,10 +115,8 @@ onDOMready().then(() => {
|
|||
if (option.type === 'checkbox') {
|
||||
option = (option.labels || [])[0] || option.nextElementSibling || option;
|
||||
}
|
||||
progress = document.body.appendChild($element({
|
||||
className: 'set-option-progress',
|
||||
targetElement: option,
|
||||
}));
|
||||
progress = document.body.appendChild(
|
||||
$create('.set-option-progress', {targetElement: option}));
|
||||
}
|
||||
}
|
||||
if (progress) {
|
||||
|
@ -222,12 +220,7 @@ onDOMready().then(() => {
|
|||
break;
|
||||
}
|
||||
// avoid flicker: wait for the second stylesheet to load, then apply the theme
|
||||
document.head.appendChild($element({
|
||||
tag: 'link',
|
||||
id: 'cm-theme2',
|
||||
rel: 'stylesheet',
|
||||
href: url
|
||||
}));
|
||||
document.head.appendChild($create('link#cm-theme2', {rel: 'stylesheet', href: url}));
|
||||
setTimeout(() => {
|
||||
CodeMirror.setOption(option, value);
|
||||
themeLink.remove();
|
||||
|
@ -287,7 +280,7 @@ onDOMready().then(() => {
|
|||
function optionsFromArray(parent, options) {
|
||||
const fragment = document.createDocumentFragment();
|
||||
for (const opt of options) {
|
||||
fragment.appendChild($element({tag: 'option', textContent: opt}));
|
||||
fragment.appendChild($create('option', opt));
|
||||
}
|
||||
parent.appendChild(fragment);
|
||||
}
|
||||
|
|
|
@ -69,8 +69,7 @@ var initColorpicker = () => {
|
|||
}
|
||||
|
||||
function configureColorpicker() {
|
||||
const input = $element({
|
||||
tag: 'input',
|
||||
const input = $create('input', {
|
||||
type: 'search',
|
||||
spellcheck: false,
|
||||
value: prefs.get('editor.colorpicker.hotkey'),
|
||||
|
|
18
edit/edit.js
18
edit/edit.js
|
@ -457,22 +457,20 @@ function toMozillaFormat() {
|
|||
|
||||
function fromMozillaFormat() {
|
||||
const popup = showCodeMirrorPopup(t('styleFromMozillaFormatPrompt'),
|
||||
$element({appendChild: [
|
||||
$element({
|
||||
tag: 'button',
|
||||
$create([
|
||||
$create('button', {
|
||||
name: 'import-append',
|
||||
textContent: t('importAppendLabel'),
|
||||
title: 'Ctrl-Enter:\n' + t('importAppendTooltip'),
|
||||
onclick: doImport,
|
||||
}),
|
||||
$element({
|
||||
tag: 'button',
|
||||
$create('button', {
|
||||
name: 'import-replace',
|
||||
textContent: t('importReplaceLabel'),
|
||||
title: 'Ctrl-Shift-Enter:\n' + t('importReplaceTooltip'),
|
||||
onclick: () => doImport({replaceOldStyle: true}),
|
||||
}),
|
||||
]}));
|
||||
]));
|
||||
const contents = $('.contents', popup);
|
||||
contents.insertBefore(popup.codebox.display.wrapper, contents.firstElementChild);
|
||||
popup.codebox.focus();
|
||||
|
@ -522,10 +520,8 @@ function fromMozillaFormat() {
|
|||
}
|
||||
|
||||
function showError(errors) {
|
||||
showHelp(t('styleFromMozillaFormatError'), $element({
|
||||
tag: 'pre',
|
||||
textContent: Array.isArray(errors) ? errors.join('\n') : errors,
|
||||
}));
|
||||
showHelp(t('styleFromMozillaFormatError'),
|
||||
$create('pre', Array.isArray(errors) ? errors.join('\n') : errors));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -617,7 +613,7 @@ function showCodeMirrorPopup(title, html, options) {
|
|||
|
||||
function setGlobalProgress(done, total) {
|
||||
const progressElement = $('#global-progress') ||
|
||||
total && document.body.appendChild($element({id: 'global-progress'}));
|
||||
total && document.body.appendChild($create('#global-progress'));
|
||||
if (total) {
|
||||
const progress = (done / Math.max(done, total) * 100).toFixed(1);
|
||||
progressElement.style.borderLeftWidth = progress + 'vw';
|
||||
|
|
134
edit/lint.js
134
edit/lint.js
|
@ -1,7 +1,6 @@
|
|||
/* global CodeMirror messageBox */
|
||||
/* global editors makeSectionVisible showCodeMirrorPopup showHelp */
|
||||
/* global loadScript require CSSLint stylelint */
|
||||
/* global makeLink */
|
||||
'use strict';
|
||||
|
||||
onDOMready().then(loadLinterAssets);
|
||||
|
@ -231,9 +230,7 @@ function updateLinter({immediately, linter = linterConfig.getName()} = {}) {
|
|||
cm.options.gutters = guttersOption;
|
||||
const el = $('.' + GUTTERS_CLASS, cm.display.gutters);
|
||||
if (linter && !el) {
|
||||
cm.display.gutters.appendChild($element({
|
||||
className: 'CodeMirror-gutter ' + GUTTERS_CLASS
|
||||
}));
|
||||
cm.display.gutters.appendChild($create('.CodeMirror-gutter ' + GUTTERS_CLASS));
|
||||
} else if (!linter && el) {
|
||||
el.remove();
|
||||
}
|
||||
|
@ -281,9 +278,8 @@ function updateLintReportInternal(scope, {postponeNewIssues} = {}) {
|
|||
const newMarkers = lintState.stylusMarkers = new Map();
|
||||
const oldText = (lintState.body || {}).textContentCached || '';
|
||||
const activeLine = cm.getCursor().line;
|
||||
const body = !(lintState.marked || {}).length ? {} : $element({
|
||||
tag: 'tbody',
|
||||
appendChild: lintState.marked.map(mark => {
|
||||
const body = !(lintState.marked || {}).length ? {} :
|
||||
$create('tbody', lintState.marked.map(mark => {
|
||||
const info = mark.__annotation;
|
||||
const {line, ch} = info.from;
|
||||
const isActiveLine = line === activeLine;
|
||||
|
@ -294,27 +290,15 @@ function updateLintReportInternal(scope, {postponeNewIssues} = {}) {
|
|||
oldMarkers.delete(pos);
|
||||
}
|
||||
newMarkers.set(pos, message);
|
||||
return $element({
|
||||
tag: 'tr',
|
||||
className: info.severity,
|
||||
appendChild: [
|
||||
$element({
|
||||
tag: 'td',
|
||||
attributes: {role: 'severity'},
|
||||
dataset: {rule: info.rule},
|
||||
appendChild: $element({
|
||||
className: 'CodeMirror-lint-marker-' + info.severity,
|
||||
textContent: info.severity,
|
||||
}),
|
||||
}),
|
||||
$element({tag: 'td', attributes: {role: 'line'}, textContent: line + 1}),
|
||||
$element({tag: 'td', attributes: {role: 'sep'}, textContent: ':'}),
|
||||
$element({tag: 'td', attributes: {role: 'col'}, textContent: ch + 1}),
|
||||
$element({tag: 'td', attributes: {role: 'message'}, textContent: message, title}),
|
||||
],
|
||||
});
|
||||
})
|
||||
});
|
||||
return $create(`tr.${info.severity}`, [
|
||||
$create('td', {attributes: {role: 'severity'}, dataset: {rule: info.rule}},
|
||||
$create('.CodeMirror-lint-marker-' + info.severity, info.severity)),
|
||||
$create('td', {attributes: {role: 'line'}}, line + 1),
|
||||
$create('td', {attributes: {role: 'sep'}}, ':'),
|
||||
$create('td', {attributes: {role: 'col'}}, ch + 1),
|
||||
$create('td', {attributes: {role: 'message'}, title}, message),
|
||||
]);
|
||||
}));
|
||||
body.textContentCached = body.textContent || '';
|
||||
lintState.body = body.textContentCached && body;
|
||||
result.changed |= oldText !== body.textContentCached;
|
||||
|
@ -340,14 +324,10 @@ function renderLintReport(someBlockChanged) {
|
|||
if (!body) {
|
||||
return;
|
||||
}
|
||||
const newBlock = $element({
|
||||
tag: 'table',
|
||||
appendChild: [
|
||||
$element({tag: 'caption', textContent: label + ' ' + (index + 1)}),
|
||||
body,
|
||||
],
|
||||
cm,
|
||||
});
|
||||
const newBlock = $create('table', {cm}, [
|
||||
$create('caption', label + ' ' + (index + 1)),
|
||||
body,
|
||||
]);
|
||||
newContent.appendChild(newBlock);
|
||||
issueCount += newBlock.rows.length;
|
||||
|
||||
|
@ -388,35 +368,29 @@ function showLintHelp() {
|
|||
: 'https://github.com/CSSLint/csslint/issues/535';
|
||||
let headerLink, template;
|
||||
if (linter === 'csslint') {
|
||||
headerLink = makeLink('https://github.com/CSSLint/csslint/wiki/Rules-by-ID', 'CSSLint');
|
||||
headerLink = $createLink('https://github.com/CSSLint/csslint/wiki/Rules-by-ID', 'CSSLint');
|
||||
template = ruleID => {
|
||||
const rule = linterConfig.allRuleIds.csslint.find(rule => rule.id === ruleID);
|
||||
return rule &&
|
||||
$element({tag: 'li', appendChild: [
|
||||
$element({tag: 'b', appendChild: makeLink(rule.url || baseUrl, rule.name)}),
|
||||
$element({tag: 'br'}),
|
||||
$create('li', [
|
||||
$create('b', $createLink(rule.url || baseUrl, rule.name)),
|
||||
$create('br'),
|
||||
rule.desc,
|
||||
]});
|
||||
]);
|
||||
};
|
||||
} else {
|
||||
headerLink = makeLink(baseUrl, 'stylelint');
|
||||
headerLink = $createLink(baseUrl, 'stylelint');
|
||||
template = rule =>
|
||||
$element({
|
||||
tag: 'li',
|
||||
appendChild: makeLink(baseUrl + rule, rule),
|
||||
});
|
||||
$create('li',
|
||||
$createLink(baseUrl + rule, rule));
|
||||
}
|
||||
const header = t('linterIssuesHelp', '\x01').split('\x01');
|
||||
const activeRules = new Set($$('#lint td[role="severity"]').map(el => el.dataset.rule));
|
||||
return showHelp(t('linterIssues'),
|
||||
$element({appendChild: [
|
||||
$create([
|
||||
header[0], headerLink, header[1],
|
||||
$element({
|
||||
tag: 'ul',
|
||||
className: 'rules',
|
||||
appendChild: [...activeRules.values()].map(template),
|
||||
}),
|
||||
]})
|
||||
$create('ul.rules', [...activeRules.values()].map(template)),
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -465,41 +439,21 @@ function setupLinterPopup(config) {
|
|||
});
|
||||
|
||||
function makeFooter() {
|
||||
const makeButton = (className, onclick, text, options = {}) =>
|
||||
$element(Object.assign(options, {
|
||||
className,
|
||||
onclick,
|
||||
tag: 'button',
|
||||
type: 'button',
|
||||
textContent: t(text),
|
||||
}));
|
||||
return $element({
|
||||
appendChild: [
|
||||
$element({
|
||||
tag: 'p',
|
||||
appendChild: [
|
||||
t('linterRulesLink') + ' ',
|
||||
$element({
|
||||
tag: 'a',
|
||||
target: '_blank',
|
||||
href: linter === 'stylelint'
|
||||
? 'https://stylelint.io/user-guide/rules/'
|
||||
: 'https://github.com/CSSLint/csslint/wiki/Rules-by-ID',
|
||||
textContent: linterTitle
|
||||
}),
|
||||
linter === 'csslint' ? ' ' + t('linterCSSLintSettings') : ''
|
||||
]
|
||||
}),
|
||||
makeButton('save', save, 'styleSaveLabel', {title: 'Ctrl-Enter'}),
|
||||
makeButton('cancel', cancel, 'confirmClose'),
|
||||
makeButton('reset', reset, 'genericResetLabel', {title: t('linterResetMessage')}),
|
||||
$element({
|
||||
tag: 'span',
|
||||
className: 'saved-message',
|
||||
textContent: t('genericSavedMessage')
|
||||
})
|
||||
]
|
||||
});
|
||||
return $create('div', [
|
||||
$create('p', [
|
||||
t('linterRulesLink') + ' ',
|
||||
$createLink(
|
||||
linter === 'stylelint'
|
||||
? 'https://stylelint.io/user-guide/rules/'
|
||||
: 'https://github.com/CSSLint/csslint/wiki/Rules-by-ID',
|
||||
linterTitle),
|
||||
linter === 'csslint' ? ' ' + t('linterCSSLintSettings') : '',
|
||||
]),
|
||||
$create('button.save', {onclick: save, title: 'Ctrl-Enter'}, t('styleSaveLabel')),
|
||||
$create('button.cancel', {onclick: cancel}, t('confirmClose')),
|
||||
$create('button.reset', {onclick: reset, title: t('linterResetMessage')}, t('genericResetLabel')),
|
||||
$create('span.saved-message', t('genericSavedMessage')),
|
||||
]);
|
||||
}
|
||||
|
||||
function save(event) {
|
||||
|
@ -516,9 +470,7 @@ function setupLinterPopup(config) {
|
|||
if (invalid.length) {
|
||||
showLinterErrorMessage(linter, [
|
||||
t('linterInvalidConfigError'),
|
||||
$element({tag: 'ul', appendChild: invalid.map(name =>
|
||||
$element({tag: 'li', textContent: name})),
|
||||
}),
|
||||
$create('ul', invalid.map(name => $create('li', name))),
|
||||
], popup);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ var regExpTester = (() => {
|
|||
if (!isInit) {
|
||||
init();
|
||||
}
|
||||
showHelp('', $element({className: 'regexp-report'}));
|
||||
showHelp('', $create('.regexp-report'));
|
||||
} else if (!state && isShown()) {
|
||||
if (isInit) {
|
||||
uninit();
|
||||
|
@ -108,19 +108,19 @@ var regExpTester = (() => {
|
|||
const faviconUrl = url.startsWith(URLS.ownOrigin)
|
||||
? OWN_ICON
|
||||
: GET_FAVICON_URL + new URL(url).hostname;
|
||||
const icon = $element({tag: 'img', src: faviconUrl});
|
||||
const icon = $create('img', {src: faviconUrl});
|
||||
if (match.text.length === url.length) {
|
||||
full.push($element({appendChild: [
|
||||
full.push($create('div', [
|
||||
icon,
|
||||
url,
|
||||
]}));
|
||||
]));
|
||||
} else {
|
||||
partial.push($element({appendChild: [
|
||||
partial.push($create('div', [
|
||||
icon,
|
||||
url.substr(0, match.pos),
|
||||
$element({tag: 'mark', textContent: match.text}),
|
||||
$create('mark', match.text),
|
||||
url.substr(match.pos + match.text.length),
|
||||
]}));
|
||||
]));
|
||||
}
|
||||
}
|
||||
if (full.length) {
|
||||
|
@ -131,33 +131,28 @@ var regExpTester = (() => {
|
|||
}
|
||||
}
|
||||
// render stats
|
||||
const report = $element({className: 'regexp-report'});
|
||||
const br = $element({tag: 'br'});
|
||||
const report = $create('.regexp-report');
|
||||
const br = $create('br');
|
||||
for (const type in stats) {
|
||||
// top level groups: full, partial, none, invalid
|
||||
const {label, data} = stats[type];
|
||||
if (!data.length) {
|
||||
continue;
|
||||
}
|
||||
const block = report.appendChild($element({
|
||||
tag: 'details',
|
||||
open: true,
|
||||
dataset: {type},
|
||||
appendChild: $element({tag: 'summary', appendChild: label}),
|
||||
}));
|
||||
const block = report.appendChild(
|
||||
$create('details', {open: true, dataset: {type}}, [
|
||||
$create('summary', label),
|
||||
]));
|
||||
// 2nd level: regexp text
|
||||
for (const {text, urls} of data) {
|
||||
if (urls) {
|
||||
// type is partial or full
|
||||
block.appendChild($element({
|
||||
tag: 'details',
|
||||
open: true,
|
||||
appendChild: [
|
||||
$element({tag: 'summary', textContent: text}),
|
||||
block.appendChild(
|
||||
$create('details', {open: true}, [
|
||||
$create('summary', text),
|
||||
// 3rd level: tab urls
|
||||
...urls,
|
||||
],
|
||||
}));
|
||||
]));
|
||||
} else {
|
||||
// type is none or invalid
|
||||
block.appendChild(document.createTextNode(text));
|
||||
|
@ -165,12 +160,11 @@ var regExpTester = (() => {
|
|||
}
|
||||
}
|
||||
}
|
||||
report.appendChild($element({
|
||||
tag: 'p',
|
||||
className: 'regexp-report-note',
|
||||
appendChild: t('styleRegexpTestNote').split(/(\\+)/)
|
||||
.map(s => s.startsWith('\\') ? $element({tag: 'code', textContent: s}) : s),
|
||||
}));
|
||||
report.appendChild(
|
||||
$create('p.regexp-report-note',
|
||||
t('styleRegexpTestNote')
|
||||
.split(/(\\+)/)
|
||||
.map(s => (s.startsWith('\\') ? $create('code', s) : s))));
|
||||
showHelp(t('styleRegexpTestTitle'), report);
|
||||
|
||||
report.onclick = event => {
|
||||
|
|
|
@ -64,7 +64,7 @@ function showKeyMapHelp() {
|
|||
if (index > offset) {
|
||||
cell.appendChild(document.createTextNode(text.substring(offset, index)));
|
||||
}
|
||||
cell.appendChild($element({tag: 'mark', textContent: match}));
|
||||
cell.appendChild($create('mark', match));
|
||||
offset = index + match.length;
|
||||
});
|
||||
if (offset + 1 !== text.length) {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/* global CodeMirror dirtyReporter initLint */
|
||||
/* global showToggleStyleHelp goBackToManage updateLintReportIfEnabled */
|
||||
/* global editors linterConfig updateLinter regExpTester mozParser */
|
||||
/* global makeLink createAppliesToLineWidget messageBox */
|
||||
/* global createAppliesToLineWidget messageBox */
|
||||
'use strict';
|
||||
|
||||
function createSourceEditor(style) {
|
||||
|
@ -12,9 +12,7 @@ function createSourceEditor(style) {
|
|||
$('#name').disabled = true;
|
||||
$('#mozilla-format-container').remove();
|
||||
$('#sections').textContent = '';
|
||||
$('#sections').appendChild(
|
||||
$element({className: 'single-editor'})
|
||||
);
|
||||
$('#sections').appendChild($create('.single-editor'));
|
||||
|
||||
const dirty = dirtyReporter();
|
||||
dirty.onChange(() => {
|
||||
|
@ -236,15 +234,12 @@ function createSourceEditor(style) {
|
|||
return;
|
||||
}
|
||||
const contents = Array.isArray(err) ?
|
||||
$element({tag: 'pre', textContent: err.join('\n')}) :
|
||||
$create('pre', err.join('\n')) :
|
||||
[String(err)];
|
||||
if (Number.isInteger(err.index)) {
|
||||
const pos = cm.posFromIndex(err.index);
|
||||
contents[0] += ` (line ${pos.line + 1} col ${pos.ch + 1})`;
|
||||
contents.push($element({
|
||||
tag: 'pre',
|
||||
textContent: drawLinePointer(pos)
|
||||
}));
|
||||
contents.push($create('pre', drawLinePointer(pos)));
|
||||
}
|
||||
messageBox.alert(contents);
|
||||
});
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* global CodeMirror semverCompare makeLink closeCurrentTab */
|
||||
/* global CodeMirror semverCompare closeCurrentTab */
|
||||
/* global messageBox download chromeLocal */
|
||||
'use strict';
|
||||
|
||||
|
@ -44,11 +44,8 @@
|
|||
|
||||
setTimeout(() => {
|
||||
if (!installed) {
|
||||
const div = $element({});
|
||||
$('.header').appendChild($element({
|
||||
className: 'lds-spinner',
|
||||
appendChild: new Array(12).fill(div).map(e => e.cloneNode()),
|
||||
}));
|
||||
$('.header').appendChild($create('.lds-spinner',
|
||||
new Array(12).fill($create('div')).map(e => e.cloneNode())));
|
||||
}
|
||||
}, 200);
|
||||
|
||||
|
@ -101,8 +98,7 @@
|
|||
|
||||
$('.applies-to').textContent = '';
|
||||
getAppliesTo(style).forEach(pattern =>
|
||||
$('.applies-to').appendChild($element({tag: 'li', textContent: pattern}))
|
||||
);
|
||||
$('.applies-to').appendChild($create('li', pattern)));
|
||||
|
||||
$('.external-link').textContent = '';
|
||||
const externalLink = makeExternalLink();
|
||||
|
@ -125,46 +121,35 @@
|
|||
const [, name, email, url] = match;
|
||||
const frag = document.createDocumentFragment();
|
||||
if (email) {
|
||||
frag.appendChild(makeLink(`mailto:${email}`, name));
|
||||
frag.appendChild($createLink(`mailto:${email}`, name));
|
||||
} else {
|
||||
frag.appendChild($element({
|
||||
tag: 'span',
|
||||
textContent: name
|
||||
}));
|
||||
frag.appendChild($create('span', name));
|
||||
}
|
||||
if (url) {
|
||||
frag.appendChild(makeLink(
|
||||
url,
|
||||
$element({
|
||||
tag: 'svg#svg',
|
||||
viewBox: '0 0 20 20',
|
||||
class: 'svg-icon',
|
||||
appendChild: $element({
|
||||
tag: 'svg#path',
|
||||
frag.appendChild($createLink(url,
|
||||
$create('SVG:svg.svg-icon', {viewBox: '0 0 20 20'},
|
||||
$create('SVG:path', {
|
||||
d: 'M4,4h5v2H6v8h8v-3h2v5H4V4z M11,3h6v6l-2-2l-4,4L9,9l4-4L11,3z'
|
||||
})
|
||||
})
|
||||
}))
|
||||
));
|
||||
}
|
||||
return frag;
|
||||
}
|
||||
|
||||
function makeExternalLink() {
|
||||
const urls = [];
|
||||
if (data.homepageURL) {
|
||||
urls.push([data.homepageURL, t('externalHomepage')]);
|
||||
}
|
||||
if (data.supportURL) {
|
||||
urls.push([data.supportURL, t('externalSupport')]);
|
||||
}
|
||||
if (urls.length) {
|
||||
return $element({appendChild: [
|
||||
$element({tag: 'h3', textContent: t('externalLink')}),
|
||||
$element({tag: 'ul', appendChild: urls.map(args =>
|
||||
$element({tag: 'li', appendChild: makeLink(...args)})
|
||||
)})
|
||||
]});
|
||||
}
|
||||
const urls = [
|
||||
data.homepageURL && [data.homepageURL, t('externalHomepage')],
|
||||
data.supportURL && [data.supportURL, t('externalSupport')],
|
||||
];
|
||||
return (data.homepageURL || data.supportURL) && (
|
||||
$create('div', [
|
||||
$create('h3', t('externalLink')),
|
||||
$create('ul', urls.map(args => args &&
|
||||
$create('li',
|
||||
$createLink(...args)
|
||||
)
|
||||
))
|
||||
]));
|
||||
}
|
||||
|
||||
function installButtonClass() {
|
||||
|
@ -230,26 +215,23 @@
|
|||
|
||||
function buildWarning(err) {
|
||||
const contents = Array.isArray(err) ?
|
||||
$element({tag: 'pre', textContent: err.join('\n')}) :
|
||||
$create('pre', err.join('\n')) :
|
||||
[err && err.message || err || 'Unknown error'];
|
||||
if (Number.isInteger(err.index)) {
|
||||
const pos = cm.posFromIndex(err.index);
|
||||
contents[0] = `${pos.line + 1}:${pos.ch + 1} ` + contents[0];
|
||||
contents.push($element({
|
||||
tag: 'pre',
|
||||
textContent: drawLinePointer(pos)
|
||||
}));
|
||||
contents.push($create('pre', drawLinePointer(pos)));
|
||||
setTimeout(() => {
|
||||
cm.scrollIntoView({line: pos.line + 1, ch: pos.ch}, window.innerHeight / 4);
|
||||
cm.setCursor(pos.line, pos.ch + 1);
|
||||
cm.focus();
|
||||
});
|
||||
}
|
||||
return $element({className: 'warning', appendChild: [
|
||||
return $create('.warning', [
|
||||
t('parseUsercssError'),
|
||||
'\n',
|
||||
...contents,
|
||||
]});
|
||||
]);
|
||||
}
|
||||
|
||||
function drawLinePointer(pos) {
|
||||
|
@ -281,7 +263,7 @@
|
|||
// update UI
|
||||
if (versionTest < 0) {
|
||||
$('.actions').parentNode.insertBefore(
|
||||
$element({className: 'warning', textContent: t('versionInvalidOlder')}),
|
||||
$create('.warning', t('versionInvalidOlder')),
|
||||
$('.actions')
|
||||
);
|
||||
}
|
||||
|
|
87
js/dom.js
87
js/dom.js
|
@ -73,8 +73,7 @@ if (!chrome.app && chrome.windows) {
|
|||
}
|
||||
const iconset = ['', 'light/'][prefs.get('iconset')] || '';
|
||||
for (const size of [38, 32, 19, 16]) {
|
||||
document.head.appendChild($element({
|
||||
tag: 'link',
|
||||
document.head.appendChild($create('link', {
|
||||
rel: 'icon',
|
||||
href: `/images/icon/${iconset}${size}.png`,
|
||||
sizes: size + 'x' + size,
|
||||
|
@ -167,35 +166,88 @@ function $$(selector, base = document) {
|
|||
}
|
||||
|
||||
|
||||
function $element(opt) {
|
||||
// tag: string, default 'div', may include namespace like 'ns#tag'
|
||||
// appendChild: element/string or an array of elements/strings
|
||||
// dataset: object
|
||||
// any DOM property: assigned as is
|
||||
const [ns, tag] = opt.tag && opt.tag.includes('#')
|
||||
? opt.tag.split('#')
|
||||
: [null, opt.tag];
|
||||
function $create(selector = 'div', properties, children) {
|
||||
/*
|
||||
$create('tag#id.class.class', ?[children])
|
||||
$create('tag#id.class.class', ?textContentOrChildNode)
|
||||
$create('tag#id.class.class', {properties}, ?[children])
|
||||
$create('tag#id.class.class', {properties}, ?textContentOrChildNode)
|
||||
tag is 'div' by default, #id and .class are optional
|
||||
|
||||
$create([children])
|
||||
|
||||
$create({propertiesAndOptions})
|
||||
$create({propertiesAndOptions}, ?[children])
|
||||
tag: string, default 'div'
|
||||
appendChild: element/string or an array of elements/strings
|
||||
dataset: object
|
||||
any DOM property: assigned as is
|
||||
|
||||
tag may include namespace like 'ns:tag'
|
||||
*/
|
||||
let ns, tag, opt;
|
||||
|
||||
if (typeof selector === 'string') {
|
||||
if (Array.isArray(properties) ||
|
||||
properties instanceof Node ||
|
||||
typeof properties !== 'object') {
|
||||
opt = {};
|
||||
children = properties;
|
||||
} else {
|
||||
opt = properties || {};
|
||||
}
|
||||
const idStart = (selector.indexOf('#') + 1 || selector.length + 1) - 1;
|
||||
const classStart = (selector.indexOf('.') + 1 || selector.length + 1) - 1;
|
||||
const id = selector.slice(idStart + 1, classStart);
|
||||
if (id) {
|
||||
opt.id = id;
|
||||
}
|
||||
const cls = selector.slice(classStart + 1);
|
||||
if (cls) {
|
||||
opt[selector.includes(':') ? 'class' : 'className'] =
|
||||
cls.includes('.') ? cls.replace(/\./g, ' ') : cls;
|
||||
}
|
||||
tag = selector.slice(0, Math.min(idStart, classStart));
|
||||
|
||||
} else if (Array.isArray(selector)) {
|
||||
tag = 'div';
|
||||
opt = {};
|
||||
children = selector;
|
||||
|
||||
} else {
|
||||
opt = selector;
|
||||
tag = opt.tag;
|
||||
delete opt.tag;
|
||||
children = opt.appendChild || properties;
|
||||
delete opt.appendChild;
|
||||
}
|
||||
|
||||
if (tag && tag.includes(':')) {
|
||||
([ns, tag] = tag.split(':'));
|
||||
}
|
||||
|
||||
const element = ns
|
||||
? document.createElementNS(ns === 'SVG' || ns === 'svg' ? 'http://www.w3.org/2000/svg' : ns, tag)
|
||||
: document.createElement(tag || 'div');
|
||||
const children = Array.isArray(opt.appendChild) ? opt.appendChild : [opt.appendChild];
|
||||
for (const child of children) {
|
||||
|
||||
for (const child of Array.isArray(children) ? children : [children]) {
|
||||
if (child) {
|
||||
element.appendChild(child instanceof Node ? child : document.createTextNode(child));
|
||||
}
|
||||
}
|
||||
delete opt.appendChild;
|
||||
delete opt.tag;
|
||||
|
||||
if (opt.dataset) {
|
||||
Object.assign(element.dataset, opt.dataset);
|
||||
delete opt.dataset;
|
||||
}
|
||||
|
||||
if (opt.attributes) {
|
||||
for (const attr in opt.attributes) {
|
||||
element.setAttribute(attr, opt.attributes[attr]);
|
||||
}
|
||||
delete opt.attributes;
|
||||
}
|
||||
|
||||
if (ns) {
|
||||
for (const attr in opt) {
|
||||
element.setAttributeNS(null, attr, opt[attr]);
|
||||
|
@ -203,11 +255,12 @@ function $element(opt) {
|
|||
} else {
|
||||
Object.assign(element, opt);
|
||||
}
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
|
||||
function makeLink(href = '', content) {
|
||||
function $createLink(href = '', content) {
|
||||
const opt = {
|
||||
tag: 'a',
|
||||
target: '_blank',
|
||||
|
@ -217,9 +270,9 @@ function makeLink(href = '', content) {
|
|||
Object.assign(opt, href);
|
||||
} else {
|
||||
opt.href = href;
|
||||
opt.appendChild = content;
|
||||
}
|
||||
return $element(opt);
|
||||
opt.appendChild = opt.appendChild || content;
|
||||
return $create(opt);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
/* global messageBox makeLink */
|
||||
/* global messageBox */
|
||||
'use strict';
|
||||
|
||||
function configDialog(style) {
|
||||
const varsHash = deepCopy(style.usercssData.vars) || {};
|
||||
const data = style.usercssData;
|
||||
const varsHash = deepCopy(data.vars) || {};
|
||||
const varNames = Object.keys(varsHash);
|
||||
const vars = varNames.map(name => varsHash[name]);
|
||||
const elements = [];
|
||||
|
@ -12,21 +13,12 @@ function configDialog(style) {
|
|||
renderValues();
|
||||
|
||||
return messageBox({
|
||||
title: `${style.name} v${style.usercssData.version}`,
|
||||
title: `${style.name} v${data.version}`,
|
||||
className: 'config-dialog',
|
||||
contents: [
|
||||
$element({
|
||||
className: 'config-heading',
|
||||
appendChild: style.usercssData.supportURL && makeLink({
|
||||
className: 'external-support',
|
||||
href: style.usercssData.supportURL,
|
||||
textContent: t('externalFeedback')
|
||||
})
|
||||
}),
|
||||
$element({
|
||||
className: 'config-body',
|
||||
appendChild: elements
|
||||
})
|
||||
$create('.config-heading', data.supportURL &&
|
||||
$createLink({className: '.external-support', href: data.supportURL}, t('externalFeedback'))),
|
||||
$create('.config-body', elements)
|
||||
],
|
||||
buttons: [
|
||||
t('confirmSave'),
|
||||
|
@ -71,19 +63,17 @@ function configDialog(style) {
|
|||
continue;
|
||||
}
|
||||
invalid.push(['*' + va.name, ': ', ...error].map(e =>
|
||||
e[0] === '*' && $element({tag: 'b', textContent: e.slice(1)}) || e));
|
||||
e[0] === '*' && $create('b', e.slice(1)) || e));
|
||||
if (bgva) {
|
||||
styleVars[va.name].value = deepCopy(bgva);
|
||||
}
|
||||
}
|
||||
if (invalid.length) {
|
||||
messageBox.alert([
|
||||
$element({textContent: t('usercssConfigIncomplete'), style: 'max-width: 34em'}),
|
||||
$element({
|
||||
tag: 'ol',
|
||||
style: 'text-align: left',
|
||||
appendChild: invalid.map(msg => $element({tag: 'li', appendChild: msg})),
|
||||
}),
|
||||
$create('div', {style: 'max-width: 34em'}, t('usercssConfigIncomplete')),
|
||||
$create('ol', {style: 'text-align: left'},
|
||||
invalid.map(msg =>
|
||||
$create({tag: 'li', appendChild: msg}))),
|
||||
]);
|
||||
}
|
||||
return numValid && BG.usercssHelper.save(style);
|
||||
|
@ -91,30 +81,28 @@ function configDialog(style) {
|
|||
|
||||
function buildConfigForm() {
|
||||
for (const va of vars) {
|
||||
let appendChild;
|
||||
let children;
|
||||
switch (va.type) {
|
||||
case 'color':
|
||||
appendChild = [$element({
|
||||
className: 'cm-colorview',
|
||||
appendChild: va.inputColor = $element({
|
||||
va,
|
||||
className: 'color-swatch',
|
||||
onclick: showColorpicker,
|
||||
})
|
||||
})];
|
||||
va.inputColor = $create('.color-swatch', {va, onclick: showColorpicker});
|
||||
children = [
|
||||
$create('.cm-colorview', [
|
||||
va.inputColor,
|
||||
]),
|
||||
];
|
||||
break;
|
||||
|
||||
case 'checkbox':
|
||||
va.input = $element({tag: 'input', type: 'checkbox', className: 'slider'});
|
||||
va.input = $create('input.slider', {type: 'checkbox'});
|
||||
va.input.onchange = () => {
|
||||
va.dirty = true;
|
||||
va.value = String(Number(va.input.checked));
|
||||
};
|
||||
appendChild = [
|
||||
$element({tag: 'span', className: 'onoffswitch', appendChild: [
|
||||
children = [
|
||||
$create('span.onoffswitch', [
|
||||
va.input,
|
||||
$element({tag: 'span'})
|
||||
]})
|
||||
$create('span'),
|
||||
])
|
||||
];
|
||||
break;
|
||||
|
||||
|
@ -122,36 +110,30 @@ function configDialog(style) {
|
|||
case 'dropdown':
|
||||
case 'image':
|
||||
// TODO: a image picker input?
|
||||
va.input = $element({
|
||||
tag: 'select',
|
||||
appendChild: va.options.map(o => $element({
|
||||
tag: 'option', value: o.name, textContent: o.label
|
||||
}))
|
||||
});
|
||||
va.input = $create('select',
|
||||
va.options.map(o =>
|
||||
$create('option', {value: o.name}, o.label)));
|
||||
va.input.onchange = () => {
|
||||
va.dirty = true;
|
||||
va.value = va.input.value;
|
||||
};
|
||||
appendChild = [va.input];
|
||||
children = [va.input];
|
||||
break;
|
||||
|
||||
default:
|
||||
va.input = $element({tag: 'input', type: 'text'});
|
||||
va.input = $create('input', {type: 'text'});
|
||||
va.input.oninput = () => {
|
||||
va.dirty = true;
|
||||
va.value = va.input.value;
|
||||
};
|
||||
appendChild = [va.input];
|
||||
children = [va.input];
|
||||
break;
|
||||
}
|
||||
elements.push($element({
|
||||
tag: 'label',
|
||||
className: `config-${va.type}`,
|
||||
appendChild: [
|
||||
$element({tag: 'span', appendChild: va.label}),
|
||||
...appendChild,
|
||||
],
|
||||
}));
|
||||
elements.push(
|
||||
$create(`label.config-${va.type}`, [
|
||||
$create('span', va.label),
|
||||
...children,
|
||||
]));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -175,18 +175,16 @@ function importFromString(jsonString) {
|
|||
.map(kind => {
|
||||
const {ids, names, legend} = stats[kind];
|
||||
const listItemsWithId = (name, i) =>
|
||||
$element({dataset: {id: ids[i]}, textContent: name});
|
||||
$create('div', {dataset: {id: ids[i]}}, name);
|
||||
const listItems = name =>
|
||||
$element({textContent: name});
|
||||
$create('div', name);
|
||||
const block =
|
||||
$element({tag: 'details', dataset: {id: kind}, appendChild: [
|
||||
$element({tag: 'summary', appendChild:
|
||||
$element({tag: 'b', textContent: names.length + ' ' + t(legend)})
|
||||
}),
|
||||
$element({tag: 'small', appendChild:
|
||||
names.map(ids ? listItemsWithId : listItems)
|
||||
}),
|
||||
]});
|
||||
$create('details', {dataset: {id: kind}}, [
|
||||
$create('summary',
|
||||
$create('b', names.length + ' ' + t(legend))),
|
||||
$create('small',
|
||||
names.map(ids ? listItemsWithId : listItems)),
|
||||
]);
|
||||
return block;
|
||||
});
|
||||
scrollTo(0, 0);
|
||||
|
@ -308,8 +306,7 @@ $('#file-all-styles').onclick = () => {
|
|||
const text = JSON.stringify(styles, null, '\t');
|
||||
const blob = new Blob([text], {type: 'application/json'});
|
||||
const objectURL = URL.createObjectURL(blob);
|
||||
let link = $element({
|
||||
tag:'a',
|
||||
let link = $create('a', {
|
||||
href: objectURL,
|
||||
type: 'application/json',
|
||||
download: generateFileName(),
|
||||
|
@ -319,8 +316,7 @@ $('#file-all-styles').onclick = () => {
|
|||
link.dispatchEvent(new MouseEvent('click'));
|
||||
setTimeout(() => URL.revokeObjectURL(objectURL));
|
||||
} else {
|
||||
const iframe = document.body.appendChild($element({
|
||||
tag: 'iframe',
|
||||
const iframe = document.body.appendChild($create('iframe', {
|
||||
style: 'width: 0; height: 0; position: fixed; opacity: 0;'.replace(/;/g, '!important;'),
|
||||
}));
|
||||
doTimeout()
|
||||
|
|
|
@ -5,8 +5,7 @@ onDOMready().then(() => {
|
|||
let prevText, focusedLink, focusedEntry;
|
||||
let prevTime = performance.now();
|
||||
let focusedName = '';
|
||||
const input = $element({
|
||||
tag: 'textarea',
|
||||
const input = $create('textarea', {
|
||||
spellcheck: false,
|
||||
oninput: incrementalSearch,
|
||||
});
|
||||
|
|
|
@ -84,14 +84,14 @@ function initGlobalEvents() {
|
|||
switchUI({styleOnly: true});
|
||||
|
||||
// translate CSS manually
|
||||
document.head.appendChild($element({tag: 'style', textContent: `
|
||||
document.head.appendChild($create('style', `
|
||||
.disabled h2::after {
|
||||
content: "${t('genericDisabledLabel')}";
|
||||
}
|
||||
#update-all-no-updates[data-skipped-edited="true"]:after {
|
||||
content: " ${t('updateAllCheckSucceededSomeEdited')}";
|
||||
}
|
||||
`}));
|
||||
`));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -174,10 +174,7 @@ function showUpdateHistory() {
|
|||
BG.chromeLocal.getValue('updateLog').then((lines = []) => {
|
||||
messageBox({
|
||||
title: t('updateCheckHistory'),
|
||||
contents: $element({
|
||||
className: 'update-history-log',
|
||||
textContent: lines.join('\n'),
|
||||
}),
|
||||
contents: $create('.update-history-log', lines.join('\n')),
|
||||
buttons: [t('confirmOK')],
|
||||
onshow: () => ($('#message-box-contents').scrollTop = 1e9),
|
||||
});
|
||||
|
|
|
@ -58,27 +58,25 @@ function messageBox({
|
|||
removeSelf();
|
||||
}
|
||||
const id = 'message-box';
|
||||
messageBox.element = $element({id, className, appendChild: [
|
||||
$element({appendChild: [
|
||||
$element({id: `${id}-title`, textContent: title}),
|
||||
$element({id: `${id}-close-icon`, appendChild:
|
||||
$element({tag: 'SVG#svg', class: 'svg-icon', viewBox: '0 0 20 20', appendChild:
|
||||
$element({tag: 'SVG#path', d: 'M11.69,10l4.55,4.55-1.69,1.69L10,11.69,' +
|
||||
'5.45,16.23,3.77,14.55,8.31,10,3.77,5.45,5.45,3.77,10,8.31l4.55-4.55,1.69,1.69Z',
|
||||
})
|
||||
}),
|
||||
onclick: messageBox.listeners.closeIcon}),
|
||||
$element({id: `${id}-contents`, appendChild: tHTML(contents)}),
|
||||
$element({id: `${id}-buttons`, appendChild:
|
||||
buttons.map((content, buttonIndex) => content && $element({
|
||||
tag: 'button',
|
||||
buttonIndex,
|
||||
textContent: content.textContent || content,
|
||||
onclick: content.onclick || messageBox.listeners.button,
|
||||
}))
|
||||
}),
|
||||
]}),
|
||||
]});
|
||||
messageBox.element =
|
||||
$create({id, className}, [
|
||||
$create([
|
||||
$create(`#${id}-title`, title),
|
||||
$create(`#${id}-close-icon`, {onclick: messageBox.listeners.closeIcon},
|
||||
$create('SVG:svg.svg-icon', {viewBox: '0 0 20 20'},
|
||||
$create('SVG:path', {d: 'M11.69,10l4.55,4.55-1.69,1.69L10,11.69,' +
|
||||
'5.45,16.23,3.77,14.55,8.31,10,3.77,5.45,5.45,3.77,10,8.31l4.55-4.55,1.69,1.69Z',
|
||||
}))),
|
||||
$create(`#${id}-contents`, tHTML(contents)),
|
||||
$create(`#${id}-buttons`,
|
||||
buttons.map((content, buttonIndex) => content &&
|
||||
$create('button', {
|
||||
buttonIndex,
|
||||
textContent: content.textContent || content,
|
||||
onclick: content.onclick || messageBox.listeners.button,
|
||||
}))),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
||||
function bindGlobalListeners() {
|
||||
|
|
|
@ -142,13 +142,13 @@ window.addEventListener('showStyles:done', function _() {
|
|||
line
|
||||
.split(/(<.*?>)/)
|
||||
.map(s => (!s.startsWith('<') ? s :
|
||||
$element({tag: 'mark', textContent: s.slice(1, -1)})));
|
||||
$create('mark', s.slice(1, -1))));
|
||||
const linesToElements = text =>
|
||||
text
|
||||
.trim()
|
||||
.split('\n')
|
||||
.map((line, i, array) =>
|
||||
$element(i < array.length - 1 ? {
|
||||
$create(i < array.length - 1 ? {
|
||||
tag: 'p',
|
||||
appendChild: keysToElements(line),
|
||||
} : {
|
||||
|
@ -159,9 +159,9 @@ window.addEventListener('showStyles:done', function _() {
|
|||
}));
|
||||
[
|
||||
linesToElements(t('popupHotkeysInfo')),
|
||||
$element({tag: 'button', textContent: t('confirmOK')}),
|
||||
$create('button', t('confirmOK')),
|
||||
].forEach(child => {
|
||||
container.appendChild($element({appendChild: child}));
|
||||
container.appendChild($create('div', child));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user