WIP: edit page
This commit is contained in:
parent
fd9ab5d6e5
commit
ba6159e067
|
@ -1,7 +1,6 @@
|
||||||
/*
|
/*
|
||||||
global CodeMirror loadScript
|
global CodeMirror loadScript
|
||||||
global editor ownTabId
|
global editor ownTabId
|
||||||
global save toggleStyle makeSectionVisible
|
|
||||||
global messageBox
|
global messageBox
|
||||||
*/
|
*/
|
||||||
'use strict';
|
'use strict';
|
||||||
|
@ -37,6 +36,8 @@ onDOMscriptReady('/codemirror.js').then(() => {
|
||||||
getSection,
|
getSection,
|
||||||
rerouteHotkeys,
|
rerouteHotkeys,
|
||||||
});
|
});
|
||||||
|
Object.assign(CodeMirror.commands, COMMANDS);
|
||||||
|
rerouteHotkeys(true);
|
||||||
|
|
||||||
CodeMirror.defineInitHook(cm => {
|
CodeMirror.defineInitHook(cm => {
|
||||||
if (!cm.display.wrapper.closest('#sections')) {
|
if (!cm.display.wrapper.closest('#sections')) {
|
||||||
|
@ -58,27 +59,10 @@ onDOMscriptReady('/codemirror.js').then(() => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
new MutationObserver((mutations, observer) => {
|
// FIXME: pull this into a module
|
||||||
if (!$('#sections')) {
|
window.rerouteHotkeys = rerouteHotkeys;
|
||||||
return;
|
|
||||||
}
|
|
||||||
observer.disconnect();
|
|
||||||
|
|
||||||
prefs.subscribe(['editor.keyMap'], showHotkeyInTooltip);
|
prefs.subscribe(null, onPrefChanged);
|
||||||
addEventListener('showHotkeyInTooltip', showHotkeyInTooltip);
|
|
||||||
showHotkeyInTooltip();
|
|
||||||
|
|
||||||
// N.B. the onchange event listeners should be registered before setupLivePrefs()
|
|
||||||
$('#options').addEventListener('change', onOptionElementChanged);
|
|
||||||
buildThemeElement();
|
|
||||||
buildKeymapElement();
|
|
||||||
setupLivePrefs();
|
|
||||||
|
|
||||||
Object.assign(CodeMirror.commands, COMMANDS);
|
|
||||||
rerouteHotkeys(true);
|
|
||||||
}).observe(document, {childList: true, subtree: true});
|
|
||||||
|
|
||||||
return;
|
|
||||||
|
|
||||||
////////////////////////////////////////////////
|
////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@ -88,6 +72,9 @@ onDOMscriptReady('/codemirror.js').then(() => {
|
||||||
|
|
||||||
function setOption(o, v) {
|
function setOption(o, v) {
|
||||||
CodeMirror.defaults[o] = v;
|
CodeMirror.defaults[o] = v;
|
||||||
|
if (!editor) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const editors = editor.getEditors();
|
const editors = editor.getEditors();
|
||||||
if (editors.length > 4 && (o === 'theme' || o === 'lineWrapping')) {
|
if (editors.length > 4 && (o === 'theme' || o === 'lineWrapping')) {
|
||||||
throttleSetOption({key: o, value: v, index: 0});
|
throttleSetOption({key: o, value: v, index: 0});
|
||||||
|
@ -178,19 +165,11 @@ onDOMscriptReady('/codemirror.js').then(() => {
|
||||||
}
|
}
|
||||||
|
|
||||||
function nextEditor(cm) {
|
function nextEditor(cm) {
|
||||||
return nextPrevEditor(cm, 1);
|
return editor.nextEditor(cm);
|
||||||
}
|
}
|
||||||
|
|
||||||
function prevEditor(cm) {
|
function prevEditor(cm) {
|
||||||
return nextPrevEditor(cm, -1);
|
return editor.prevEditor(cm);
|
||||||
}
|
|
||||||
|
|
||||||
function nextPrevEditor(cm, direction) {
|
|
||||||
const editors = editor.getEditors();
|
|
||||||
cm = editors[(editors.indexOf(cm) + direction + editors.length) % editors.length];
|
|
||||||
editor.scrollToEditor(cm);
|
|
||||||
cm.focus();
|
|
||||||
return cm;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function jumpToLine(cm) {
|
function jumpToLine(cm) {
|
||||||
|
@ -230,14 +209,12 @@ onDOMscriptReady('/codemirror.js').then(() => {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function onOptionElementChanged(event) {
|
function onPrefChanged(key, value) {
|
||||||
const el = event.target;
|
let option = key.replace(/^editor\./, '');
|
||||||
let option = el.id.replace(/^editor\./, '');
|
|
||||||
if (!option) {
|
if (!option) {
|
||||||
console.error('no "cm_option"', el);
|
console.error('no "cm_option"', key);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let value = el.type === 'checkbox' ? el.checked : el.value;
|
|
||||||
switch (option) {
|
switch (option) {
|
||||||
case 'tabSize':
|
case 'tabSize':
|
||||||
value = Number(value);
|
value = Number(value);
|
||||||
|
@ -255,11 +232,11 @@ onDOMscriptReady('/codemirror.js').then(() => {
|
||||||
// use non-localized 'default' internally
|
// use non-localized 'default' internally
|
||||||
if (!value || value === 'default' || value === t('defaultTheme')) {
|
if (!value || value === 'default' || value === t('defaultTheme')) {
|
||||||
value = 'default';
|
value = 'default';
|
||||||
if (prefs.get(el.id) !== value) {
|
if (prefs.get(key) !== value) {
|
||||||
prefs.set(el.id, value);
|
prefs.set(key, value);
|
||||||
}
|
}
|
||||||
themeLink.href = '';
|
themeLink.href = '';
|
||||||
el.selectedIndex = 0;
|
$('#editor.theme').value = value;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
const url = chrome.runtime.getURL('vendor/codemirror/theme/' + value + '.css');
|
const url = chrome.runtime.getURL('vendor/codemirror/theme/' + value + '.css');
|
||||||
|
@ -278,7 +255,9 @@ onDOMscriptReady('/codemirror.js').then(() => {
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'autocompleteOnTyping':
|
case 'autocompleteOnTyping':
|
||||||
editor.getEditors().forEach(cm => setupAutocomplete(cm, el.checked));
|
if (editor) {
|
||||||
|
editor.getEditors().forEach(cm => setupAutocomplete(cm, value));
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case 'autoCloseBrackets':
|
case 'autoCloseBrackets':
|
||||||
|
@ -306,62 +285,6 @@ onDOMscriptReady('/codemirror.js').then(() => {
|
||||||
CodeMirror.setOption(option, value);
|
CodeMirror.setOption(option, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildThemeElement() {
|
|
||||||
const themeElement = $('#editor.theme');
|
|
||||||
const themeList = localStorage.codeMirrorThemes;
|
|
||||||
|
|
||||||
const optionsFromArray = options => {
|
|
||||||
const fragment = document.createDocumentFragment();
|
|
||||||
options.forEach(opt => fragment.appendChild($create('option', opt)));
|
|
||||||
themeElement.appendChild(fragment);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (themeList) {
|
|
||||||
optionsFromArray(themeList.split(/\s+/));
|
|
||||||
} else {
|
|
||||||
// Chrome is starting up and shows our edit.html, but the background page isn't loaded yet
|
|
||||||
const theme = prefs.get('editor.theme');
|
|
||||||
optionsFromArray([theme === 'default' ? t('defaultTheme') : theme]);
|
|
||||||
getCodeMirrorThemes().then(() => {
|
|
||||||
const themes = (localStorage.codeMirrorThemes || '').split(/\s+/);
|
|
||||||
optionsFromArray(themes);
|
|
||||||
themeElement.selectedIndex = Math.max(0, themes.indexOf(theme));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildKeymapElement() {
|
|
||||||
// move 'pc' or 'mac' prefix to the end of the displayed label
|
|
||||||
const maps = Object.keys(CodeMirror.keyMap)
|
|
||||||
.map(name => ({
|
|
||||||
value: name,
|
|
||||||
name: name.replace(/^(pc|mac)(.+)/, (s, arch, baseName) =>
|
|
||||||
baseName.toLowerCase() + '-' + (arch === 'mac' ? 'Mac' : 'PC')),
|
|
||||||
}))
|
|
||||||
.sort((a, b) => a.name < b.name && -1 || a.name > b.name && 1);
|
|
||||||
|
|
||||||
const fragment = document.createDocumentFragment();
|
|
||||||
let bin = fragment;
|
|
||||||
let groupName;
|
|
||||||
// group suffixed maps in <optgroup>
|
|
||||||
maps.forEach(({value, name}, i) => {
|
|
||||||
groupName = !name.includes('-') ? name : groupName;
|
|
||||||
const groupWithNext = maps[i + 1] && maps[i + 1].name.startsWith(groupName);
|
|
||||||
if (groupWithNext) {
|
|
||||||
if (bin === fragment) {
|
|
||||||
bin = fragment.appendChild($create('optgroup', {label: name.split('-')[0]}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const el = bin.appendChild($create('option', {value}, name));
|
|
||||||
if (value === prefs.defaults['editor.keyMap']) {
|
|
||||||
el.dataset.default = '';
|
|
||||||
el.title = t('defaultTheme');
|
|
||||||
}
|
|
||||||
if (!groupWithNext) bin = fragment;
|
|
||||||
});
|
|
||||||
$('#editor.keyMap').appendChild(fragment);
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////
|
////////////////////////////////////////////////
|
||||||
|
|
||||||
function rerouteHotkeys(enable, immediately) {
|
function rerouteHotkeys(enable, immediately) {
|
||||||
|
@ -477,121 +400,6 @@ onDOMscriptReady('/codemirror.js').then(() => {
|
||||||
|
|
||||||
////////////////////////////////////////////////
|
////////////////////////////////////////////////
|
||||||
|
|
||||||
function getCodeMirrorThemes() {
|
|
||||||
if (!chrome.runtime.getPackageDirectoryEntry) {
|
|
||||||
const themes = [
|
|
||||||
chrome.i18n.getMessage('defaultTheme'),
|
|
||||||
/* populate-theme-start */
|
|
||||||
'3024-day',
|
|
||||||
'3024-night',
|
|
||||||
'abcdef',
|
|
||||||
'ambiance',
|
|
||||||
'ambiance-mobile',
|
|
||||||
'base16-dark',
|
|
||||||
'base16-light',
|
|
||||||
'bespin',
|
|
||||||
'blackboard',
|
|
||||||
'cobalt',
|
|
||||||
'colorforth',
|
|
||||||
'darcula',
|
|
||||||
'dracula',
|
|
||||||
'duotone-dark',
|
|
||||||
'duotone-light',
|
|
||||||
'eclipse',
|
|
||||||
'elegant',
|
|
||||||
'erlang-dark',
|
|
||||||
'gruvbox-dark',
|
|
||||||
'hopscotch',
|
|
||||||
'icecoder',
|
|
||||||
'idea',
|
|
||||||
'isotope',
|
|
||||||
'lesser-dark',
|
|
||||||
'liquibyte',
|
|
||||||
'lucario',
|
|
||||||
'material',
|
|
||||||
'mbo',
|
|
||||||
'mdn-like',
|
|
||||||
'midnight',
|
|
||||||
'monokai',
|
|
||||||
'neat',
|
|
||||||
'neo',
|
|
||||||
'night',
|
|
||||||
'oceanic-next',
|
|
||||||
'panda-syntax',
|
|
||||||
'paraiso-dark',
|
|
||||||
'paraiso-light',
|
|
||||||
'pastel-on-dark',
|
|
||||||
'railscasts',
|
|
||||||
'rubyblue',
|
|
||||||
'seti',
|
|
||||||
'shadowfox',
|
|
||||||
'solarized',
|
|
||||||
'ssms',
|
|
||||||
'the-matrix',
|
|
||||||
'tomorrow-night-bright',
|
|
||||||
'tomorrow-night-eighties',
|
|
||||||
'ttcn',
|
|
||||||
'twilight',
|
|
||||||
'vibrant-ink',
|
|
||||||
'xq-dark',
|
|
||||||
'xq-light',
|
|
||||||
'yeti',
|
|
||||||
'zenburn',
|
|
||||||
/* populate-theme-end */
|
|
||||||
];
|
|
||||||
localStorage.codeMirrorThemes = themes.join(' ');
|
|
||||||
return Promise.resolve(themes);
|
|
||||||
}
|
|
||||||
return new Promise(resolve => {
|
|
||||||
chrome.runtime.getPackageDirectoryEntry(rootDir => {
|
|
||||||
rootDir.getDirectory('vendor/codemirror/theme', {create: false}, themeDir => {
|
|
||||||
themeDir.createReader().readEntries(entries => {
|
|
||||||
const themes = [
|
|
||||||
chrome.i18n.getMessage('defaultTheme')
|
|
||||||
].concat(
|
|
||||||
entries.filter(entry => entry.isFile)
|
|
||||||
.sort((a, b) => (a.name < b.name ? -1 : 1))
|
|
||||||
.map(entry => entry.name.replace(/\.css$/, ''))
|
|
||||||
);
|
|
||||||
localStorage.codeMirrorThemes = themes.join(' ');
|
|
||||||
resolve(themes);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function showHotkeyInTooltip(_, mapName = prefs.get('editor.keyMap')) {
|
|
||||||
const extraKeys = CodeMirror.defaults.extraKeys;
|
|
||||||
for (const el of $$('[data-hotkey-tooltip]')) {
|
|
||||||
if (el._hotkeyTooltipKeyMap !== mapName) {
|
|
||||||
el._hotkeyTooltipKeyMap = mapName;
|
|
||||||
const title = el._hotkeyTooltipTitle = el._hotkeyTooltipTitle || el.title;
|
|
||||||
const cmd = el.dataset.hotkeyTooltip;
|
|
||||||
const key = cmd[0] === '=' ? cmd.slice(1) :
|
|
||||||
findKeyForCommand(cmd, mapName) ||
|
|
||||||
extraKeys && findKeyForCommand(cmd, extraKeys);
|
|
||||||
const newTitle = title + (title && key ? '\n' : '') + (key || '');
|
|
||||||
if (el.title !== newTitle) el.title = newTitle;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function findKeyForCommand(command, map) {
|
|
||||||
if (typeof map === 'string') map = CodeMirror.keyMap[map];
|
|
||||||
let key = Object.keys(map).find(k => map[k] === command);
|
|
||||||
if (key) {
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
for (const ft of Array.isArray(map.fallthrough) ? map.fallthrough : [map.fallthrough]) {
|
|
||||||
key = ft && findKeyForCommand(command, ft);
|
|
||||||
if (key) {
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
function setupAutocomplete(cm, enable = true) {
|
function setupAutocomplete(cm, enable = true) {
|
||||||
const onOff = enable ? 'on' : 'off';
|
const onOff = enable ? 'on' : 'off';
|
||||||
cm[onOff]('changes', autocompleteOnTyping);
|
cm[onOff]('changes', autocompleteOnTyping);
|
||||||
|
@ -627,4 +435,12 @@ onDOMscriptReady('/codemirror.js').then(() => {
|
||||||
function autocompletePicked(cm) {
|
function autocompletePicked(cm) {
|
||||||
cm.state.autocompletePicked = true;
|
cm.state.autocompletePicked = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function save() {
|
||||||
|
editor.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleStyle() {
|
||||||
|
editor.toggleStyle();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
221
edit/edit.js
221
edit/edit.js
|
@ -29,24 +29,215 @@ msg.onExtension(onRuntimeMessage);
|
||||||
|
|
||||||
preinit();
|
preinit();
|
||||||
|
|
||||||
Promise.all([
|
(() => {
|
||||||
initStyleData(),
|
onDOMready().then(() => {
|
||||||
onDOMready(),
|
prefs.subscribe(['editor.keyMap'], showHotkeyInTooltip);
|
||||||
])
|
addEventListener('showHotkeyInTooltip', showHotkeyInTooltip);
|
||||||
.then(([style]) => {
|
showHotkeyInTooltip();
|
||||||
const usercss = isUsercss(style);
|
|
||||||
$('#heading').textContent = t(style.id ? 'editStyleHeading' : 'addStyleTitle');
|
|
||||||
$('#name').placeholder = t(usercss ? 'usercssEditorNamePlaceholder' : 'styleMissingName');
|
|
||||||
$('#name').title = usercss ? t('usercssReplaceTemplateName') : '';
|
|
||||||
|
|
||||||
$('#preview-label').classList.toggle('hidden', !style.id);
|
buildThemeElement();
|
||||||
|
buildKeymapElement();
|
||||||
|
|
||||||
$('#beautify').onclick = () => beautify(editor.getEditors());
|
setupLivePrefs();
|
||||||
$('#lint').addEventListener('scroll', hideLintHeaderOnScroll, {passive: true});
|
});
|
||||||
window.addEventListener('resize', () => debounce(rememberWindowSize, 100));
|
|
||||||
|
|
||||||
editor = usercss ? createSourceEditor(style) : createSectionEditor(style);
|
initEditor();
|
||||||
});
|
|
||||||
|
function getCodeMirrorThemes() {
|
||||||
|
if (!chrome.runtime.getPackageDirectoryEntry) {
|
||||||
|
const themes = [
|
||||||
|
chrome.i18n.getMessage('defaultTheme'),
|
||||||
|
/* populate-theme-start */
|
||||||
|
'3024-day',
|
||||||
|
'3024-night',
|
||||||
|
'abcdef',
|
||||||
|
'ambiance',
|
||||||
|
'ambiance-mobile',
|
||||||
|
'base16-dark',
|
||||||
|
'base16-light',
|
||||||
|
'bespin',
|
||||||
|
'blackboard',
|
||||||
|
'cobalt',
|
||||||
|
'colorforth',
|
||||||
|
'darcula',
|
||||||
|
'dracula',
|
||||||
|
'duotone-dark',
|
||||||
|
'duotone-light',
|
||||||
|
'eclipse',
|
||||||
|
'elegant',
|
||||||
|
'erlang-dark',
|
||||||
|
'gruvbox-dark',
|
||||||
|
'hopscotch',
|
||||||
|
'icecoder',
|
||||||
|
'idea',
|
||||||
|
'isotope',
|
||||||
|
'lesser-dark',
|
||||||
|
'liquibyte',
|
||||||
|
'lucario',
|
||||||
|
'material',
|
||||||
|
'mbo',
|
||||||
|
'mdn-like',
|
||||||
|
'midnight',
|
||||||
|
'monokai',
|
||||||
|
'neat',
|
||||||
|
'neo',
|
||||||
|
'night',
|
||||||
|
'oceanic-next',
|
||||||
|
'panda-syntax',
|
||||||
|
'paraiso-dark',
|
||||||
|
'paraiso-light',
|
||||||
|
'pastel-on-dark',
|
||||||
|
'railscasts',
|
||||||
|
'rubyblue',
|
||||||
|
'seti',
|
||||||
|
'shadowfox',
|
||||||
|
'solarized',
|
||||||
|
'ssms',
|
||||||
|
'the-matrix',
|
||||||
|
'tomorrow-night-bright',
|
||||||
|
'tomorrow-night-eighties',
|
||||||
|
'ttcn',
|
||||||
|
'twilight',
|
||||||
|
'vibrant-ink',
|
||||||
|
'xq-dark',
|
||||||
|
'xq-light',
|
||||||
|
'yeti',
|
||||||
|
'zenburn',
|
||||||
|
/* populate-theme-end */
|
||||||
|
];
|
||||||
|
localStorage.codeMirrorThemes = themes.join(' ');
|
||||||
|
return Promise.resolve(themes);
|
||||||
|
}
|
||||||
|
return new Promise(resolve => {
|
||||||
|
chrome.runtime.getPackageDirectoryEntry(rootDir => {
|
||||||
|
rootDir.getDirectory('vendor/codemirror/theme', {create: false}, themeDir => {
|
||||||
|
themeDir.createReader().readEntries(entries => {
|
||||||
|
const themes = [
|
||||||
|
chrome.i18n.getMessage('defaultTheme')
|
||||||
|
].concat(
|
||||||
|
entries.filter(entry => entry.isFile)
|
||||||
|
.sort((a, b) => (a.name < b.name ? -1 : 1))
|
||||||
|
.map(entry => entry.name.replace(/\.css$/, ''))
|
||||||
|
);
|
||||||
|
localStorage.codeMirrorThemes = themes.join(' ');
|
||||||
|
resolve(themes);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function findKeyForCommand(command, map) {
|
||||||
|
if (typeof map === 'string') map = CodeMirror.keyMap[map];
|
||||||
|
let key = Object.keys(map).find(k => map[k] === command);
|
||||||
|
if (key) {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
for (const ft of Array.isArray(map.fallthrough) ? map.fallthrough : [map.fallthrough]) {
|
||||||
|
key = ft && findKeyForCommand(command, ft);
|
||||||
|
if (key) {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildThemeElement() {
|
||||||
|
const themeElement = $('#editor.theme');
|
||||||
|
const themeList = localStorage.codeMirrorThemes;
|
||||||
|
|
||||||
|
const optionsFromArray = options => {
|
||||||
|
const fragment = document.createDocumentFragment();
|
||||||
|
options.forEach(opt => fragment.appendChild($create('option', opt)));
|
||||||
|
themeElement.appendChild(fragment);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (themeList) {
|
||||||
|
optionsFromArray(themeList.split(/\s+/));
|
||||||
|
} else {
|
||||||
|
// Chrome is starting up and shows our edit.html, but the background page isn't loaded yet
|
||||||
|
const theme = prefs.get('editor.theme');
|
||||||
|
optionsFromArray([theme === 'default' ? t('defaultTheme') : theme]);
|
||||||
|
getCodeMirrorThemes().then(() => {
|
||||||
|
const themes = (localStorage.codeMirrorThemes || '').split(/\s+/);
|
||||||
|
optionsFromArray(themes);
|
||||||
|
themeElement.selectedIndex = Math.max(0, themes.indexOf(theme));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildKeymapElement() {
|
||||||
|
// move 'pc' or 'mac' prefix to the end of the displayed label
|
||||||
|
const maps = Object.keys(CodeMirror.keyMap)
|
||||||
|
.map(name => ({
|
||||||
|
value: name,
|
||||||
|
name: name.replace(/^(pc|mac)(.+)/, (s, arch, baseName) =>
|
||||||
|
baseName.toLowerCase() + '-' + (arch === 'mac' ? 'Mac' : 'PC')),
|
||||||
|
}))
|
||||||
|
.sort((a, b) => a.name < b.name && -1 || a.name > b.name && 1);
|
||||||
|
|
||||||
|
const fragment = document.createDocumentFragment();
|
||||||
|
let bin = fragment;
|
||||||
|
let groupName;
|
||||||
|
// group suffixed maps in <optgroup>
|
||||||
|
maps.forEach(({value, name}, i) => {
|
||||||
|
groupName = !name.includes('-') ? name : groupName;
|
||||||
|
const groupWithNext = maps[i + 1] && maps[i + 1].name.startsWith(groupName);
|
||||||
|
if (groupWithNext) {
|
||||||
|
if (bin === fragment) {
|
||||||
|
bin = fragment.appendChild($create('optgroup', {label: name.split('-')[0]}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const el = bin.appendChild($create('option', {value}, name));
|
||||||
|
if (value === prefs.defaults['editor.keyMap']) {
|
||||||
|
el.dataset.default = '';
|
||||||
|
el.title = t('defaultTheme');
|
||||||
|
}
|
||||||
|
if (!groupWithNext) bin = fragment;
|
||||||
|
});
|
||||||
|
$('#editor.keyMap').appendChild(fragment);
|
||||||
|
}
|
||||||
|
|
||||||
|
function showHotkeyInTooltip(_, mapName = prefs.get('editor.keyMap')) {
|
||||||
|
const extraKeys = CodeMirror.defaults.extraKeys;
|
||||||
|
for (const el of $$('[data-hotkey-tooltip]')) {
|
||||||
|
if (el._hotkeyTooltipKeyMap !== mapName) {
|
||||||
|
el._hotkeyTooltipKeyMap = mapName;
|
||||||
|
const title = el._hotkeyTooltipTitle = el._hotkeyTooltipTitle || el.title;
|
||||||
|
const cmd = el.dataset.hotkeyTooltip;
|
||||||
|
const key = cmd[0] === '=' ? cmd.slice(1) :
|
||||||
|
findKeyForCommand(cmd, mapName) ||
|
||||||
|
extraKeys && findKeyForCommand(cmd, extraKeys);
|
||||||
|
const newTitle = title + (title && key ? '\n' : '') + (key || '');
|
||||||
|
if (el.title !== newTitle) el.title = newTitle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function initEditor() {
|
||||||
|
return Promise.all([
|
||||||
|
initStyleData(),
|
||||||
|
onDOMready(),
|
||||||
|
])
|
||||||
|
.then(([style]) => {
|
||||||
|
const usercss = isUsercss(style);
|
||||||
|
$('#heading').textContent = t(style.id ? 'editStyleHeading' : 'addStyleTitle');
|
||||||
|
$('#name').placeholder = t(usercss ? 'usercssEditorNamePlaceholder' : 'styleMissingName');
|
||||||
|
$('#name').title = usercss ? t('usercssReplaceTemplateName') : '';
|
||||||
|
|
||||||
|
$('#preview-label').classList.toggle('hidden', !style.id);
|
||||||
|
|
||||||
|
$('#beautify').onclick = () => beautify(editor.getEditors());
|
||||||
|
$('#lint').addEventListener('scroll', hideLintHeaderOnScroll, {passive: true});
|
||||||
|
window.addEventListener('resize', () => debounce(rememberWindowSize, 100));
|
||||||
|
|
||||||
|
editor = usercss ? createSourceEditor(style) : createSectionEditor(style);
|
||||||
|
if (editor.ready) {
|
||||||
|
return editor.ready();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
function preinit() {
|
function preinit() {
|
||||||
// make querySelectorAll enumeration code readable
|
// make querySelectorAll enumeration code readable
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
CodeMirror nextPrevEditorOnKeydown showAppliesToHelp propertyToCss
|
CodeMirror nextPrevEditorOnKeydown showAppliesToHelp propertyToCss
|
||||||
regExpTester linter cssToProperty createLivePreview showCodeMirrorPopup
|
regExpTester linter cssToProperty createLivePreview showCodeMirrorPopup
|
||||||
sectionsToMozFormat editorWorker messageBox clipString beautify
|
sectionsToMozFormat editorWorker messageBox clipString beautify
|
||||||
|
rerouteHotkeys
|
||||||
*/
|
*/
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
@ -110,15 +111,16 @@ function createSectionsEditor(style) {
|
||||||
}
|
}
|
||||||
|
|
||||||
let sectionOrder = '';
|
let sectionOrder = '';
|
||||||
initSection({
|
const initializing = new Promise(resolve => initSection({
|
||||||
sections: style.sections.slice(),
|
sections: style.sections.slice(),
|
||||||
done:() => {
|
done:() => {
|
||||||
// FIXME: implement this with CSS?
|
// FIXME: implement this with CSS?
|
||||||
// https://github.com/openstyles/stylus/commit/2895ce11e271788df0e4f7314b3b981fde086574
|
// https://github.com/openstyles/stylus/commit/2895ce11e271788df0e4f7314b3b981fde086574
|
||||||
// maximizeCodeHeight(sections[sections.length - 1], true);
|
|
||||||
dirty.clear();
|
dirty.clear();
|
||||||
|
rerouteHotkeys(true);
|
||||||
|
resolve();
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
|
|
||||||
const livePreview = createLivePreview();
|
const livePreview = createLivePreview();
|
||||||
livePreview.show(Boolean(style.id));
|
livePreview.show(Boolean(style.id));
|
||||||
|
@ -126,20 +128,51 @@ function createSectionsEditor(style) {
|
||||||
updateHeader();
|
updateHeader();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
ready: () => initializing,
|
||||||
replaceStyle,
|
replaceStyle,
|
||||||
isDirty: dirty.isDirty,
|
isDirty: dirty.isDirty,
|
||||||
getStyle: () => style,
|
getStyle: () => style,
|
||||||
getEditors: () =>
|
getEditors,
|
||||||
sections.filter(s => !s.isRemoved()).map(s => s.cm),
|
|
||||||
getLastActivatedEditor,
|
getLastActivatedEditor,
|
||||||
scrollToEditor,
|
scrollToEditor,
|
||||||
getStyleId: () => style.id,
|
getStyleId: () => style.id,
|
||||||
getEditorTitle: cm => {
|
getEditorTitle: cm => {
|
||||||
const index = sections.filter(s => !s.isRemoved()).findIndex(s => s.cm === cm) + 1;
|
const index = sections.filter(s => !s.isRemoved()).findIndex(s => s.cm === cm) + 1;
|
||||||
return `${t('sectionCode')} ${index + 1}`;
|
return `${t('sectionCode')} ${index + 1}`;
|
||||||
}
|
},
|
||||||
|
save: saveStyle,
|
||||||
|
toggleStyle,
|
||||||
|
nextEditor,
|
||||||
|
prevEditor
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function getEditors() {
|
||||||
|
return sections.filter(s => !s.isRemoved()).map(s => s.cm);
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleStyle() {
|
||||||
|
const newValue = !style.enabled;
|
||||||
|
dirty.modify('enabled', style.enabled, newValue);
|
||||||
|
style.enabled = newValue;
|
||||||
|
enabledEl.checked = newValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
function nextEditor(cm) {
|
||||||
|
return nextPrevEditor(cm, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function prevEditor(cm) {
|
||||||
|
return nextPrevEditor(cm, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function nextPrevEditor(cm, direction) {
|
||||||
|
const editors = getEditors();
|
||||||
|
cm = editors[(editors.indexOf(cm) + direction + editors.length) % editors.length];
|
||||||
|
scrollToEditor(cm);
|
||||||
|
cm.focus();
|
||||||
|
return cm;
|
||||||
|
}
|
||||||
|
|
||||||
function scrollToEditor(cm) {
|
function scrollToEditor(cm) {
|
||||||
const section = sections.find(s => s.cm === cm);
|
const section = sections.find(s => s.cm === cm);
|
||||||
const bounds = section.getBoundingClientRect();
|
const bounds = section.getBoundingClientRect();
|
||||||
|
@ -188,7 +221,7 @@ function createSectionsEditor(style) {
|
||||||
}
|
}
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
cm = CodeMirror.commands.prevEditor(cm);
|
cm = prevEditor(cm);
|
||||||
cm.setCursor(cm.doc.size - 1, key === 37 ? 1e20 : ch);
|
cm.setCursor(cm.doc.size - 1, key === 37 ? 1e20 : ch);
|
||||||
break;
|
break;
|
||||||
case 39:
|
case 39:
|
||||||
|
@ -204,7 +237,7 @@ function createSectionsEditor(style) {
|
||||||
}
|
}
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
cm = CodeMirror.commands.nextEditor(cm);
|
cm = nextEditor(cm);
|
||||||
cm.setCursor(0, 0);
|
cm.setCursor(0, 0);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,10 +52,6 @@ function createSourceEditor(style) {
|
||||||
updateLivePreview();
|
updateLivePreview();
|
||||||
});
|
});
|
||||||
|
|
||||||
CodeMirror.commands.prevEditor = cm => nextPrevMozDocument(cm, -1);
|
|
||||||
CodeMirror.commands.nextEditor = cm => nextPrevMozDocument(cm, 1);
|
|
||||||
CodeMirror.commands.toggleStyle = toggleStyle;
|
|
||||||
CodeMirror.commands.save = save;
|
|
||||||
CodeMirror.closestVisible = () => cm;
|
CodeMirror.closestVisible = () => cm;
|
||||||
|
|
||||||
cm.operation(initAppliesToLineWidget);
|
cm.operation(initAppliesToLineWidget);
|
||||||
|
@ -409,6 +405,10 @@ function createSourceEditor(style) {
|
||||||
getLastActivatedEditor: () => cm,
|
getLastActivatedEditor: () => cm,
|
||||||
scrollToEditor: () => {},
|
scrollToEditor: () => {},
|
||||||
getStyleId: () => style.id,
|
getStyleId: () => style.id,
|
||||||
getEditorTitle: () => ''
|
getEditorTitle: () => '',
|
||||||
|
save,
|
||||||
|
toggleStyle,
|
||||||
|
prevEditor: cm => nextPrevMozDocument(cm, -1),
|
||||||
|
nextEditor: cm => nextPrevMozDocument(cm, 1)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user