diff --git a/edit/codemirror-default.js b/edit/codemirror-default.js index 0912f445..1b9b15b3 100644 --- a/edit/codemirror-default.js +++ b/edit/codemirror-default.js @@ -1,4 +1,4 @@ -/* global CodeMirror prefs loadScript editor editors */ +/* global CodeMirror prefs loadScript editor */ 'use strict'; diff --git a/edit/codemirror-editing-hooks.js b/edit/codemirror-editing-hooks.js index f8a4aafb..a5eccd27 100644 --- a/edit/codemirror-editing-hooks.js +++ b/edit/codemirror-editing-hooks.js @@ -27,13 +27,8 @@ onDOMscriptReady('/codemirror.js').then(() => { 'find', 'findNext', 'findPrev', 'replace', 'replaceAll', 'colorpicker', ]); - Object.assign(CodeMirror, { - getOption, - setOption, - closestVisible, - }); Object.assign(CodeMirror.prototype, { - getSection, + // getSection, rerouteHotkeys, }); Object.assign(CodeMirror.commands, COMMANDS); @@ -256,6 +251,7 @@ onDOMscriptReady('/codemirror.js').then(() => { case 'autocompleteOnTyping': if (editor) { + // FIXME: this won't work with removed sections editor.getEditors().forEach(cm => setupAutocomplete(cm, value)); } return; @@ -304,7 +300,7 @@ onDOMscriptReady('/codemirror.js').then(() => { } const rerouteCommand = name => { if (REROUTED.has(name)) { - CodeMirror.commands[name](closestVisible(event.target)); + CodeMirror.commands[name](editor.closestVisible(event.target)); return true; } }; @@ -317,87 +313,6 @@ onDOMscriptReady('/codemirror.js').then(() => { //////////////////////////////////////////////// - // priority: - // 1. associated CM for applies-to element - // 2. last active if visible - // 3. first visible - function closestVisible(nearbyElement) { - const cm = - nearbyElement instanceof CodeMirror ? nearbyElement : - nearbyElement instanceof Node && - (nearbyElement.closest('#sections > .section') || {}).CodeMirror || - editor.getLastActivatedEditor(); - if (nearbyElement instanceof Node && cm) { - const {left, top} = nearbyElement.getBoundingClientRect(); - const bounds = cm.display.wrapper.getBoundingClientRect(); - if (top >= 0 && top >= bounds.top && - left >= 0 && left >= bounds.left) { - return cm; - } - } - // closest editor should have at least 2 lines visible - const lineHeight = editor.getEditors()[0].defaultTextHeight(); - const scrollY = window.scrollY; - const windowBottom = scrollY + window.innerHeight - 2 * lineHeight; - const allSectionsContainerTop = scrollY + $('#sections').getBoundingClientRect().top; - const distances = []; - const alreadyInView = cm && offscreenDistance(null, cm) === 0; - return alreadyInView ? cm : findClosest(); - - function offscreenDistance(index, cm) { - if (index >= 0 && distances[index] !== undefined) { - return distances[index]; - } - const section = cm.display.wrapper.closest('.section'); - if (!section) { - return 1e9; - } - const top = allSectionsContainerTop + section.offsetTop; - if (top < scrollY + lineHeight) { - return Math.max(0, scrollY - top - lineHeight); - } - if (top < windowBottom) { - return 0; - } - const distance = top - windowBottom + section.offsetHeight; - if (index >= 0) { - distances[index] = distance; - } - return distance; - } - - function findClosest() { - const editors = editor.getEditors(); - const last = editors.length - 1; - let a = 0; - let b = last; - let c; - let distance; - while (a < b - 1) { - c = (a + b) / 2 | 0; - distance = offscreenDistance(c); - if (!distance || !c) { - break; - } - const distancePrev = offscreenDistance(c - 1); - const distanceNext = c < last ? offscreenDistance(c + 1) : 1e20; - if (distancePrev <= distance && distance <= distanceNext) { - b = c; - } else { - a = c; - } - } - while (b && offscreenDistance(b - 1) <= offscreenDistance(b)) { - b--; - } - const cm = editors[b]; - if (distances[b] > 0) { - editor.scrollToEditor(cm); - } - return cm; - } - } - //////////////////////////////////////////////// function setupAutocomplete(cm, enable = true) { diff --git a/edit/edit.js b/edit/edit.js index a9dd4f13..7067ae28 100644 --- a/edit/edit.js +++ b/edit/edit.js @@ -4,9 +4,7 @@ global createSourceEditor global closeCurrentTab regExpTester messageBox global setupCodeMirror global beautify -global initWithSectionStyle addSections removeSection getSectionsHashes global sectionsToMozFormat -global exclusions global moveFocus editorWorker msg createSectionEditor */ 'use strict'; diff --git a/edit/global-search.js b/edit/global-search.js index c05492c5..7fe232f8 100644 --- a/edit/global-search.js +++ b/edit/global-search.js @@ -1,4 +1,4 @@ -/* global CodeMirror editors makeSectionVisible */ +/* global CodeMirror */ /* global focusAccessibility */ /* global colorMimicry editor */ 'use strict'; @@ -207,7 +207,7 @@ onDOMready().then(() => { } const cmFocused = document.activeElement && document.activeElement.closest('.CodeMirror'); state.activeAppliesTo = $(`.${APPLIES_VALUE_CLASS}:focus, .${APPLIES_VALUE_CLASS}.${TARGET_CLASS}`); - state.cmStart = CodeMirror.closestVisible( + state.cmStart = editor.closestVisible( cmFocused && document.activeElement || state.activeAppliesTo || state.cm); @@ -291,7 +291,7 @@ onDOMready().then(() => { function doSearchInApplies(cm, canAdvance) { if (!state.searchInApplies) return; - const inputs = [...cm.getSection().getElementsByClassName(APPLIES_VALUE_CLASS)]; + const inputs = editor.getSearchableInputs(cm); if (state.reverse) inputs.reverse(); inputs.splice(0, inputs.indexOf(state.activeAppliesTo)); for (const input of inputs) { diff --git a/edit/sections-editor.js b/edit/sections-editor.js index 668e3c3d..aa3fe69a 100644 --- a/edit/sections-editor.js +++ b/edit/sections-editor.js @@ -61,10 +61,11 @@ function createResizeGrip(cm) { const allBounds = $('#sections').getBoundingClientRect(); const pageExtrasHeight = allBounds.top + window.scrollY + parseFloat(getComputedStyle($('#sections')).paddingBottom); - const sectionExtrasHeight = cm.getSection().clientHeight - wrapper.offsetHeight; + const sectionEl = wrapper.parentNode; + const sectionExtrasHeight = sectionEl.clientHeight - wrapper.offsetHeight; cm.state.toggleHeightSaved = wrapper.clientHeight; cm.setSize(null, window.innerHeight - sectionExtrasHeight - pageExtrasHeight); - const bounds = cm.getSection().getBoundingClientRect(); + const bounds = sectionEl.getBoundingClientRect(); if (bounds.top < 0 || bounds.bottom > window.innerHeight) { window.scrollBy(0, bounds.top); } @@ -143,9 +144,96 @@ function createSectionsEditor(style) { save: saveStyle, toggleStyle, nextEditor, - prevEditor + prevEditor, + closestVisible, + getSearchableInputs, }; + function getSearchableInputs(cm) { + return sections.find(s => s.cm === cm).appliesTo.map(a => a.valueEl).filter(Boolean); + } + + // priority: + // 1. associated CM for applies-to element + // 2. last active if visible + // 3. first visible + function closestVisible(nearbyElement) { + const cm = + nearbyElement instanceof CodeMirror ? nearbyElement : + nearbyElement instanceof Node && + (nearbyElement.closest('#sections > .section') || {}).CodeMirror || + editor.getLastActivatedEditor(); + if (nearbyElement instanceof Node && cm) { + const {left, top} = nearbyElement.getBoundingClientRect(); + const bounds = cm.display.wrapper.getBoundingClientRect(); + if (top >= 0 && top >= bounds.top && + left >= 0 && left >= bounds.left) { + return cm; + } + } + // closest editor should have at least 2 lines visible + const lineHeight = editor.getEditors()[0].defaultTextHeight(); + const scrollY = window.scrollY; + const windowBottom = scrollY + window.innerHeight - 2 * lineHeight; + const allSectionsContainerTop = scrollY + $('#sections').getBoundingClientRect().top; + const distances = []; + const alreadyInView = cm && offscreenDistance(null, cm) === 0; + return alreadyInView ? cm : findClosest(); + + function offscreenDistance(index, cm) { + if (index >= 0 && distances[index] !== undefined) { + return distances[index]; + } + const section = cm.display.wrapper.closest('.section'); + if (!section) { + return 1e9; + } + const top = allSectionsContainerTop + section.offsetTop; + if (top < scrollY + lineHeight) { + return Math.max(0, scrollY - top - lineHeight); + } + if (top < windowBottom) { + return 0; + } + const distance = top - windowBottom + section.offsetHeight; + if (index >= 0) { + distances[index] = distance; + } + return distance; + } + + function findClosest() { + const editors = editor.getEditors(); + const last = editors.length - 1; + let a = 0; + let b = last; + let c; + let distance; + while (a < b - 1) { + c = (a + b) / 2 | 0; + distance = offscreenDistance(c); + if (!distance || !c) { + break; + } + const distancePrev = offscreenDistance(c - 1); + const distanceNext = c < last ? offscreenDistance(c + 1) : 1e20; + if (distancePrev <= distance && distance <= distanceNext) { + b = c; + } else { + a = c; + } + } + while (b && offscreenDistance(b - 1) <= offscreenDistance(b)) { + b--; + } + const cm = editors[b]; + if (distances[b] > 0) { + editor.scrollToEditor(cm); + } + return cm; + } + } + function getEditors() { return sections.filter(s => !s.isRemoved()).map(s => s.cm); } @@ -241,12 +329,13 @@ function createSectionsEditor(style) { cm.setCursor(0, 0); break; } - const animation = (cm.getSection().firstElementChild.getAnimations() || [])[0]; - if (animation) { - animation.playbackRate = -1; - animation.currentTime = 2000; - animation.play(); - } + // FIXME: what is this? + // const animation = (cm.getSection().firstElementChild.getAnimations() || [])[0]; + // if (animation) { + // animation.playbackRate = -1; + // animation.currentTime = 2000; + // animation.play(); + // } } function scrollEntirePageOnCtrlShift(event) { diff --git a/edit/source-editor.js b/edit/source-editor.js index 1afb502d..10604551 100644 --- a/edit/source-editor.js +++ b/edit/source-editor.js @@ -52,8 +52,6 @@ function createSourceEditor(style) { updateLivePreview(); }); - CodeMirror.closestVisible = () => cm; - cm.operation(initAppliesToLineWidget); const metaCompiler = createMetaCompiler(cm); @@ -409,6 +407,7 @@ function createSourceEditor(style) { save, toggleStyle, prevEditor: cm => nextPrevMozDocument(cm, -1), - nextEditor: cm => nextPrevMozDocument(cm, 1) + nextEditor: cm => nextPrevMozDocument(cm, 1), + closestVisible: () => cm }; }