Refactor: pull out sections editor section

This commit is contained in:
eight 2018-10-12 16:18:40 +08:00
parent 8016346035
commit 1a5a206fe6
4 changed files with 476 additions and 423 deletions

View File

@ -95,6 +95,7 @@
<script src="edit/refresh-on-view.js"></script> <script src="edit/refresh-on-view.js"></script>
<script src="edit/source-editor.js"></script> <script src="edit/source-editor.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/edit.js"></script> <script src="edit/edit.js"></script>

View File

@ -1,9 +1,9 @@
/* global CodeMirror onDOMready prefs setupLivePrefs $ $$ $create t tHTML /* global CodeMirror onDOMready prefs setupLivePrefs $ $$ $create t tHTML
createSourceEditor queryTabs sessionStorageHash getOwnTab FIREFOX API tryCatch createSourceEditor queryTabs sessionStorageHash getOwnTab FIREFOX API tryCatch
closeCurrentTab messageBox debounce workerUtil closeCurrentTab messageBox debounce workerUtil
beautify beautify ignoreChromeError
moveFocus msg createSectionsEditor rerouteHotkeys */ moveFocus msg createSectionsEditor rerouteHotkeys */
/* exported showCodeMirrorPopup editorWorker */ /* exported showCodeMirrorPopup editorWorker toggleContextMenuDelete */
'use strict'; 'use strict';
const editorWorker = workerUtil.createWorker({ const editorWorker = workerUtil.createWorker({
@ -557,3 +557,14 @@ function isWindowMaximized() {
window.outerHeight < screen.availHeight + 10 window.outerHeight < screen.availHeight + 10
); );
} }
function toggleContextMenuDelete(event) {
if (chrome.contextMenus && event.button === 2 && prefs.get('editor.contextDelete')) {
chrome.contextMenus.update('editor.contextDelete', {
enabled: Boolean(
this.selectionStart !== this.selectionEnd ||
this.somethingSelected && this.somethingSelected()
),
}, ignoreChromeError);
}
}

View File

@ -0,0 +1,416 @@
/* global template cmFactory $ propertyToCss CssToProperty linter regExpTester
FIREFOX toggleContextMenuDelete beautify showHelp t tryRegExp */
/* exported createSection */
'use strict';
function createResizeGrip(cm) {
const wrapper = cm.display.wrapper;
wrapper.classList.add('resize-grip-enabled');
const resizeGrip = template.resizeGrip.cloneNode(true);
wrapper.appendChild(resizeGrip);
let lastClickTime = 0;
resizeGrip.onmousedown = event => {
if (event.button !== 0) {
return;
}
event.preventDefault();
if (Date.now() - lastClickTime < 500) {
lastClickTime = 0;
toggleSectionHeight(cm);
return;
}
lastClickTime = Date.now();
const minHeight = cm.defaultTextHeight() +
/* .CodeMirror-lines padding */
cm.display.lineDiv.offsetParent.offsetTop +
/* borders */
wrapper.offsetHeight - wrapper.clientHeight;
wrapper.style.pointerEvents = 'none';
document.body.style.cursor = 's-resize';
document.addEventListener('mousemove', resize);
document.addEventListener('mouseup', resizeStop);
function resize(e) {
const cmPageY = wrapper.getBoundingClientRect().top + window.scrollY;
const height = Math.max(minHeight, e.pageY - cmPageY);
if (height !== wrapper.clientHeight) {
cm.setSize(null, height);
}
}
function resizeStop() {
document.removeEventListener('mouseup', resizeStop);
document.removeEventListener('mousemove', resize);
wrapper.style.pointerEvents = '';
document.body.style.cursor = '';
}
};
function toggleSectionHeight(cm) {
if (cm.state.toggleHeightSaved) {
// restore previous size
cm.setSize(null, cm.state.toggleHeightSaved);
cm.state.toggleHeightSaved = 0;
} else {
// maximize
const wrapper = cm.display.wrapper;
const allBounds = $('#sections').getBoundingClientRect();
const pageExtrasHeight = allBounds.top + window.scrollY +
parseFloat(getComputedStyle($('#sections')).paddingBottom);
const sectionEl = wrapper.parentNode;
const sectionExtrasHeight = sectionEl.clientHeight - wrapper.offsetHeight;
cm.state.toggleHeightSaved = wrapper.clientHeight;
cm.setSize(null, window.innerHeight - sectionExtrasHeight - pageExtrasHeight);
const bounds = sectionEl.getBoundingClientRect();
if (bounds.top < 0 || bounds.bottom > window.innerHeight) {
window.scrollBy(0, bounds.top);
}
}
}
}
function createSection({
originalSection,
genId,
dirty,
showMozillaFormatImport,
removeSection,
insertSectionAfter,
moveSectionUp,
moveSectionDown,
restoreSection,
nextEditor,
prevEditor
}) {
const sectionId = genId();
const el = template.section.cloneNode(true);
const cm = cmFactory.create(wrapper => {
el.insertBefore(wrapper, $('.code-label', el).nextSibling);
}, {value: originalSection.code});
const changeListeners = new Set();
const appliesToContainer = $('.applies-to-list', el);
const appliesTo = [];
for (const [key, fnName] of Object.entries(propertyToCss)) {
if (originalSection[key]) {
originalSection[key].forEach(value =>
insertApplyAfter({type: fnName, value})
);
}
}
if (!appliesTo.length) {
insertApplyAfter({all: true});
}
let changeGeneration = cm.changeGeneration();
let removed = false;
registerEvents();
updateRegexpTester();
createResizeGrip(cm);
linter.enableForEditor(cm);
let lastActive = 0;
const section = {
id: sectionId,
el,
cm,
render,
getCode,
getModel,
remove,
restore,
isRemoved: () => removed,
onChange,
off,
getLastActive: () => lastActive,
appliesTo
};
return section;
function onChange(fn) {
changeListeners.add(fn);
}
function off(fn) {
changeListeners.delete(fn);
}
function emitSectionChange() {
for (const fn of changeListeners) {
fn();
}
}
function getModel() {
const section = {
code: cm.getValue()
};
for (const apply of appliesTo) {
if (apply.all) {
continue;
}
const key = CssToProperty[apply.getType()];
if (!section[key]) {
section[key] = [];
}
section[key].push(apply.getValue());
}
return section;
}
function registerEvents() {
cm.on('changes', () => {
const newGeneration = cm.changeGeneration();
dirty.modify(`section.${sectionId}.code`, changeGeneration, newGeneration);
changeGeneration = newGeneration;
emitSectionChange();
});
cm.on('paste', (cm, event) => {
const text = event.clipboardData.getData('text') || '';
if (
text.includes('@-moz-document') &&
text.replace(/\/\*[\s\S]*?(?:\*\/|$)/g, '')
.match(/@-moz-document[\s\r\n]+(url|url-prefix|domain|regexp)\(/)
) {
event.preventDefault();
showMozillaFormatImport(text);
}
// FIXME: why?
// if (editors.length === 1) {
// setTimeout(() => {
// if (cm.display.sizer.clientHeight > cm.display.wrapper.clientHeight) {
// maximizeCodeHeight.stats = null;
// maximizeCodeHeight(cm.getSection(), true);
// }
// });
// }
});
if (!FIREFOX) {
cm.on('mousedown', (cm, event) => toggleContextMenuDelete.call(cm, event));
}
cm.on('focus', () => {
lastActive = Date.now();
});
cm.display.wrapper.addEventListener('keydown', event =>
handleKeydown(cm, event), true);
$('.applies-to-help', el).addEventListener('click', showAppliesToHelp);
$('.remove-section', el).addEventListener('click', () => removeSection(section));
$('.add-section', el).addEventListener('click', () => insertSectionAfter(undefined, section));
$('.clone-section', el).addEventListener('click', () => insertSectionAfter(getModel(), section));
$('.move-section-up', el).addEventListener('click', () => moveSectionUp(section));
$('.move-section-down', el).addEventListener('click', () => moveSectionDown(section));
$('.beautify-section', el).addEventListener('click', () => beautify([cm]));
$('.restore-section', el).addEventListener('click', () => restoreSection(section));
$('.test-regexp', el).addEventListener('click', () => {
regExpTester.toggle();
updateRegexpTester();
});
}
function handleKeydown(cm, event) {
const key = event.which;
if (key < 37 || key > 40 || event.shiftKey || event.altKey || event.metaKey) {
return;
}
const {line, ch} = cm.getCursor();
switch (key) {
case 37:
// arrow Left
if (line || ch) {
return;
}
// fallthrough to arrow Up
case 38:
// arrow Up
cm = line === 0 && prevEditor(cm, false);
if (!cm) {
return;
}
event.preventDefault();
event.stopPropagation();
cm.setCursor(cm.doc.size - 1, key === 37 ? 1e20 : ch);
break;
case 39:
// arrow Right
if (line < cm.doc.size - 1 || ch < cm.getLine(line).length - 1) {
return;
}
// fallthrough to arrow Down
case 40:
// arrow Down
cm = line === cm.doc.size - 1 && nextEditor(cm, false);
if (!cm) {
return;
}
event.preventDefault();
event.stopPropagation();
cm.setCursor(0, 0);
break;
}
// FIXME: what is this?
// const animation = (cm.getSection().firstElementChild.getAnimations() || [])[0];
// if (animation) {
// animation.playbackRate = -1;
// animation.currentTime = 2000;
// animation.play();
// }
}
function showAppliesToHelp(event) {
event.preventDefault();
showHelp(t('appliesLabel'), t('appliesHelp'));
}
function getCode() {
return cm.getValue();
}
function remove(destroy = false) {
linter.disableForEditor(cm);
el.classList.add('removed');
removed = true;
appliesTo.forEach(a => a.remove());
if (destroy) {
cmFactory.destroy(cm);
}
}
function restore() {
linter.enableForEditor(cm);
el.classList.remove('removed');
removed = false;
appliesTo.forEach(a => a.restore());
render();
}
function render() {
cm.refresh();
}
function updateRegexpTester() {
const regexps = appliesTo.filter(a => a.getType() === 'regexp')
.map(a => a.getValue());
if (regexps.length) {
el.classList.add('has-regexp');
regExpTester.update(regexps);
} else {
el.classList.remove('has-regexp');
regExpTester.toggle(false);
}
}
function insertApplyAfter(init, base) {
const apply = createApply(init);
if (base) {
const index = appliesTo.indexOf(base);
appliesTo.splice(index + 1, 0, apply);
appliesToContainer.insertBefore(apply.el, base.el.nextSibling);
} else {
appliesTo.push(apply);
appliesToContainer.appendChild(apply.el);
}
dirty.add(apply, apply);
if (appliesTo.length > 1 && appliesTo[0].all) {
removeApply(appliesTo[0]);
}
emitSectionChange();
}
function removeApply(apply) {
const index = appliesTo.indexOf(apply);
appliesTo.splice(index, 1);
apply.remove();
apply.el.remove();
dirty.remove(apply, apply);
if (!appliesTo.length) {
insertApplyAfter({all: true});
}
emitSectionChange();
}
function createApply({type = 'url', value, all = false}) {
const applyId = genId();
const dirtyPrefix = `section.${sectionId}.apply.${applyId}`;
const el = all ? template.appliesToEverything.cloneNode(true) :
template.appliesTo.cloneNode(true);
const selectEl = !all && $('.applies-type', el);
if (selectEl) {
selectEl.value = type;
selectEl.addEventListener('change', () => {
const oldKey = type;
dirty.modify(`${dirtyPrefix}.type`, type, selectEl.value);
type = selectEl.value;
if (oldKey === 'regexp' || type === 'regexp') {
updateRegexpTester();
}
emitSectionChange();
validate();
});
}
const valueEl = !all && $('.applies-value', el);
if (valueEl) {
valueEl.value = value;
valueEl.addEventListener('input', () => {
dirty.modify(`${dirtyPrefix}.value`, value, valueEl.value);
value = valueEl.value;
if (type === 'regexp') {
updateRegexpTester();
}
emitSectionChange();
});
valueEl.addEventListener('change', validate);
}
const apply = {
id: applyId,
all,
remove,
restore,
el,
getType: () => type,
getValue: () => value,
valueEl
};
const removeButton = $('.remove-applies-to', el);
if (removeButton) {
removeButton.addEventListener('click', e => {
e.preventDefault();
removeApply(apply);
});
}
$('.add-applies-to', el).addEventListener('click', e => {
e.preventDefault();
insertApplyAfter({type, value: ''}, apply);
});
return apply;
function validate() {
if (type !== 'regexp' || tryRegExp(value)) {
valueEl.setCustomValidity('');
} else {
valueEl.setCustomValidity(t('styleBadRegexp'));
setTimeout(() => valueEl.reportValidity());
}
}
function remove() {
dirty.remove(`${dirtyPrefix}.type`, type);
dirty.remove(`${dirtyPrefix}.value`, value);
}
function restore() {
dirty.add(`${dirtyPrefix}.type`, type);
dirty.add(`${dirtyPrefix}.value`, value);
}
}
}

View File

@ -1,79 +1,11 @@
/* global dirtyReporter showHelp prefs ignoreChromeError /* global dirtyReporter showHelp toggleContextMenuDelete createSection
CodeMirror propertyToCss CodeMirror linter createLivePreview showCodeMirrorPopup
regExpTester linter createLivePreview showCodeMirrorPopup sectionsToMozFormat messageBox clipString
sectionsToMozFormat messageBox clipString beautify rerouteHotkeys $ $$ $create t FIREFOX API
rerouteHotkeys cmFactory CssToProperty template $ $$ $create t FIREFOX API debounce */
debounce tryRegExp
*/
/* exported createSectionsEditor */ /* exported createSectionsEditor */
'use strict'; 'use strict';
function createResizeGrip(cm) {
const wrapper = cm.display.wrapper;
wrapper.classList.add('resize-grip-enabled');
const resizeGrip = template.resizeGrip.cloneNode(true);
wrapper.appendChild(resizeGrip);
let lastClickTime = 0;
resizeGrip.onmousedown = event => {
if (event.button !== 0) {
return;
}
event.preventDefault();
if (Date.now() - lastClickTime < 500) {
lastClickTime = 0;
toggleSectionHeight(cm);
return;
}
lastClickTime = Date.now();
const minHeight = cm.defaultTextHeight() +
/* .CodeMirror-lines padding */
cm.display.lineDiv.offsetParent.offsetTop +
/* borders */
wrapper.offsetHeight - wrapper.clientHeight;
wrapper.style.pointerEvents = 'none';
document.body.style.cursor = 's-resize';
document.addEventListener('mousemove', resize);
document.addEventListener('mouseup', resizeStop);
function resize(e) {
const cmPageY = wrapper.getBoundingClientRect().top + window.scrollY;
const height = Math.max(minHeight, e.pageY - cmPageY);
if (height !== wrapper.clientHeight) {
cm.setSize(null, height);
}
}
function resizeStop() {
document.removeEventListener('mouseup', resizeStop);
document.removeEventListener('mousemove', resize);
wrapper.style.pointerEvents = '';
document.body.style.cursor = '';
}
};
function toggleSectionHeight(cm) {
if (cm.state.toggleHeightSaved) {
// restore previous size
cm.setSize(null, cm.state.toggleHeightSaved);
cm.state.toggleHeightSaved = 0;
} else {
// maximize
const wrapper = cm.display.wrapper;
const allBounds = $('#sections').getBoundingClientRect();
const pageExtrasHeight = allBounds.top + window.scrollY +
parseFloat(getComputedStyle($('#sections')).paddingBottom);
const sectionEl = wrapper.parentNode;
const sectionExtrasHeight = sectionEl.clientHeight - wrapper.offsetHeight;
cm.state.toggleHeightSaved = wrapper.clientHeight;
cm.setSize(null, window.innerHeight - sectionExtrasHeight - pageExtrasHeight);
const bounds = sectionEl.getBoundingClientRect();
if (bounds.top < 0 || bounds.bottom > window.innerHeight) {
window.scrollBy(0, bounds.top);
}
}
}
}
function createSectionsEditor(style) { function createSectionsEditor(style) {
let INC_ID = 0; // an increment id that is used by various object to track the order let INC_ID = 0; // an increment id that is used by various object to track the order
const dirty = dirtyReporter(); const dirty = dirtyReporter();
@ -97,7 +29,7 @@ function createSectionsEditor(style) {
$('#to-mozilla').addEventListener('click', showMozillaFormat); $('#to-mozilla').addEventListener('click', showMozillaFormat);
$('#to-mozilla-help').addEventListener('click', showToMozillaHelp); $('#to-mozilla-help').addEventListener('click', showToMozillaHelp);
$('#from-mozilla').addEventListener('click', () => fromMozillaFormat()); $('#from-mozilla').addEventListener('click', () => showMozillaFormatImport());
$('#save-button').addEventListener('click', saveStyle); $('#save-button').addEventListener('click', saveStyle);
// FIXME: this element doesn't exist? // FIXME: this element doesn't exist?
$('#sections-help').addEventListener('click', showSectionHelp); $('#sections-help').addEventListener('click', showSectionHelp);
@ -150,15 +82,8 @@ function createSectionsEditor(style) {
getSearchableInputs, getSearchableInputs,
}; };
function toggleContextMenuDelete(event) { function genId() {
if (chrome.contextMenus && event.button === 2 && prefs.get('editor.contextDelete')) { return INC_ID++;
chrome.contextMenus.update('editor.contextDelete', {
enabled: Boolean(
this.selectionStart !== this.selectionEnd ||
this.somethingSelected && this.somethingSelected()
),
}, ignoreChromeError);
}
} }
function setGlobalProgress(done, total) { function setGlobalProgress(done, total) {
@ -180,11 +105,6 @@ function createSectionsEditor(style) {
showHelp(t('styleMozillaFormatHeading'), t('styleToMozillaFormatHelp')); showHelp(t('styleMozillaFormatHeading'), t('styleToMozillaFormatHelp'));
} }
function showAppliesToHelp(event) {
event.preventDefault();
showHelp(t('appliesLabel'), t('appliesHelp'));
}
function showSectionHelp(event) { function showSectionHelp(event) {
event.preventDefault(); event.preventDefault();
showHelp(t('styleSectionsTitle'), t('sectionHelp')); showHelp(t('styleSectionsTitle'), t('sectionHelp'));
@ -287,11 +207,33 @@ function createSectionsEditor(style) {
enabledEl.checked = newValue; enabledEl.checked = newValue;
} }
function nextEditor(cm) { function nextEditor(cm, cycle = true) {
if (!cycle) {
for (const section of sections) {
if (section.isRemoved()) {
continue;
}
if (cm === section.cm) {
return;
}
break;
}
}
return nextPrevEditor(cm, 1); return nextPrevEditor(cm, 1);
} }
function prevEditor(cm) { function prevEditor(cm, cycle = true) {
if (!cycle) {
for (let i = sections.length - 1; i >= 0; i--) {
if (sections[i].isRemoved()) {
continue;
}
if (cm === sections[i].cm) {
return;
}
break;
}
}
return nextPrevEditor(cm, -1); return nextPrevEditor(cm, -1);
} }
@ -332,55 +274,6 @@ function createSectionsEditor(style) {
return result; return result;
} }
function nextPrevEditorOnKeydown(cm, event) {
const key = event.which;
if (key < 37 || key > 40 || event.shiftKey || event.altKey || event.metaKey) {
return;
}
const {line, ch} = cm.getCursor();
switch (key) {
case 37:
// arrow Left
if (line || ch) {
return;
}
// fallthrough to arrow Up
case 38:
// arrow Up
if (line > 0 || cm === sections[0].cm) {
return;
}
event.preventDefault();
event.stopPropagation();
cm = prevEditor(cm);
cm.setCursor(cm.doc.size - 1, key === 37 ? 1e20 : ch);
break;
case 39:
// arrow Right
if (line < cm.doc.size - 1 || ch < cm.getLine(line).length - 1) {
return;
}
// fallthrough to arrow Down
case 40:
// arrow Down
if (line < cm.doc.size - 1 || cm === sections[sections.length - 1].cm) {
return;
}
event.preventDefault();
event.stopPropagation();
cm = nextEditor(cm);
cm.setCursor(0, 0);
break;
}
// FIXME: what is this?
// const animation = (cm.getSection().firstElementChild.getAnimations() || [])[0];
// if (animation) {
// animation.playbackRate = -1;
// animation.currentTime = 2000;
// animation.play();
// }
}
function scrollEntirePageOnCtrlShift(event) { function scrollEntirePageOnCtrlShift(event) {
// make Shift-Ctrl-Wheel scroll entire page even when mouse is over a code editor // make Shift-Ctrl-Wheel scroll entire page even when mouse is over a code editor
if (event.shiftKey && event.ctrlKey && !event.altKey && !event.metaKey) { if (event.shiftKey && event.ctrlKey && !event.altKey && !event.metaKey) {
@ -396,7 +289,7 @@ function createSectionsEditor(style) {
popup.codebox.execCommand('selectAll'); popup.codebox.execCommand('selectAll');
} }
function fromMozillaFormat(text = '') { function showMozillaFormatImport(text = '') {
const popup = showCodeMirrorPopup(t('styleFromMozillaFormatPrompt'), const popup = showCodeMirrorPopup(t('styleFromMozillaFormatPrompt'),
$create('.buttons', [ $create('.buttons', [
$create('button', { $create('button', {
@ -612,7 +505,19 @@ function createSectionsEditor(style) {
if (!init) { if (!init) {
init = {code: '', urlPrefixes: ['http://example.com']}; init = {code: '', urlPrefixes: ['http://example.com']};
} }
const section = createSection(init); const section = createSection({
originalSection: init,
genId,
dirty,
showMozillaFormatImport,
removeSection,
restoreSection,
insertSectionAfter,
moveSectionUp,
moveSectionDown,
prevEditor,
nextEditor
});
if (base) { if (base) {
const index = sections.indexOf(base); const index = sections.indexOf(base);
sections.splice(index + 1, 0, section); sections.splice(index + 1, 0, section);
@ -650,286 +555,6 @@ function createSectionsEditor(style) {
updateSectionOrder(); updateSectionOrder();
} }
function createSection(originalSection) {
const sectionId = INC_ID++;
const el = template.section.cloneNode(true);
const cm = cmFactory.create(wrapper => {
el.insertBefore(wrapper, $('.code-label', el).nextSibling);
}, {value: originalSection.code});
const changeListeners = new Set();
const appliesToContainer = $('.applies-to-list', el);
const appliesTo = [];
for (const [key, fnName] of Object.entries(propertyToCss)) {
if (originalSection[key]) {
originalSection[key].forEach(value =>
insertApplyAfter({type: fnName, value})
);
}
}
if (!appliesTo.length) {
insertApplyAfter({all: true});
}
let changeGeneration = cm.changeGeneration();
let removed = false;
registerEvents();
updateRegexpTester();
createResizeGrip(cm);
linter.enableForEditor(cm);
let lastActive = 0;
const section = {
id: sectionId,
el,
cm,
render,
getCode,
getModel,
remove,
restore,
isRemoved: () => removed,
onChange,
off,
getLastActive: () => lastActive,
appliesTo
};
return section;
function onChange(fn) {
changeListeners.add(fn);
}
function off(fn) {
changeListeners.delete(fn);
}
function emitSectionChange() {
for (const fn of changeListeners) {
fn();
}
}
function getModel() {
const section = {
code: cm.getValue()
};
for (const apply of appliesTo) {
if (apply.all) {
continue;
}
const key = CssToProperty[apply.getType()];
if (!section[key]) {
section[key] = [];
}
section[key].push(apply.getValue());
}
return section;
}
function registerEvents() {
cm.on('changes', () => {
const newGeneration = cm.changeGeneration();
dirty.modify(`section.${sectionId}.code`, changeGeneration, newGeneration);
changeGeneration = newGeneration;
emitSectionChange();
});
cm.on('paste', (cm, event) => {
const text = event.clipboardData.getData('text') || '';
if (
text.includes('@-moz-document') &&
text.replace(/\/\*[\s\S]*?(?:\*\/|$)/g, '')
.match(/@-moz-document[\s\r\n]+(url|url-prefix|domain|regexp)\(/)
) {
event.preventDefault();
fromMozillaFormat(text);
}
// FIXME: why?
// if (editors.length === 1) {
// setTimeout(() => {
// if (cm.display.sizer.clientHeight > cm.display.wrapper.clientHeight) {
// maximizeCodeHeight.stats = null;
// maximizeCodeHeight(cm.getSection(), true);
// }
// });
// }
});
if (!FIREFOX) {
cm.on('mousedown', (cm, event) => toggleContextMenuDelete.call(cm, event));
}
cm.on('focus', () => {
lastActive = Date.now();
});
cm.display.wrapper.addEventListener('keydown', event =>
nextPrevEditorOnKeydown(cm, event), true);
$('.applies-to-help', el).addEventListener('click', showAppliesToHelp);
$('.remove-section', el).addEventListener('click', () => removeSection(section));
$('.add-section', el).addEventListener('click', () => insertSectionAfter(undefined, section));
$('.clone-section', el).addEventListener('click', () => insertSectionAfter(getModel(), section));
$('.move-section-up', el).addEventListener('click', () => moveSectionUp(section));
$('.move-section-down', el).addEventListener('click', () => moveSectionDown(section));
$('.beautify-section', el).addEventListener('click', () => beautify([cm]));
$('.restore-section', el).addEventListener('click', () => restoreSection(section));
$('.test-regexp', el).addEventListener('click', () => {
regExpTester.toggle();
updateRegexpTester();
});
}
function getCode() {
return cm.getValue();
}
function remove(destroy = false) {
linter.disableForEditor(cm);
el.classList.add('removed');
removed = true;
appliesTo.forEach(a => a.remove());
if (destroy) {
cmFactory.destroy(cm);
}
}
function restore() {
linter.enableForEditor(cm);
el.classList.remove('removed');
removed = false;
appliesTo.forEach(a => a.restore());
render();
}
function render() {
cm.refresh();
}
function updateRegexpTester() {
const regexps = appliesTo.filter(a => a.getType() === 'regexp')
.map(a => a.getValue());
if (regexps.length) {
el.classList.add('has-regexp');
regExpTester.update(regexps);
} else {
el.classList.remove('has-regexp');
regExpTester.toggle(false);
}
}
function insertApplyAfter(init, base) {
const apply = createApply(init);
if (base) {
const index = appliesTo.indexOf(base);
appliesTo.splice(index + 1, 0, apply);
appliesToContainer.insertBefore(apply.el, base.el.nextSibling);
} else {
appliesTo.push(apply);
appliesToContainer.appendChild(apply.el);
}
dirty.add(apply, apply);
if (appliesTo.length > 1 && appliesTo[0].all) {
removeApply(appliesTo[0]);
}
emitSectionChange();
}
function removeApply(apply) {
const index = appliesTo.indexOf(apply);
appliesTo.splice(index, 1);
apply.remove();
apply.el.remove();
dirty.remove(apply, apply);
if (!appliesTo.length) {
insertApplyAfter({all: true});
}
emitSectionChange();
}
function createApply({type = 'url', value, all = false}) {
const applyId = INC_ID++;
const dirtyPrefix = `section.${sectionId}.apply.${applyId}`;
const el = all ? template.appliesToEverything.cloneNode(true) :
template.appliesTo.cloneNode(true);
const selectEl = !all && $('.applies-type', el);
if (selectEl) {
selectEl.value = type;
selectEl.addEventListener('change', () => {
const oldKey = type;
dirty.modify(`${dirtyPrefix}.type`, type, selectEl.value);
type = selectEl.value;
if (oldKey === 'regexp' || type === 'regexp') {
updateRegexpTester();
}
emitSectionChange();
validate();
});
}
const valueEl = !all && $('.applies-value', el);
if (valueEl) {
valueEl.value = value;
valueEl.addEventListener('input', () => {
dirty.modify(`${dirtyPrefix}.value`, value, valueEl.value);
value = valueEl.value;
if (type === 'regexp') {
updateRegexpTester();
}
emitSectionChange();
});
valueEl.addEventListener('change', validate);
}
const apply = {
id: applyId,
all,
remove,
restore,
el,
getType: () => type,
getValue: () => value,
valueEl
};
const removeButton = $('.remove-applies-to', el);
if (removeButton) {
removeButton.addEventListener('click', e => {
e.preventDefault();
removeApply(apply);
});
}
$('.add-applies-to', el).addEventListener('click', e => {
e.preventDefault();
insertApplyAfter({type, value: ''}, apply);
});
return apply;
function validate() {
if (type !== 'regexp' || tryRegExp(value)) {
valueEl.setCustomValidity('');
} else {
valueEl.setCustomValidity(t('styleBadRegexp'));
setTimeout(() => valueEl.reportValidity());
}
}
function remove() {
dirty.remove(`${dirtyPrefix}.type`, type);
dirty.remove(`${dirtyPrefix}.value`, value);
}
function restore() {
dirty.add(`${dirtyPrefix}.type`, type);
dirty.add(`${dirtyPrefix}.value`, value);
}
}
}
function replaceSections(originalSections) { function replaceSections(originalSections) {
for (const section of sections) { for (const section of sections) {
section.remove(true); section.remove(true);