File modules/editor/codemirror/src/edit/methods.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 { deleteNearSelection } from "./deleteNearSelection.js" 2 import { commands } from "./commands.js" 3 import { attachDoc } from "../model/document_data.js" 4 import { activeElt, addClass, rmClass } from "../util/dom.js" 5 import { eventMixin, signal } from "../util/event.js" 6 import { getLineStyles, getContextBefore, takeToken } from "../line/highlight.js" 7 import { indentLine } from "../input/indent.js" 8 import { triggerElectric } from "../input/input.js" 9 import { onKeyDown, onKeyPress, onKeyUp } from "./key_events.js" 10 import { onMouseDown } from "./mouse_events.js" 11 import { getKeyMap } from "../input/keymap.js" 12 import { endOfLine, moveLogically, moveVisually } from "../input/movement.js" 13 import { endOperation, methodOp, operation, runInOp, startOperation } from "../display/operations.js" 14 import { clipLine, clipPos, equalCursorPos, Pos } from "../line/pos.js" 15 import { charCoords, charWidth, clearCaches, clearLineMeasurementCache, coordsChar, cursorCoords, displayHeight, displayWidth, estimateLineHeights, fromCoordSystem, intoCoordSystem, scrollGap, textHeight } from "../measurement/position_measurement.js" 16 import { Range } from "../model/selection.js" 17 import { replaceOneSelection, skipAtomic } from "../model/selection_updates.js" 18 import { addToScrollTop, ensureCursorVisible, scrollIntoView, scrollToCoords, scrollToCoordsRange, scrollToRange } from "../display/scrolling.js" 19 import { heightAtLine } from "../line/spans.js" 20 import { updateGutterSpace } from "../display/update_display.js" 21 import { indexOf, insertSorted, isWordChar, sel_dontScroll, sel_move } from "../util/misc.js" 22 import { signalLater } from "../util/operation_group.js" 23 import { getLine, isLine, lineAtHeight } from "../line/utils_line.js" 24 import { regChange, regLineChange } from "../display/view_tracking.js" 25 26 // The publicly visible API. Note that methodOp(f) means 27 // 'wrap f in an operation, performed on its `this` parameter'. 28 29 // This is not the complete set of editor methods. Most of the 30 // methods defined on the Doc type are also injected into 31 // CodeMirror.prototype, for backwards compatibility and 32 // convenience. 33 34 export default function(CodeMirror) { 35 let optionHandlers = CodeMirror.optionHandlers 36 37 let helpers = CodeMirror.helpers = {} 38 39 CodeMirror.prototype = { 40 constructor: CodeMirror, 41 focus: function(){window.focus(); this.display.input.focus()}, 42 43 setOption: function(option, value) { 44 let options = this.options, old = options[option] 45 if (options[option] == value && option != "mode") return 46 options[option] = value 47 if (optionHandlers.hasOwnProperty(option)) 48 operation(this, optionHandlers[option])(this, value, old) 49 signal(this, "optionChange", this, option) 50 }, 51 52 getOption: function(option) {return this.options[option]}, 53 getDoc: function() {return this.doc}, 54 55 addKeyMap: function(map, bottom) { 56 this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map)) 57 }, 58 removeKeyMap: function(map) { 59 let maps = this.state.keyMaps 60 for (let i = 0; i < maps.length; ++i) 61 if (maps[i] == map || maps[i].name == map) { 62 maps.splice(i, 1) 63 return true 64 } 65 }, 66 67 addOverlay: methodOp(function(spec, options) { 68 let mode = spec.token ? spec : CodeMirror.getMode(this.options, spec) 69 if (mode.startState) throw new Error("Overlays may not be stateful.") 70 insertSorted(this.state.overlays, 71 {mode: mode, modeSpec: spec, opaque: options && options.opaque, 72 priority: (options && options.priority) || 0}, 73 overlay => overlay.priority) 74 this.state.modeGen++ 75 regChange(this) 76 }), 77 removeOverlay: methodOp(function(spec) { 78 let overlays = this.state.overlays 79 for (let i = 0; i < overlays.length; ++i) { 80 let cur = overlays[i].modeSpec 81 if (cur == spec || typeof spec == "string" && cur.name == spec) { 82 overlays.splice(i, 1) 83 this.state.modeGen++ 84 regChange(this) 85 return 86 } 87 } 88 }), 89 90 indentLine: methodOp(function(n, dir, aggressive) { 91 if (typeof dir != "string" && typeof dir != "number") { 92 if (dir == null) dir = this.options.smartIndent ? "smart" : "prev" 93 else dir = dir ? "add" : "subtract" 94 } 95 if (isLine(this.doc, n)) indentLine(this, n, dir, aggressive) 96 }), 97 indentSelection: methodOp(function(how) { 98 let ranges = this.doc.sel.ranges, end = -1 99 for (let i = 0; i < ranges.length; i++) { 100 let range = ranges[i] 101 if (!range.empty()) { 102 let from = range.from(), to = range.to() 103 let start = Math.max(end, from.line) 104 end = Math.min(this.lastLine(), to.line - (to.ch ? 0 : 1)) + 1 105 for (let j = start; j < end; ++j) 106 indentLine(this, j, how) 107 let newRanges = this.doc.sel.ranges 108 if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].from().ch > 0) 109 replaceOneSelection(this.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll) 110 } else if (range.head.line > end) { 111 indentLine(this, range.head.line, how, true) 112 end = range.head.line 113 if (i == this.doc.sel.primIndex) ensureCursorVisible(this) 114 } 115 } 116 }), 117 118 // Fetch the parser token for a given character. Useful for hacks 119 // that want to inspect the mode state (say, for completion). 120 getTokenAt: function(pos, precise) { 121 return takeToken(this, pos, precise) 122 }, 123 124 getLineTokens: function(line, precise) { 125 return takeToken(this, Pos(line), precise, true) 126 }, 127 128 getTokenTypeAt: function(pos) { 129 pos = clipPos(this.doc, pos) 130 let styles = getLineStyles(this, getLine(this.doc, pos.line)) 131 let before = 0, after = (styles.length - 1) / 2, ch = pos.ch 132 let type 133 if (ch == 0) type = styles[2] 134 else for (;;) { 135 let mid = (before + after) >> 1 136 if ((mid ? styles[mid * 2 - 1] : 0) >= ch) after = mid 137 else if (styles[mid * 2 + 1] < ch) before = mid + 1 138 else { type = styles[mid * 2 + 2]; break } 139 } 140 let cut = type ? type.indexOf("overlay ") : -1 141 return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1) 142 }, 143 144 getModeAt: function(pos) { 145 let mode = this.doc.mode 146 if (!mode.innerMode) return mode 147 return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode 148 }, 149 150 getHelper: function(pos, type) { 151 return this.getHelpers(pos, type)[0] 152 }, 153 154 getHelpers: function(pos, type) { 155 let found = [] 156 if (!helpers.hasOwnProperty(type)) return found 157 let help = helpers[type], mode = this.getModeAt(pos) 158 if (typeof mode[type] == "string") { 159 if (help[mode[type]]) found.push(help[mode[type]]) 160 } else if (mode[type]) { 161 for (let i = 0; i < mode[type].length; i++) { 162 let val = help[mode[type][i]] 163 if (val) found.push(val) 164 } 165 } else if (mode.helperType && help[mode.helperType]) { 166 found.push(help[mode.helperType]) 167 } else if (help[mode.name]) { 168 found.push(help[mode.name]) 169 } 170 for (let i = 0; i < help._global.length; i++) { 171 let cur = help._global[i] 172 if (cur.pred(mode, this) && indexOf(found, cur.val) == -1) 173 found.push(cur.val) 174 } 175 return found 176 }, 177 178 getStateAfter: function(line, precise) { 179 let doc = this.doc 180 line = clipLine(doc, line == null ? doc.first + doc.size - 1: line) 181 return getContextBefore(this, line + 1, precise).state 182 }, 183 184 cursorCoords: function(start, mode) { 185 let pos, range = this.doc.sel.primary() 186 if (start == null) pos = range.head 187 else if (typeof start == "object") pos = clipPos(this.doc, start) 188 else pos = start ? range.from() : range.to() 189 return cursorCoords(this, pos, mode || "page") 190 }, 191 192 charCoords: function(pos, mode) { 193 return charCoords(this, clipPos(this.doc, pos), mode || "page") 194 }, 195 196 coordsChar: function(coords, mode) { 197 coords = fromCoordSystem(this, coords, mode || "page") 198 return coordsChar(this, coords.left, coords.top) 199 }, 200 201 lineAtHeight: function(height, mode) { 202 height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top 203 return lineAtHeight(this.doc, height + this.display.viewOffset) 204 }, 205 heightAtLine: function(line, mode, includeWidgets) { 206 let end = false, lineObj 207 if (typeof line == "number") { 208 let last = this.doc.first + this.doc.size - 1 209 if (line < this.doc.first) line = this.doc.first 210 else if (line > last) { line = last; end = true } 211 lineObj = getLine(this.doc, line) 212 } else { 213 lineObj = line 214 } 215 return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || "page", includeWidgets || end).top + 216 (end ? this.doc.height - heightAtLine(lineObj) : 0) 217 }, 218 219 defaultTextHeight: function() { return textHeight(this.display) }, 220 defaultCharWidth: function() { return charWidth(this.display) }, 221 222 getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo}}, 223 224 addWidget: function(pos, node, scroll, vert, horiz) { 225 let display = this.display 226 pos = cursorCoords(this, clipPos(this.doc, pos)) 227 let top = pos.bottom, left = pos.left 228 node.style.position = "absolute" 229 node.setAttribute("cm-ignore-events", "true") 230 this.display.input.setUneditable(node) 231 display.sizer.appendChild(node) 232 if (vert == "over") { 233 top = pos.top 234 } else if (vert == "above" || vert == "near") { 235 let vspace = Math.max(display.wrapper.clientHeight, this.doc.height), 236 hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth) 237 // Default to positioning above (if specified and possible); otherwise default to positioning below 238 if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight) 239 top = pos.top - node.offsetHeight 240 else if (pos.bottom + node.offsetHeight <= vspace) 241 top = pos.bottom 242 if (left + node.offsetWidth > hspace) 243 left = hspace - node.offsetWidth 244 } 245 node.style.top = top + "px" 246 node.style.left = node.style.right = "" 247 if (horiz == "right") { 248 left = display.sizer.clientWidth - node.offsetWidth 249 node.style.right = "0px" 250 } else { 251 if (horiz == "left") left = 0 252 else if (horiz == "middle") left = (display.sizer.clientWidth - node.offsetWidth) / 2 253 node.style.left = left + "px" 254 } 255 if (scroll) 256 scrollIntoView(this, {left, top, right: left + node.offsetWidth, bottom: top + node.offsetHeight}) 257 }, 258 259 triggerOnKeyDown: methodOp(onKeyDown), 260 triggerOnKeyPress: methodOp(onKeyPress), 261 triggerOnKeyUp: onKeyUp, 262 triggerOnMouseDown: methodOp(onMouseDown), 263 264 execCommand: function(cmd) { 265 if (commands.hasOwnProperty(cmd)) 266 return commands[cmd].call(null, this) 267 }, 268 269 triggerElectric: methodOp(function(text) { triggerElectric(this, text) }), 270 271 findPosH: function(from, amount, unit, visually) { 272 let dir = 1 273 if (amount < 0) { dir = -1; amount = -amount } 274 let cur = clipPos(this.doc, from) 275 for (let i = 0; i < amount; ++i) { 276 cur = findPosH(this.doc, cur, dir, unit, visually) 277 if (cur.hitSide) break 278 } 279 return cur 280 }, 281 282 moveH: methodOp(function(dir, unit) { 283 this.extendSelectionsBy(range => { 284 if (this.display.shift || this.doc.extend || range.empty()) 285 return findPosH(this.doc, range.head, dir, unit, this.options.rtlMoveVisually) 286 else 287 return dir < 0 ? range.from() : range.to() 288 }, sel_move) 289 }), 290 291 deleteH: methodOp(function(dir, unit) { 292 let sel = this.doc.sel, doc = this.doc 293 if (sel.somethingSelected()) 294 doc.replaceSelection("", null, "+delete") 295 else 296 deleteNearSelection(this, range => { 297 let other = findPosH(doc, range.head, dir, unit, false) 298 return dir < 0 ? {from: other, to: range.head} : {from: range.head, to: other} 299 }) 300 }), 301 302 findPosV: function(from, amount, unit, goalColumn) { 303 let dir = 1, x = goalColumn 304 if (amount < 0) { dir = -1; amount = -amount } 305 let cur = clipPos(this.doc, from) 306 for (let i = 0; i < amount; ++i) { 307 let coords = cursorCoords(this, cur, "div") 308 if (x == null) x = coords.left 309 else coords.left = x 310 cur = findPosV(this, coords, dir, unit) 311 if (cur.hitSide) break 312 } 313 return cur 314 }, 315 316 moveV: methodOp(function(dir, unit) { 317 let doc = this.doc, goals = [] 318 let collapse = !this.display.shift && !doc.extend && doc.sel.somethingSelected() 319 doc.extendSelectionsBy(range => { 320 if (collapse) 321 return dir < 0 ? range.from() : range.to() 322 let headPos = cursorCoords(this, range.head, "div") 323 if (range.goalColumn != null) headPos.left = range.goalColumn 324 goals.push(headPos.left) 325 let pos = findPosV(this, headPos, dir, unit) 326 if (unit == "page" && range == doc.sel.primary()) 327 addToScrollTop(this, charCoords(this, pos, "div").top - headPos.top) 328 return pos 329 }, sel_move) 330 if (goals.length) for (let i = 0; i < doc.sel.ranges.length; i++) 331 doc.sel.ranges[i].goalColumn = goals[i] 332 }), 333 334 // Find the word at the given position (as returned by coordsChar). 335 findWordAt: function(pos) { 336 let doc = this.doc, line = getLine(doc, pos.line).text 337 let start = pos.ch, end = pos.ch 338 if (line) { 339 let helper = this.getHelper(pos, "wordChars") 340 if ((pos.sticky == "before" || end == line.length) && start) --start; else ++end 341 let startChar = line.charAt(start) 342 let check = isWordChar(startChar, helper) 343 ? ch => isWordChar(ch, helper) 344 : /\s/.test(startChar) ? ch => /\s/.test(ch) 345 : ch => (!/\s/.test(ch) && !isWordChar(ch)) 346 while (start > 0 && check(line.charAt(start - 1))) --start 347 while (end < line.length && check(line.charAt(end))) ++end 348 } 349 return new Range(Pos(pos.line, start), Pos(pos.line, end)) 350 }, 351 352 toggleOverwrite: function(value) { 353 if (value != null && value == this.state.overwrite) return 354 if (this.state.overwrite = !this.state.overwrite) 355 addClass(this.display.cursorDiv, "CodeMirror-overwrite") 356 else 357 rmClass(this.display.cursorDiv, "CodeMirror-overwrite") 358 359 signal(this, "overwriteToggle", this, this.state.overwrite) 360 }, 361 hasFocus: function() { return this.display.input.getField() == activeElt() }, 362 isReadOnly: function() { return !!(this.options.readOnly || this.doc.cantEdit) }, 363 364 scrollTo: methodOp(function (x, y) { scrollToCoords(this, x, y) }), 365 getScrollInfo: function() { 366 let scroller = this.display.scroller 367 return {left: scroller.scrollLeft, top: scroller.scrollTop, 368 height: scroller.scrollHeight - scrollGap(this) - this.display.barHeight, 369 width: scroller.scrollWidth - scrollGap(this) - this.display.barWidth, 370 clientHeight: displayHeight(this), clientWidth: displayWidth(this)} 371 }, 372 373 scrollIntoView: methodOp(function(range, margin) { 374 if (range == null) { 375 range = {from: this.doc.sel.primary().head, to: null} 376 if (margin == null) margin = this.options.cursorScrollMargin 377 } else if (typeof range == "number") { 378 range = {from: Pos(range, 0), to: null} 379 } else if (range.from == null) { 380 range = {from: range, to: null} 381 } 382 if (!range.to) range.to = range.from 383 range.margin = margin || 0 384 385 if (range.from.line != null) { 386 scrollToRange(this, range) 387 } else { 388 scrollToCoordsRange(this, range.from, range.to, range.margin) 389 } 390 }), 391 392 setSize: methodOp(function(width, height) { 393 let interpret = val => typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val 394 if (width != null) this.display.wrapper.style.width = interpret(width) 395 if (height != null) this.display.wrapper.style.height = interpret(height) 396 if (this.options.lineWrapping) clearLineMeasurementCache(this) 397 let lineNo = this.display.viewFrom 398 this.doc.iter(lineNo, this.display.viewTo, line => { 399 if (line.widgets) for (let i = 0; i < line.widgets.length; i++) 400 if (line.widgets[i].noHScroll) { regLineChange(this, lineNo, "widget"); break } 401 ++lineNo 402 }) 403 this.curOp.forceUpdate = true 404 signal(this, "refresh", this) 405 }), 406 407 operation: function(f){return runInOp(this, f)}, 408 startOperation: function(){return startOperation(this)}, 409 endOperation: function(){return endOperation(this)}, 410 411 refresh: methodOp(function() { 412 let oldHeight = this.display.cachedTextHeight 413 regChange(this) 414 this.curOp.forceUpdate = true 415 clearCaches(this) 416 scrollToCoords(this, this.doc.scrollLeft, this.doc.scrollTop) 417 updateGutterSpace(this) 418 if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5) 419 estimateLineHeights(this) 420 signal(this, "refresh", this) 421 }), 422 423 swapDoc: methodOp(function(doc) { 424 let old = this.doc 425 old.cm = null 426 attachDoc(this, doc) 427 clearCaches(this) 428 this.display.input.reset() 429 scrollToCoords(this, doc.scrollLeft, doc.scrollTop) 430 this.curOp.forceScroll = true 431 signalLater(this, "swapDoc", this, old) 432 return old 433 }), 434 435 getInputField: function(){return this.display.input.getField()}, 436 getWrapperElement: function(){return this.display.wrapper}, 437 getScrollerElement: function(){return this.display.scroller}, 438 getGutterElement: function(){return this.display.gutters} 439 } 440 eventMixin(CodeMirror) 441 442 CodeMirror.registerHelper = function(type, name, value) { 443 if (!helpers.hasOwnProperty(type)) helpers[type] = CodeMirror[type] = {_global: []} 444 helpers[type][name] = value 445 } 446 CodeMirror.registerGlobalHelper = function(type, name, predicate, value) { 447 CodeMirror.registerHelper(type, name, value) 448 helpers[type]._global.push({pred: predicate, val: value}) 449 } 450 } 451 452 // Used for horizontal relative motion. Dir is -1 or 1 (left or 453 // right), unit can be "char", "column" (like char, but doesn't 454 // cross line boundaries), "word" (across next word), or "group" (to 455 // the start of next group of word or non-word-non-whitespace 456 // chars). The visually param controls whether, in right-to-left 457 // text, direction 1 means to move towards the next index in the 458 // string, or towards the character to the right of the current 459 // position. The resulting position will have a hitSide=true 460 // property if it reached the end of the document. 461 function findPosH(doc, pos, dir, unit, visually) { 462 let oldPos = pos 463 let origDir = dir 464 let lineObj = getLine(doc, pos.line) 465 function findNextLine() { 466 let l = pos.line + dir 467 if (l < doc.first || l >= doc.first + doc.size) return false 468 pos = new Pos(l, pos.ch, pos.sticky) 469 return lineObj = getLine(doc, l) 470 } 471 function moveOnce(boundToLine) { 472 let next 473 if (visually) { 474 next = moveVisually(doc.cm, lineObj, pos, dir) 475 } else { 476 next = moveLogically(lineObj, pos, dir) 477 } 478 if (next == null) { 479 if (!boundToLine && findNextLine()) 480 pos = endOfLine(visually, doc.cm, lineObj, pos.line, dir) 481 else 482 return false 483 } else { 484 pos = next 485 } 486 return true 487 } 488 489 if (unit == "char") { 490 moveOnce() 491 } else if (unit == "column") { 492 moveOnce(true) 493 } else if (unit == "word" || unit == "group") { 494 let sawType = null, group = unit == "group" 495 let helper = doc.cm && doc.cm.getHelper(pos, "wordChars") 496 for (let first = true;; first = false) { 497 if (dir < 0 && !moveOnce(!first)) break 498 let cur = lineObj.text.charAt(pos.ch) || "\n" 499 let type = isWordChar(cur, helper) ? "w" 500 : group && cur == "\n" ? "n" 501 : !group || /\s/.test(cur) ? null 502 : "p" 503 if (group && !first && !type) type = "s" 504 if (sawType && sawType != type) { 505 if (dir < 0) {dir = 1; moveOnce(); pos.sticky = "after"} 506 break 507 } 508 509 if (type) sawType = type 510 if (dir > 0 && !moveOnce(!first)) break 511 } 512 } 513 let result = skipAtomic(doc, pos, oldPos, origDir, true) 514 if (equalCursorPos(oldPos, result)) result.hitSide = true 515 return result 516 } 517 518 // For relative vertical movement. Dir may be -1 or 1. Unit can be 519 // "page" or "line". The resulting position will have a hitSide=true 520 // property if it reached the end of the document. 521 function findPosV(cm, pos, dir, unit) { 522 let doc = cm.doc, x = pos.left, y 523 if (unit == "page") { 524 let pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight) 525 let moveAmount = Math.max(pageSize - .5 * textHeight(cm.display), 3) 526 y = (dir > 0 ? pos.bottom : pos.top) + dir * moveAmount 527 528 } else if (unit == "line") { 529 y = dir > 0 ? pos.bottom + 3 : pos.top - 3 530 } 531 let target 532 for (;;) { 533 target = coordsChar(cm, x, y) 534 if (!target.outside) break 535 if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break } 536 y += dir * 5 537 } 538 return target 539 }
Download modules/editor/codemirror/src/edit/methods.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'.