Spaces:
Sleeping
Sleeping
processmaker-community / processmaker-files /gulliver /js /codemirror /src /display /update_display.js
| import { sawCollapsedSpans } from "../line/saw_special_spans" | |
| import { heightAtLine, visualLineEndNo, visualLineNo } from "../line/spans" | |
| import { getLine, lineNumberFor } from "../line/utils_line" | |
| import { displayHeight, displayWidth, getDimensions, paddingVert, scrollGap } from "../measurement/position_measurement" | |
| import { mac, webkit } from "../util/browser" | |
| import { activeElt, removeChildren } from "../util/dom" | |
| import { hasHandler, signal } from "../util/event" | |
| import { indexOf } from "../util/misc" | |
| import { buildLineElement, updateLineForChanges } from "./update_line" | |
| import { startWorker } from "./highlight_worker" | |
| import { maybeUpdateLineNumberWidth } from "./line_numbers" | |
| import { measureForScrollbars, updateScrollbars } from "./scrollbars" | |
| import { updateSelection } from "./selection" | |
| import { updateHeightsInViewport, visibleLines } from "./update_lines" | |
| import { adjustView, countDirtyView, resetView } from "./view_tracking" | |
| // DISPLAY DRAWING | |
| export class DisplayUpdate { | |
| constructor(cm, viewport, force) { | |
| let display = cm.display | |
| this.viewport = viewport | |
| // Store some values that we'll need later (but don't want to force a relayout for) | |
| this.visible = visibleLines(display, cm.doc, viewport) | |
| this.editorIsHidden = !display.wrapper.offsetWidth | |
| this.wrapperHeight = display.wrapper.clientHeight | |
| this.wrapperWidth = display.wrapper.clientWidth | |
| this.oldDisplayWidth = displayWidth(cm) | |
| this.force = force | |
| this.dims = getDimensions(cm) | |
| this.events = [] | |
| } | |
| signal(emitter, type) { | |
| if (hasHandler(emitter, type)) | |
| this.events.push(arguments) | |
| } | |
| finish() { | |
| for (let i = 0; i < this.events.length; i++) | |
| signal.apply(null, this.events[i]) | |
| } | |
| } | |
| export function maybeClipScrollbars(cm) { | |
| let display = cm.display | |
| if (!display.scrollbarsClipped && display.scroller.offsetWidth) { | |
| display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.clientWidth | |
| display.heightForcer.style.height = scrollGap(cm) + "px" | |
| display.sizer.style.marginBottom = -display.nativeBarWidth + "px" | |
| display.sizer.style.borderRightWidth = scrollGap(cm) + "px" | |
| display.scrollbarsClipped = true | |
| } | |
| } | |
| // Does the actual updating of the line display. Bails out | |
| // (returning false) when there is nothing to be done and forced is | |
| // false. | |
| export function updateDisplayIfNeeded(cm, update) { | |
| let display = cm.display, doc = cm.doc | |
| if (update.editorIsHidden) { | |
| resetView(cm) | |
| return false | |
| } | |
| // Bail out if the visible area is already rendered and nothing changed. | |
| if (!update.force && | |
| update.visible.from >= display.viewFrom && update.visible.to <= display.viewTo && | |
| (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo) && | |
| display.renderedView == display.view && countDirtyView(cm) == 0) | |
| return false | |
| if (maybeUpdateLineNumberWidth(cm)) { | |
| resetView(cm) | |
| update.dims = getDimensions(cm) | |
| } | |
| // Compute a suitable new viewport (from & to) | |
| let end = doc.first + doc.size | |
| let from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first) | |
| let to = Math.min(end, update.visible.to + cm.options.viewportMargin) | |
| if (display.viewFrom < from && from - display.viewFrom < 20) from = Math.max(doc.first, display.viewFrom) | |
| if (display.viewTo > to && display.viewTo - to < 20) to = Math.min(end, display.viewTo) | |
| if (sawCollapsedSpans) { | |
| from = visualLineNo(cm.doc, from) | |
| to = visualLineEndNo(cm.doc, to) | |
| } | |
| let different = from != display.viewFrom || to != display.viewTo || | |
| display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != update.wrapperWidth | |
| adjustView(cm, from, to) | |
| display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom)) | |
| // Position the mover div to align with the current scroll position | |
| cm.display.mover.style.top = display.viewOffset + "px" | |
| let toUpdate = countDirtyView(cm) | |
| if (!different && toUpdate == 0 && !update.force && display.renderedView == display.view && | |
| (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo)) | |
| return false | |
| // For big changes, we hide the enclosing element during the | |
| // update, since that speeds up the operations on most browsers. | |
| let focused = activeElt() | |
| if (toUpdate > 4) display.lineDiv.style.display = "none" | |
| patchDisplay(cm, display.updateLineNumbers, update.dims) | |
| if (toUpdate > 4) display.lineDiv.style.display = "" | |
| display.renderedView = display.view | |
| // There might have been a widget with a focused element that got | |
| // hidden or updated, if so re-focus it. | |
| if (focused && activeElt() != focused && focused.offsetHeight) focused.focus() | |
| // Prevent selection and cursors from interfering with the scroll | |
| // width and height. | |
| removeChildren(display.cursorDiv) | |
| removeChildren(display.selectionDiv) | |
| display.gutters.style.height = display.sizer.style.minHeight = 0 | |
| if (different) { | |
| display.lastWrapHeight = update.wrapperHeight | |
| display.lastWrapWidth = update.wrapperWidth | |
| startWorker(cm, 400) | |
| } | |
| display.updateLineNumbers = null | |
| return true | |
| } | |
| export function postUpdateDisplay(cm, update) { | |
| let viewport = update.viewport | |
| for (let first = true;; first = false) { | |
| if (!first || !cm.options.lineWrapping || update.oldDisplayWidth == displayWidth(cm)) { | |
| // Clip forced viewport to actual scrollable area. | |
| if (viewport && viewport.top != null) | |
| viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - displayHeight(cm), viewport.top)} | |
| // Updated line heights might result in the drawn area not | |
| // actually covering the viewport. Keep looping until it does. | |
| update.visible = visibleLines(cm.display, cm.doc, viewport) | |
| if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo) | |
| break | |
| } | |
| if (!updateDisplayIfNeeded(cm, update)) break | |
| updateHeightsInViewport(cm) | |
| let barMeasure = measureForScrollbars(cm) | |
| updateSelection(cm) | |
| updateScrollbars(cm, barMeasure) | |
| setDocumentHeight(cm, barMeasure) | |
| } | |
| update.signal(cm, "update", cm) | |
| if (cm.display.viewFrom != cm.display.reportedViewFrom || cm.display.viewTo != cm.display.reportedViewTo) { | |
| update.signal(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo) | |
| cm.display.reportedViewFrom = cm.display.viewFrom; cm.display.reportedViewTo = cm.display.viewTo | |
| } | |
| } | |
| export function updateDisplaySimple(cm, viewport) { | |
| let update = new DisplayUpdate(cm, viewport) | |
| if (updateDisplayIfNeeded(cm, update)) { | |
| updateHeightsInViewport(cm) | |
| postUpdateDisplay(cm, update) | |
| let barMeasure = measureForScrollbars(cm) | |
| updateSelection(cm) | |
| updateScrollbars(cm, barMeasure) | |
| setDocumentHeight(cm, barMeasure) | |
| update.finish() | |
| } | |
| } | |
| // Sync the actual display DOM structure with display.view, removing | |
| // nodes for lines that are no longer in view, and creating the ones | |
| // that are not there yet, and updating the ones that are out of | |
| // date. | |
| function patchDisplay(cm, updateNumbersFrom, dims) { | |
| let display = cm.display, lineNumbers = cm.options.lineNumbers | |
| let container = display.lineDiv, cur = container.firstChild | |
| function rm(node) { | |
| let next = node.nextSibling | |
| // Works around a throw-scroll bug in OS X Webkit | |
| if (webkit && mac && cm.display.currentWheelTarget == node) | |
| node.style.display = "none" | |
| else | |
| node.parentNode.removeChild(node) | |
| return next | |
| } | |
| let view = display.view, lineN = display.viewFrom | |
| // Loop over the elements in the view, syncing cur (the DOM nodes | |
| // in display.lineDiv) with the view as we go. | |
| for (let i = 0; i < view.length; i++) { | |
| let lineView = view[i] | |
| if (lineView.hidden) { | |
| } else if (!lineView.node || lineView.node.parentNode != container) { // Not drawn yet | |
| let node = buildLineElement(cm, lineView, lineN, dims) | |
| container.insertBefore(node, cur) | |
| } else { // Already drawn | |
| while (cur != lineView.node) cur = rm(cur) | |
| let updateNumber = lineNumbers && updateNumbersFrom != null && | |
| updateNumbersFrom <= lineN && lineView.lineNumber | |
| if (lineView.changes) { | |
| if (indexOf(lineView.changes, "gutter") > -1) updateNumber = false | |
| updateLineForChanges(cm, lineView, lineN, dims) | |
| } | |
| if (updateNumber) { | |
| removeChildren(lineView.lineNumber) | |
| lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm.options, lineN))) | |
| } | |
| cur = lineView.node.nextSibling | |
| } | |
| lineN += lineView.size | |
| } | |
| while (cur) cur = rm(cur) | |
| } | |
| export function updateGutterSpace(cm) { | |
| let width = cm.display.gutters.offsetWidth | |
| cm.display.sizer.style.marginLeft = width + "px" | |
| } | |
| export function setDocumentHeight(cm, measure) { | |
| cm.display.sizer.style.minHeight = measure.docHeight + "px" | |
| cm.display.heightForcer.style.top = measure.docHeight + "px" | |
| cm.display.gutters.style.height = (measure.docHeight + cm.display.barHeight + scrollGap(cm)) + "px" | |
| } | |