diff --git a/edit.html b/edit.html
index 03eef893..6b7ae8b8 100644
--- a/edit.html
+++ b/edit.html
@@ -95,6 +95,7 @@
+
diff --git a/edit/edit.js b/edit/edit.js
index a5b144a4..6c861a2c 100644
--- a/edit/edit.js
+++ b/edit/edit.js
@@ -1,9 +1,9 @@
/* global CodeMirror onDOMready prefs setupLivePrefs $ $$ $create t tHTML
createSourceEditor queryTabs sessionStorageHash getOwnTab FIREFOX API tryCatch
closeCurrentTab messageBox debounce workerUtil
- beautify
+ beautify ignoreChromeError
moveFocus msg createSectionsEditor rerouteHotkeys */
-/* exported showCodeMirrorPopup editorWorker */
+/* exported showCodeMirrorPopup editorWorker toggleContextMenuDelete */
'use strict';
const editorWorker = workerUtil.createWorker({
@@ -557,3 +557,14 @@ function isWindowMaximized() {
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);
+ }
+}
diff --git a/edit/sections-editor-section.js b/edit/sections-editor-section.js
new file mode 100644
index 00000000..32196633
--- /dev/null
+++ b/edit/sections-editor-section.js
@@ -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);
+ }
+ }
+}
diff --git a/edit/sections-editor.js b/edit/sections-editor.js
index ec57d80f..c9b3421f 100644
--- a/edit/sections-editor.js
+++ b/edit/sections-editor.js
@@ -1,79 +1,11 @@
-/* global dirtyReporter showHelp prefs ignoreChromeError
- CodeMirror propertyToCss
- regExpTester linter createLivePreview showCodeMirrorPopup
- sectionsToMozFormat messageBox clipString beautify
- rerouteHotkeys cmFactory CssToProperty template $ $$ $create t FIREFOX API
- debounce tryRegExp
-*/
+/* global dirtyReporter showHelp toggleContextMenuDelete createSection
+ CodeMirror linter createLivePreview showCodeMirrorPopup
+ sectionsToMozFormat messageBox clipString
+ rerouteHotkeys $ $$ $create t FIREFOX API
+ debounce */
/* exported createSectionsEditor */
'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) {
let INC_ID = 0; // an increment id that is used by various object to track the order
const dirty = dirtyReporter();
@@ -97,7 +29,7 @@ function createSectionsEditor(style) {
$('#to-mozilla').addEventListener('click', showMozillaFormat);
$('#to-mozilla-help').addEventListener('click', showToMozillaHelp);
- $('#from-mozilla').addEventListener('click', () => fromMozillaFormat());
+ $('#from-mozilla').addEventListener('click', () => showMozillaFormatImport());
$('#save-button').addEventListener('click', saveStyle);
// FIXME: this element doesn't exist?
$('#sections-help').addEventListener('click', showSectionHelp);
@@ -150,15 +82,8 @@ function createSectionsEditor(style) {
getSearchableInputs,
};
- 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);
- }
+ function genId() {
+ return INC_ID++;
}
function setGlobalProgress(done, total) {
@@ -180,11 +105,6 @@ function createSectionsEditor(style) {
showHelp(t('styleMozillaFormatHeading'), t('styleToMozillaFormatHelp'));
}
- function showAppliesToHelp(event) {
- event.preventDefault();
- showHelp(t('appliesLabel'), t('appliesHelp'));
- }
-
function showSectionHelp(event) {
event.preventDefault();
showHelp(t('styleSectionsTitle'), t('sectionHelp'));
@@ -287,11 +207,33 @@ function createSectionsEditor(style) {
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);
}
- 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);
}
@@ -332,55 +274,6 @@ function createSectionsEditor(style) {
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) {
// make Shift-Ctrl-Wheel scroll entire page even when mouse is over a code editor
if (event.shiftKey && event.ctrlKey && !event.altKey && !event.metaKey) {
@@ -396,7 +289,7 @@ function createSectionsEditor(style) {
popup.codebox.execCommand('selectAll');
}
- function fromMozillaFormat(text = '') {
+ function showMozillaFormatImport(text = '') {
const popup = showCodeMirrorPopup(t('styleFromMozillaFormatPrompt'),
$create('.buttons', [
$create('button', {
@@ -612,7 +505,19 @@ function createSectionsEditor(style) {
if (!init) {
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) {
const index = sections.indexOf(base);
sections.splice(index + 1, 0, section);
@@ -650,286 +555,6 @@ function createSectionsEditor(style) {
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) {
for (const section of sections) {
section.remove(true);