File modules/editor/codemirror/src/edit/mouse_events.min.js

Last commit: Tue May 22 22:39:52 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 { delayBlurEvent, ensureFocus } from "../display/focus.js" 2 import { operation } from "../display/operations.js" 3 import { visibleLines } from "../display/update_lines.js" 4 import { clipPos, cmp, maxPos, minPos, Pos } from "../line/pos.js" 5 import { getLine, lineAtHeight } from "../line/utils_line.js" 6 import { posFromMouse } from "../measurement/position_measurement.js" 7 import { eventInWidget } from "../measurement/widgets.js" 8 import { normalizeSelection, Range, Selection } from "../model/selection.js" 9 import { extendRange, extendSelection, replaceOneSelection, setSelection } from "../model/selection_updates.js" 10 import { captureRightClick, chromeOS, ie, ie_version, mac, webkit } from "../util/browser.js" 11 import { getOrder, getBidiPartAt } from "../util/bidi.js" 12 import { activeElt } from "../util/dom.js" 13 import { e_button, e_defaultPrevented, e_preventDefault, e_target, hasHandler, off, on, signal, signalDOMEvent } from "../util/event.js" 14 import { dragAndDrop } from "../util/feature_detection.js" 15 import { bind, countColumn, findColumn, sel_mouse } from "../util/misc.js" 16 import { addModifierNames } from "../input/keymap.js" 17 import { Pass } from "../util/misc.js" 18 19 import { dispatchKey } from "./key_events.js" 20 import { commands } from "./commands.js" 21 22 const DOUBLECLICK_DELAY = 400 23 24 class PastClick { 25 constructor(time, pos, button) { 26 this.time = time 27 this.pos = pos 28 this.button = button 29 } 30 31 compare(time, pos, button) { 32 return this.time + DOUBLECLICK_DELAY > time && 33 cmp(pos, this.pos) == 0 && button == this.button 34 } 35 } 36 37 let lastClick, lastDoubleClick 38 function clickRepeat(pos, button) { 39 let now = +new Date 40 if (lastDoubleClick && lastDoubleClick.compare(now, pos, button)) { 41 lastClick = lastDoubleClick = null 42 return "triple" 43 } else if (lastClick && lastClick.compare(now, pos, button)) { 44 lastDoubleClick = new PastClick(now, pos, button) 45 lastClick = null 46 return "double" 47 } else { 48 lastClick = new PastClick(now, pos, button) 49 lastDoubleClick = null 50 return "single" 51 } 52 } 53 54 // A mouse down can be a single click, double click, triple click, 55 // start of selection drag, start of text drag, new cursor 56 // (ctrl-click), rectangle drag (alt-drag), or xwin 57 // middle-click-paste. Or it might be a click on something we should 58 // not interfere with, such as a scrollbar or widget. 59 export function onMouseDown(e) { 60 let cm = this, display = cm.display 61 if (signalDOMEvent(cm, e) || display.activeTouch && display.input.supportsTouch()) return 62 display.input.ensurePolled() 63 display.shift = e.shiftKey 64 65 if (eventInWidget(display, e)) { 66 if (!webkit) { 67 // Briefly turn off draggability, to allow widgets to do 68 // normal dragging things. 69 display.scroller.draggable = false 70 setTimeout(() => display.scroller.draggable = true, 100) 71 } 72 return 73 } 74 if (clickInGutter(cm, e)) return 75 let pos = posFromMouse(cm, e), button = e_button(e), repeat = pos ? clickRepeat(pos, button) : "single" 76 window.focus() 77 78 // #3261: make sure, that we're not starting a second selection 79 if (button == 1 && cm.state.selectingText) 80 cm.state.selectingText(e) 81 82 if (pos && handleMappedButton(cm, button, pos, repeat, e)) return 83 84 if (button == 1) { 85 if (pos) leftButtonDown(cm, pos, repeat, e) 86 else if (e_target(e) == display.scroller) e_preventDefault(e) 87 } else if (button == 2) { 88 if (pos) extendSelection(cm.doc, pos) 89 setTimeout(() => display.input.focus(), 20) 90 } else if (button == 3) { 91 if (captureRightClick) onContextMenu(cm, e) 92 else delayBlurEvent(cm) 93 } 94 } 95 96 function handleMappedButton(cm, button, pos, repeat, event) { 97 let name = "Click" 98 if (repeat == "double") name = "Double" + name 99 else if (repeat == "triple") name = "Triple" + name 100 name = (button == 1 ? "Left" : button == 2 ? "Middle" : "Right") + name 101 102 return dispatchKey(cm, addModifierNames(name, event), event, bound => { 103 if (typeof bound == "string") bound = commands[bound] 104 if (!bound) return false 105 let done = false 106 try { 107 if (cm.isReadOnly()) cm.state.suppressEdits = true 108 done = bound(cm, pos) != Pass 109 } finally { 110 cm.state.suppressEdits = false 111 } 112 return done 113 }) 114 } 115 116 function configureMouse(cm, repeat, event) { 117 let option = cm.getOption("configureMouse") 118 let value = option ? option(cm, repeat, event) : {} 119 if (value.unit == null) { 120 let rect = chromeOS ? event.shiftKey && event.metaKey : event.altKey 121 value.unit = rect ? "rectangle" : repeat == "single" ? "char" : repeat == "double" ? "word" : "line" 122 } 123 if (value.extend == null || cm.doc.extend) value.extend = cm.doc.extend || event.shiftKey 124 if (value.addNew == null) value.addNew = mac ? event.metaKey : event.ctrlKey 125 if (value.moveOnDrag == null) value.moveOnDrag = !(mac ? event.altKey : event.ctrlKey) 126 return value 127 } 128 129 function leftButtonDown(cm, pos, repeat, event) { 130 if (ie) setTimeout(bind(ensureFocus, cm), 0) 131 else cm.curOp.focus = activeElt() 132 133 let behavior = configureMouse(cm, repeat, event) 134 135 let sel = cm.doc.sel, contained 136 if (cm.options.dragDrop && dragAndDrop && !cm.isReadOnly() && 137 repeat == "single" && (contained = sel.contains(pos)) > -1 && 138 (cmp((contained = sel.ranges[contained]).from(), pos) < 0 || pos.xRel > 0) && 139 (cmp(contained.to(), pos) > 0 || pos.xRel < 0)) 140 leftButtonStartDrag(cm, event, pos, behavior) 141 else 142 leftButtonSelect(cm, event, pos, behavior) 143 } 144 145 // Start a text drag. When it ends, see if any dragging actually 146 // happen, and treat as a click if it didn't. 147 function leftButtonStartDrag(cm, event, pos, behavior) { 148 let display = cm.display, moved = false 149 let dragEnd = operation(cm, e => { 150 if (webkit) display.scroller.draggable = false 151 cm.state.draggingText = false 152 off(document, "mouseup", dragEnd) 153 off(document, "mousemove", mouseMove) 154 off(display.scroller, "dragstart", dragStart) 155 off(display.scroller, "drop", dragEnd) 156 if (!moved) { 157 e_preventDefault(e) 158 if (!behavior.addNew) 159 extendSelection(cm.doc, pos, null, null, behavior.extend) 160 // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081) 161 if (webkit || ie && ie_version == 9) 162 setTimeout(() => {document.body.focus(); display.input.focus()}, 20) 163 else 164 display.input.focus() 165 } 166 }) 167 let mouseMove = function(e2) { 168 moved = moved || Math.abs(event.clientX - e2.clientX) + Math.abs(event.clientY - e2.clientY) >= 10 169 } 170 let dragStart = () => moved = true 171 // Let the drag handler handle this. 172 if (webkit) display.scroller.draggable = true 173 cm.state.draggingText = dragEnd 174 dragEnd.copy = !behavior.moveOnDrag 175 // IE's approach to draggable 176 if (display.scroller.dragDrop) display.scroller.dragDrop() 177 on(document, "mouseup", dragEnd) 178 on(document, "mousemove", mouseMove) 179 on(display.scroller, "dragstart", dragStart) 180 on(display.scroller, "drop", dragEnd) 181 182 delayBlurEvent(cm) 183 setTimeout(() => display.input.focus(), 20) 184 } 185 186 function rangeForUnit(cm, pos, unit) { 187 if (unit == "char") return new Range(pos, pos) 188 if (unit == "word") return cm.findWordAt(pos) 189 if (unit == "line") return new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))) 190 let result = unit(cm, pos) 191 return new Range(result.from, result.to) 192 } 193 194 // Normal selection, as opposed to text dragging. 195 function leftButtonSelect(cm, event, start, behavior) { 196 let display = cm.display, doc = cm.doc 197 e_preventDefault(event) 198 199 let ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges 200 if (behavior.addNew && !behavior.extend) { 201 ourIndex = doc.sel.contains(start) 202 if (ourIndex > -1) 203 ourRange = ranges[ourIndex] 204 else 205 ourRange = new Range(start, start) 206 } else { 207 ourRange = doc.sel.primary() 208 ourIndex = doc.sel.primIndex 209 } 210 211 if (behavior.unit == "rectangle") { 212 if (!behavior.addNew) ourRange = new Range(start, start) 213 start = posFromMouse(cm, event, true, true) 214 ourIndex = -1 215 } else { 216 let range = rangeForUnit(cm, start, behavior.unit) 217 if (behavior.extend) 218 ourRange = extendRange(ourRange, range.anchor, range.head, behavior.extend) 219 else 220 ourRange = range 221 } 222 223 if (!behavior.addNew) { 224 ourIndex = 0 225 setSelection(doc, new Selection([ourRange], 0), sel_mouse) 226 startSel = doc.sel 227 } else if (ourIndex == -1) { 228 ourIndex = ranges.length 229 setSelection(doc, normalizeSelection(ranges.concat([ourRange]), ourIndex), 230 {scroll: false, origin: "*mouse"}) 231 } else if (ranges.length > 1 && ranges[ourIndex].empty() && behavior.unit == "char" && !behavior.extend) { 232 setSelection(doc, normalizeSelection(ranges.slice(0, ourIndex).concat(ranges.slice(ourIndex + 1)), 0), 233 {scroll: false, origin: "*mouse"}) 234 startSel = doc.sel 235 } else { 236 replaceOneSelection(doc, ourIndex, ourRange, sel_mouse) 237 } 238 239 let lastPos = start 240 function extendTo(pos) { 241 if (cmp(lastPos, pos) == 0) return 242 lastPos = pos 243 244 if (behavior.unit == "rectangle") { 245 let ranges = [], tabSize = cm.options.tabSize 246 let startCol = countColumn(getLine(doc, start.line).text, start.ch, tabSize) 247 let posCol = countColumn(getLine(doc, pos.line).text, pos.ch, tabSize) 248 let left = Math.min(startCol, posCol), right = Math.max(startCol, posCol) 249 for (let line = Math.min(start.line, pos.line), end = Math.min(cm.lastLine(), Math.max(start.line, pos.line)); 250 line <= end; line++) { 251 let text = getLine(doc, line).text, leftPos = findColumn(text, left, tabSize) 252 if (left == right) 253 ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos))) 254 else if (text.length > leftPos) 255 ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize)))) 256 } 257 if (!ranges.length) ranges.push(new Range(start, start)) 258 setSelection(doc, normalizeSelection(startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex), 259 {origin: "*mouse", scroll: false}) 260 cm.scrollIntoView(pos) 261 } else { 262 let oldRange = ourRange 263 let range = rangeForUnit(cm, pos, behavior.unit) 264 let anchor = oldRange.anchor, head 265 if (cmp(range.anchor, anchor) > 0) { 266 head = range.head 267 anchor = minPos(oldRange.from(), range.anchor) 268 } else { 269 head = range.anchor 270 anchor = maxPos(oldRange.to(), range.head) 271 } 272 let ranges = startSel.ranges.slice(0) 273 ranges[ourIndex] = bidiSimplify(cm, new Range(clipPos(doc, anchor), head)) 274 setSelection(doc, normalizeSelection(ranges, ourIndex), sel_mouse) 275 } 276 } 277 278 let editorSize = display.wrapper.getBoundingClientRect() 279 // Used to ensure timeout re-tries don't fire when another extend 280 // happened in the meantime (clearTimeout isn't reliable -- at 281 // least on Chrome, the timeouts still happen even when cleared, 282 // if the clear happens after their scheduled firing time). 283 let counter = 0 284 285 function extend(e) { 286 let curCount = ++counter 287 let cur = posFromMouse(cm, e, true, behavior.unit == "rectangle") 288 if (!cur) return 289 if (cmp(cur, lastPos) != 0) { 290 cm.curOp.focus = activeElt() 291 extendTo(cur) 292 let visible = visibleLines(display, doc) 293 if (cur.line >= visible.to || cur.line < visible.from) 294 setTimeout(operation(cm, () => {if (counter == curCount) extend(e)}), 150) 295 } else { 296 let outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0 297 if (outside) setTimeout(operation(cm, () => { 298 if (counter != curCount) return 299 display.scroller.scrollTop += outside 300 extend(e) 301 }), 50) 302 } 303 } 304 305 function done(e) { 306 cm.state.selectingText = false 307 counter = Infinity 308 e_preventDefault(e) 309 display.input.focus() 310 off(document, "mousemove", move) 311 off(document, "mouseup", up) 312 doc.history.lastSelOrigin = null 313 } 314 315 let move = operation(cm, e => { 316 if (!e_button(e)) done(e) 317 else extend(e) 318 }) 319 let up = operation(cm, done) 320 cm.state.selectingText = up 321 on(document, "mousemove", move) 322 on(document, "mouseup", up) 323 } 324 325 // Used when mouse-selecting to adjust the anchor to the proper side 326 // of a bidi jump depending on the visual position of the head. 327 function bidiSimplify(cm, range) { 328 let {anchor, head} = range, anchorLine = getLine(cm.doc, anchor.line) 329 if (cmp(anchor, head) == 0 && anchor.sticky == head.sticky) return range 330 let order = getOrder(anchorLine) 331 if (!order) return range 332 let index = getBidiPartAt(order, anchor.ch, anchor.sticky), part = order[index] 333 if (part.from != anchor.ch && part.to != anchor.ch) return range 334 let boundary = index + ((part.from == anchor.ch) == (part.level != 1) ? 0 : 1) 335 if (boundary == 0 || boundary == order.length) return range 336 337 // Compute the relative visual position of the head compared to the 338 // anchor (<0 is to the left, >0 to the right) 339 let leftSide 340 if (head.line != anchor.line) { 341 leftSide = (head.line - anchor.line) * (cm.doc.direction == "ltr" ? 1 : -1) > 0 342 } else { 343 let headIndex = getBidiPartAt(order, head.ch, head.sticky) 344 let dir = headIndex - index || (head.ch - anchor.ch) * (part.level == 1 ? -1 : 1) 345 if (headIndex == boundary - 1 || headIndex == boundary) 346 leftSide = dir < 0 347 else 348 leftSide = dir > 0 349 } 350 351 let usePart = order[boundary + (leftSide ? -1 : 0)] 352 let from = leftSide == (usePart.level == 1) 353 let ch = from ? usePart.from : usePart.to, sticky = from ? "after" : "before" 354 return anchor.ch == ch && anchor.sticky == sticky ? range : new Range(new Pos(anchor.line, ch, sticky), head) 355 } 356 357 358 // Determines whether an event happened in the gutter, and fires the 359 // handlers for the corresponding event. 360 function gutterEvent(cm, e, type, prevent) { 361 let mX, mY 362 if (e.touches) { 363 mX = e.touches[0].clientX 364 mY = e.touches[0].clientY 365 } else { 366 try { mX = e.clientX; mY = e.clientY } 367 catch(e) { return false } 368 } 369 if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) return false 370 if (prevent) e_preventDefault(e) 371 372 let display = cm.display 373 let lineBox = display.lineDiv.getBoundingClientRect() 374 375 if (mY > lineBox.bottom || !hasHandler(cm, type)) return e_defaultPrevented(e) 376 mY -= lineBox.top - display.viewOffset 377 378 for (let i = 0; i < cm.options.gutters.length; ++i) { 379 let g = display.gutters.childNodes[i] 380 if (g && g.getBoundingClientRect().right >= mX) { 381 let line = lineAtHeight(cm.doc, mY) 382 let gutter = cm.options.gutters[i] 383 signal(cm, type, cm, line, gutter, e) 384 return e_defaultPrevented(e) 385 } 386 } 387 } 388 389 export function clickInGutter(cm, e) { 390 return gutterEvent(cm, e, "gutterClick", true) 391 } 392 393 // CONTEXT MENU HANDLING 394 395 // To make the context menu work, we need to briefly unhide the 396 // textarea (making it as unobtrusive as possible) to let the 397 // right-click take effect on it. 398 export function onContextMenu(cm, e) { 399 if (eventInWidget(cm.display, e) || contextMenuInGutter(cm, e)) return 400 if (signalDOMEvent(cm, e, "contextmenu")) return 401 cm.display.input.onContextMenu(e) 402 } 403 404 function contextMenuInGutter(cm, e) { 405 if (!hasHandler(cm, "gutterContextMenu")) return false 406 return gutterEvent(cm, e, "gutterContextMenu", false) 407 }
Download modules/editor/codemirror/src/edit/mouse_events.min.js
History Tue, 22 May 2018 22:39:52 +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'.