File modules/editor/codemirror/src/line/line_data.min.js

Last commit: Tue May 22 22:39:53 2018 +0200	Jan Dankert	Fix für PHP 7.2: 'Object' darf nun nicht mehr als Klassennamen verwendet werden. AUCH NICHT IN EINEM NAMESPACE! WTF, wozu habe ich das in einen verfickten Namespace gepackt? Wozu soll der sonst da sein??? Amateure. Daher nun notgedrungen unbenannt in 'BaseObject'.
1 import { getOrder } from "../util/bidi.js" 2 import { ie, ie_version, webkit } from "../util/browser.js" 3 import { elt, eltP, joinClasses } from "../util/dom.js" 4 import { eventMixin, signal } from "../util/event.js" 5 import { hasBadBidiRects, zeroWidthElement } from "../util/feature_detection.js" 6 import { lst, spaceStr } from "../util/misc.js" 7 8 import { getLineStyles } from "./highlight.js" 9 import { attachMarkedSpans, compareCollapsedMarkers, detachMarkedSpans, lineIsHidden, visualLineContinued } from "./spans.js" 10 import { getLine, lineNo, updateLineHeight } from "./utils_line.js" 11 12 // LINE DATA STRUCTURE 13 14 // Line objects. These hold state related to a line, including 15 // highlighting info (the styles array). 16 export class Line { 17 constructor(text, markedSpans, estimateHeight) { 18 this.text = text 19 attachMarkedSpans(this, markedSpans) 20 this.height = estimateHeight ? estimateHeight(this) : 1 21 } 22 23 lineNo() { return lineNo(this) } 24 } 25 eventMixin(Line) 26 27 // Change the content (text, markers) of a line. Automatically 28 // invalidates cached information and tries to re-estimate the 29 // line's height. 30 export function updateLine(line, text, markedSpans, estimateHeight) { 31 line.text = text 32 if (line.stateAfter) line.stateAfter = null 33 if (line.styles) line.styles = null 34 if (line.order != null) line.order = null 35 detachMarkedSpans(line) 36 attachMarkedSpans(line, markedSpans) 37 let estHeight = estimateHeight ? estimateHeight(line) : 1 38 if (estHeight != line.height) updateLineHeight(line, estHeight) 39 } 40 41 // Detach a line from the document tree and its markers. 42 export function cleanUpLine(line) { 43 line.parent = null 44 detachMarkedSpans(line) 45 } 46 47 // Convert a style as returned by a mode (either null, or a string 48 // containing one or more styles) to a CSS style. This is cached, 49 // and also looks for line-wide styles. 50 let styleToClassCache = {}, styleToClassCacheWithMode = {} 51 function interpretTokenStyle(style, options) { 52 if (!style || /^\s*$/.test(style)) return null 53 let cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache 54 return cache[style] || 55 (cache[style] = style.replace(/\S+/g, "cm-$&")) 56 } 57 58 // Render the DOM representation of the text of a line. Also builds 59 // up a 'line map', which points at the DOM nodes that represent 60 // specific stretches of text, and is used by the measuring code. 61 // The returned object contains the DOM node, this map, and 62 // information about line-wide styles that were set by the mode. 63 export function buildLineContent(cm, lineView) { 64 // The padding-right forces the element to have a 'border', which 65 // is needed on Webkit to be able to get line-level bounding 66 // rectangles for it (in measureChar). 67 let content = eltP("span", null, null, webkit ? "padding-right: .1px" : null) 68 let builder = {pre: eltP("pre", [content], "CodeMirror-line"), content: content, 69 col: 0, pos: 0, cm: cm, 70 trailingSpace: false, 71 splitSpaces: (ie || webkit) && cm.getOption("lineWrapping")} 72 lineView.measure = {} 73 74 // Iterate over the logical lines that make up this visual line. 75 for (let i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) { 76 let line = i ? lineView.rest[i - 1] : lineView.line, order 77 builder.pos = 0 78 builder.addToken = buildToken 79 // Optionally wire in some hacks into the token-rendering 80 // algorithm, to deal with browser quirks. 81 if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line, cm.doc.direction))) 82 builder.addToken = buildTokenBadBidi(builder.addToken, order) 83 builder.map = [] 84 let allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line) 85 insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate)) 86 if (line.styleClasses) { 87 if (line.styleClasses.bgClass) 88 builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || "") 89 if (line.styleClasses.textClass) 90 builder.textClass = joinClasses(line.styleClasses.textClass, builder.textClass || "") 91 } 92 93 // Ensure at least a single node is present, for measuring. 94 if (builder.map.length == 0) 95 builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.display.measure))) 96 97 // Store the map and a cache object for the current logical line 98 if (i == 0) { 99 lineView.measure.map = builder.map 100 lineView.measure.cache = {} 101 } else { 102 ;(lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map) 103 ;(lineView.measure.caches || (lineView.measure.caches = [])).push({}) 104 } 105 } 106 107 // See issue #2901 108 if (webkit) { 109 let last = builder.content.lastChild 110 if (/\bcm-tab\b/.test(last.className) || (last.querySelector && last.querySelector(".cm-tab"))) 111 builder.content.className = "cm-tab-wrap-hack" 112 } 113 114 signal(cm, "renderLine", cm, lineView.line, builder.pre) 115 if (builder.pre.className) 116 builder.textClass = joinClasses(builder.pre.className, builder.textClass || "") 117 118 return builder 119 } 120 121 export function defaultSpecialCharPlaceholder(ch) { 122 let token = elt("span", "\u2022", "cm-invalidchar") 123 token.title = "\\u" + ch.charCodeAt(0).toString(16) 124 token.setAttribute("aria-label", token.title) 125 return token 126 } 127 128 // Build up the DOM representation for a single token, and add it to 129 // the line map. Takes care to render special characters separately. 130 function buildToken(builder, text, style, startStyle, endStyle, title, css) { 131 if (!text) return 132 let displayText = builder.splitSpaces ? splitSpaces(text, builder.trailingSpace) : text 133 let special = builder.cm.state.specialChars, mustWrap = false 134 let content 135 if (!special.test(text)) { 136 builder.col += text.length 137 content = document.createTextNode(displayText) 138 builder.map.push(builder.pos, builder.pos + text.length, content) 139 if (ie && ie_version < 9) mustWrap = true 140 builder.pos += text.length 141 } else { 142 content = document.createDocumentFragment() 143 let pos = 0 144 while (true) { 145 special.lastIndex = pos 146 let m = special.exec(text) 147 let skipped = m ? m.index - pos : text.length - pos 148 if (skipped) { 149 let txt = document.createTextNode(displayText.slice(pos, pos + skipped)) 150 if (ie && ie_version < 9) content.appendChild(elt("span", [txt])) 151 else content.appendChild(txt) 152 builder.map.push(builder.pos, builder.pos + skipped, txt) 153 builder.col += skipped 154 builder.pos += skipped 155 } 156 if (!m) break 157 pos += skipped + 1 158 let txt 159 if (m[0] == "\t") { 160 let tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize 161 txt = content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab")) 162 txt.setAttribute("role", "presentation") 163 txt.setAttribute("cm-text", "\t") 164 builder.col += tabWidth 165 } else if (m[0] == "\r" || m[0] == "\n") { 166 txt = content.appendChild(elt("span", m[0] == "\r" ? "\u240d" : "\u2424", "cm-invalidchar")) 167 txt.setAttribute("cm-text", m[0]) 168 builder.col += 1 169 } else { 170 txt = builder.cm.options.specialCharPlaceholder(m[0]) 171 txt.setAttribute("cm-text", m[0]) 172 if (ie && ie_version < 9) content.appendChild(elt("span", [txt])) 173 else content.appendChild(txt) 174 builder.col += 1 175 } 176 builder.map.push(builder.pos, builder.pos + 1, txt) 177 builder.pos++ 178 } 179 } 180 builder.trailingSpace = displayText.charCodeAt(text.length - 1) == 32 181 if (style || startStyle || endStyle || mustWrap || css) { 182 let fullStyle = style || "" 183 if (startStyle) fullStyle += startStyle 184 if (endStyle) fullStyle += endStyle 185 let token = elt("span", [content], fullStyle, css) 186 if (title) token.title = title 187 return builder.content.appendChild(token) 188 } 189 builder.content.appendChild(content) 190 } 191 192 function splitSpaces(text, trailingBefore) { 193 if (text.length > 1 && !/ /.test(text)) return text 194 let spaceBefore = trailingBefore, result = "" 195 for (let i = 0; i < text.length; i++) { 196 let ch = text.charAt(i) 197 if (ch == " " && spaceBefore && (i == text.length - 1 || text.charCodeAt(i + 1) == 32)) 198 ch = "\u00a0" 199 result += ch 200 spaceBefore = ch == " " 201 } 202 return result 203 } 204 205 // Work around nonsense dimensions being reported for stretches of 206 // right-to-left text. 207 function buildTokenBadBidi(inner, order) { 208 return (builder, text, style, startStyle, endStyle, title, css) => { 209 style = style ? style + " cm-force-border" : "cm-force-border" 210 let start = builder.pos, end = start + text.length 211 for (;;) { 212 // Find the part that overlaps with the start of this text 213 let part 214 for (let i = 0; i < order.length; i++) { 215 part = order[i] 216 if (part.to > start && part.from <= start) break 217 } 218 if (part.to >= end) return inner(builder, text, style, startStyle, endStyle, title, css) 219 inner(builder, text.slice(0, part.to - start), style, startStyle, null, title, css) 220 startStyle = null 221 text = text.slice(part.to - start) 222 start = part.to 223 } 224 } 225 } 226 227 function buildCollapsedSpan(builder, size, marker, ignoreWidget) { 228 let widget = !ignoreWidget && marker.widgetNode 229 if (widget) builder.map.push(builder.pos, builder.pos + size, widget) 230 if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) { 231 if (!widget) 232 widget = builder.content.appendChild(document.createElement("span")) 233 widget.setAttribute("cm-marker", marker.id) 234 } 235 if (widget) { 236 builder.cm.display.input.setUneditable(widget) 237 builder.content.appendChild(widget) 238 } 239 builder.pos += size 240 builder.trailingSpace = false 241 } 242 243 // Outputs a number of spans to make up a line, taking highlighting 244 // and marked text into account. 245 function insertLineContent(line, builder, styles) { 246 let spans = line.markedSpans, allText = line.text, at = 0 247 if (!spans) { 248 for (let i = 1; i < styles.length; i+=2) 249 builder.addToken(builder, allText.slice(at, at = styles[i]), interpretTokenStyle(styles[i+1], builder.cm.options)) 250 return 251 } 252 253 let len = allText.length, pos = 0, i = 1, text = "", style, css 254 let nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, title, collapsed 255 for (;;) { 256 if (nextChange == pos) { // Update current marker set 257 spanStyle = spanEndStyle = spanStartStyle = title = css = "" 258 collapsed = null; nextChange = Infinity 259 let foundBookmarks = [], endStyles 260 for (let j = 0; j < spans.length; ++j) { 261 let sp = spans[j], m = sp.marker 262 if (m.type == "bookmark" && sp.from == pos && m.widgetNode) { 263 foundBookmarks.push(m) 264 } else if (sp.from <= pos && (sp.to == null || sp.to > pos || m.collapsed && sp.to == pos && sp.from == pos)) { 265 if (sp.to != null && sp.to != pos && nextChange > sp.to) { 266 nextChange = sp.to 267 spanEndStyle = "" 268 } 269 if (m.className) spanStyle += " " + m.className 270 if (m.css) css = (css ? css + ";" : "") + m.css 271 if (m.startStyle && sp.from == pos) spanStartStyle += " " + m.startStyle 272 if (m.endStyle && sp.to == nextChange) (endStyles || (endStyles = [])).push(m.endStyle, sp.to) 273 if (m.title && !title) title = m.title 274 if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0)) 275 collapsed = sp 276 } else if (sp.from > pos && nextChange > sp.from) { 277 nextChange = sp.from 278 } 279 } 280 if (endStyles) for (let j = 0; j < endStyles.length; j += 2) 281 if (endStyles[j + 1] == nextChange) spanEndStyle += " " + endStyles[j] 282 283 if (!collapsed || collapsed.from == pos) for (let j = 0; j < foundBookmarks.length; ++j) 284 buildCollapsedSpan(builder, 0, foundBookmarks[j]) 285 if (collapsed && (collapsed.from || 0) == pos) { 286 buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos, 287 collapsed.marker, collapsed.from == null) 288 if (collapsed.to == null) return 289 if (collapsed.to == pos) collapsed = false 290 } 291 } 292 if (pos >= len) break 293 294 let upto = Math.min(len, nextChange) 295 while (true) { 296 if (text) { 297 let end = pos + text.length 298 if (!collapsed) { 299 let tokenText = end > upto ? text.slice(0, upto - pos) : text 300 builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle, 301 spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", title, css) 302 } 303 if (end >= upto) {text = text.slice(upto - pos); pos = upto; break} 304 pos = end 305 spanStartStyle = "" 306 } 307 text = allText.slice(at, at = styles[i++]) 308 style = interpretTokenStyle(styles[i++], builder.cm.options) 309 } 310 } 311 } 312 313 314 // These objects are used to represent the visible (currently drawn) 315 // part of the document. A LineView may correspond to multiple 316 // logical lines, if those are connected by collapsed ranges. 317 export function LineView(doc, line, lineN) { 318 // The starting line 319 this.line = line 320 // Continuing lines, if any 321 this.rest = visualLineContinued(line) 322 // Number of logical lines in this visual line 323 this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1 324 this.node = this.text = null 325 this.hidden = lineIsHidden(doc, line) 326 } 327 328 // Create a range of LineView objects for the given lines. 329 export function buildViewArray(cm, from, to) { 330 let array = [], nextPos 331 for (let pos = from; pos < to; pos = nextPos) { 332 let view = new LineView(cm.doc, getLine(cm.doc, pos), pos) 333 nextPos = pos + view.size 334 array.push(view) 335 } 336 return array 337 }
Download modules/editor/codemirror/src/line/line_data.min.js
History Tue, 22 May 2018 22:39:53 +0200 Jan Dankert Fix für PHP 7.2: 'Object' darf nun nicht mehr als Klassennamen verwendet werden. AUCH NICHT IN EINEM NAMESPACE! WTF, wozu habe ich das in einen verfickten Namespace gepackt? Wozu soll der sonst da sein??? Amateure. Daher nun notgedrungen unbenannt in 'BaseObject'.