| OLD | NEW |
| 1 // CodeMirror version 3.12 | |
| 2 // | |
| 3 // CodeMirror is the only global var we claim | 1 // CodeMirror is the only global var we claim |
| 4 window.CodeMirror = (function() { | 2 window.CodeMirror = (function() { |
| 5 "use strict"; | 3 "use strict"; |
| 6 | 4 |
| 7 // BROWSER SNIFFING | 5 // BROWSER SNIFFING |
| 8 | 6 |
| 9 // Crude, but necessary to handle a number of hard-to-feature-detect | 7 // Crude, but necessary to handle a number of hard-to-feature-detect |
| 10 // bugs and behavior differences. | 8 // bugs and behavior differences. |
| 11 var gecko = /gecko\/\d/i.test(navigator.userAgent); | 9 var gecko = /gecko\/\d/i.test(navigator.userAgent); |
| 12 var ie = /MSIE \d/.test(navigator.userAgent); | 10 var ie = /MSIE \d/.test(navigator.userAgent); |
| (...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 92 // DISPLAY CONSTRUCTOR | 90 // DISPLAY CONSTRUCTOR |
| 93 | 91 |
| 94 function makeDisplay(place, docStart) { | 92 function makeDisplay(place, docStart) { |
| 95 var d = {}; | 93 var d = {}; |
| 96 | 94 |
| 97 var input = d.input = elt("textarea", null, null, "position: absolute; paddi
ng: 0; width: 1px; height: 1em; outline: none; font-size: 4px;"); | 95 var input = d.input = elt("textarea", null, null, "position: absolute; paddi
ng: 0; width: 1px; height: 1em; outline: none; font-size: 4px;"); |
| 98 if (webkit) input.style.width = "1000px"; | 96 if (webkit) input.style.width = "1000px"; |
| 99 else input.setAttribute("wrap", "off"); | 97 else input.setAttribute("wrap", "off"); |
| 100 // if border: 0; -- iOS fails to open keyboard (issue #1287) | 98 // if border: 0; -- iOS fails to open keyboard (issue #1287) |
| 101 if (ios) input.style.border = "1px solid black"; | 99 if (ios) input.style.border = "1px solid black"; |
| 102 input.setAttribute("autocorrect", "off"); input.setAttribute("autocapitalize
", "off"); | 100 input.setAttribute("autocorrect", "off"); input.setAttribute("autocapitalize
", "off"); input.setAttribute("spellcheck", "false"); |
| 103 | 101 |
| 104 // Wraps and hides input textarea | 102 // Wraps and hides input textarea |
| 105 d.inputDiv = elt("div", [input], null, "overflow: hidden; position: relative
; width: 3px; height: 0px;"); | 103 d.inputDiv = elt("div", [input], null, "overflow: hidden; position: relative
; width: 3px; height: 0px;"); |
| 106 // The actual fake scrollbars. | 104 // The actual fake scrollbars. |
| 107 d.scrollbarH = elt("div", [elt("div", null, null, "height: 1px")], "CodeMirr
or-hscrollbar"); | 105 d.scrollbarH = elt("div", [elt("div", null, null, "height: 1px")], "CodeMirr
or-hscrollbar"); |
| 108 d.scrollbarV = elt("div", [elt("div", null, null, "width: 1px")], "CodeMirro
r-vscrollbar"); | 106 d.scrollbarV = elt("div", [elt("div", null, null, "width: 1px")], "CodeMirro
r-vscrollbar"); |
| 109 d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler"); | 107 d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler"); |
| 108 d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler"); |
| 110 // DIVs containing the selection and the actual code | 109 // DIVs containing the selection and the actual code |
| 111 d.lineDiv = elt("div"); | 110 d.lineDiv = elt("div", null, "CodeMirror-code"); |
| 112 d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1"); | 111 d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1"); |
| 113 // Blinky cursor, and element used to ensure cursor fits at the end of a lin
e | 112 // Blinky cursor, and element used to ensure cursor fits at the end of a lin
e |
| 114 d.cursor = elt("div", "\u00a0", "CodeMirror-cursor"); | 113 d.cursor = elt("div", "\u00a0", "CodeMirror-cursor"); |
| 115 // Secondary cursor, shown when on a 'jump' in bi-directional text | 114 // Secondary cursor, shown when on a 'jump' in bi-directional text |
| 116 d.otherCursor = elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondary
cursor"); | 115 d.otherCursor = elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondary
cursor"); |
| 117 // Used to measure text size | 116 // Used to measure text size |
| 118 d.measure = elt("div", null, "CodeMirror-measure"); | 117 d.measure = elt("div", null, "CodeMirror-measure"); |
| 119 // Wraps everything that needs to exist inside the vertically-padded coordin
ate system | 118 // Wraps everything that needs to exist inside the vertically-padded coordin
ate system |
| 120 d.lineSpace = elt("div", [d.measure, d.selectionDiv, d.lineDiv, d.cursor, d.
otherCursor], | 119 d.lineSpace = elt("div", [d.measure, d.selectionDiv, d.lineDiv, d.cursor, d.
otherCursor], |
| 121 null, "position: relative; outline: none"); | 120 null, "position: relative; outline: none"); |
| 122 // Moved around its parent to cover visible view | 121 // Moved around its parent to cover visible view |
| 123 d.mover = elt("div", [elt("div", [d.lineSpace], "CodeMirror-lines")], null,
"position: relative"); | 122 d.mover = elt("div", [elt("div", [d.lineSpace], "CodeMirror-lines")], null,
"position: relative"); |
| 124 // Set to the height of the text, causes scrolling | 123 // Set to the height of the text, causes scrolling |
| 125 d.sizer = elt("div", [d.mover], "CodeMirror-sizer"); | 124 d.sizer = elt("div", [d.mover], "CodeMirror-sizer"); |
| 126 // D is needed because behavior of elts with overflow: auto and padding is i
nconsistent across browsers | 125 // D is needed because behavior of elts with overflow: auto and padding is i
nconsistent across browsers |
| 127 d.heightForcer = elt("div", null, null, "position: absolute; height: " + scr
ollerCutOff + "px; width: 1px;"); | 126 d.heightForcer = elt("div", null, null, "position: absolute; height: " + scr
ollerCutOff + "px; width: 1px;"); |
| 128 // Will contain the gutters, if any | 127 // Will contain the gutters, if any |
| 129 d.gutters = elt("div", null, "CodeMirror-gutters"); | 128 d.gutters = elt("div", null, "CodeMirror-gutters"); |
| 130 d.lineGutter = null; | 129 d.lineGutter = null; |
| 131 // Helper element to properly size the gutter backgrounds | |
| 132 var scrollerInner = elt("div", [d.sizer, d.heightForcer, d.gutters], null, "
position: relative; min-height: 100%"); | |
| 133 // Provides scrolling | 130 // Provides scrolling |
| 134 d.scroller = elt("div", [scrollerInner], "CodeMirror-scroll"); | 131 d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-sc
roll"); |
| 135 d.scroller.setAttribute("tabIndex", "-1"); | 132 d.scroller.setAttribute("tabIndex", "-1"); |
| 136 // The element in which the editor lives. | 133 // The element in which the editor lives. |
| 137 d.wrapper = elt("div", [d.inputDiv, d.scrollbarH, d.scrollbarV, | 134 d.wrapper = elt("div", [d.inputDiv, d.scrollbarH, d.scrollbarV, |
| 138 d.scrollbarFiller, d.scroller], "CodeMirror"); | 135 d.scrollbarFiller, d.gutterFiller, d.scroller], "Cod
eMirror"); |
| 139 // Work around IE7 z-index bug | 136 // Work around IE7 z-index bug |
| 140 if (ie_lt8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0
; } | 137 if (ie_lt8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0
; } |
| 141 if (place.appendChild) place.appendChild(d.wrapper); else place(d.wrapper); | 138 if (place.appendChild) place.appendChild(d.wrapper); else place(d.wrapper); |
| 142 | 139 |
| 143 // Needed to hide big blue blinking cursor on Mobile Safari | 140 // Needed to hide big blue blinking cursor on Mobile Safari |
| 144 if (ios) input.style.width = "0px"; | 141 if (ios) input.style.width = "0px"; |
| 145 if (!webkit) d.scroller.draggable = true; | 142 if (!webkit) d.scroller.draggable = true; |
| 146 // Needed to handle Tab key in KHTML | 143 // Needed to handle Tab key in KHTML |
| 147 if (khtml) { d.inputDiv.style.height = "1px"; d.inputDiv.style.position = "a
bsolute"; } | 144 if (khtml) { d.inputDiv.style.height = "1px"; d.inputDiv.style.position = "a
bsolute"; } |
| 148 // Need to set a minimum width to see the scrollbar on IE7 (but must not set
it on IE8). | 145 // Need to set a minimum width to see the scrollbar on IE7 (but must not set
it on IE8). |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 207 if (cm.options.lineWrapping) { | 204 if (cm.options.lineWrapping) { |
| 208 cm.display.wrapper.className += " CodeMirror-wrap"; | 205 cm.display.wrapper.className += " CodeMirror-wrap"; |
| 209 cm.display.sizer.style.minWidth = ""; | 206 cm.display.sizer.style.minWidth = ""; |
| 210 } else { | 207 } else { |
| 211 cm.display.wrapper.className = cm.display.wrapper.className.replace(" Code
Mirror-wrap", ""); | 208 cm.display.wrapper.className = cm.display.wrapper.className.replace(" Code
Mirror-wrap", ""); |
| 212 computeMaxLength(cm); | 209 computeMaxLength(cm); |
| 213 } | 210 } |
| 214 estimateLineHeights(cm); | 211 estimateLineHeights(cm); |
| 215 regChange(cm); | 212 regChange(cm); |
| 216 clearCaches(cm); | 213 clearCaches(cm); |
| 217 setTimeout(function(){updateScrollbars(cm.display, cm.doc.height);}, 100); | 214 setTimeout(function(){updateScrollbars(cm);}, 100); |
| 218 } | 215 } |
| 219 | 216 |
| 220 function estimateHeight(cm) { | 217 function estimateHeight(cm) { |
| 221 var th = textHeight(cm.display), wrapping = cm.options.lineWrapping; | 218 var th = textHeight(cm.display), wrapping = cm.options.lineWrapping; |
| 222 var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / char
Width(cm.display) - 3); | 219 var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / char
Width(cm.display) - 3); |
| 223 return function(line) { | 220 return function(line) { |
| 224 if (lineIsHidden(cm.doc, line)) | 221 if (lineIsHidden(cm.doc, line)) |
| 225 return 0; | 222 return 0; |
| 226 else if (wrapping) | 223 else if (wrapping) |
| 227 return (Math.ceil(line.text.length / perLine) || 1) * th; | 224 return (Math.ceil(line.text.length / perLine) || 1) * th; |
| (...skipping 18 matching lines...) Expand all Loading... |
| 246 | 243 |
| 247 function themeChanged(cm) { | 244 function themeChanged(cm) { |
| 248 cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s
-\S+/g, "") + | 245 cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s
-\S+/g, "") + |
| 249 cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-"); | 246 cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-"); |
| 250 clearCaches(cm); | 247 clearCaches(cm); |
| 251 } | 248 } |
| 252 | 249 |
| 253 function guttersChanged(cm) { | 250 function guttersChanged(cm) { |
| 254 updateGutters(cm); | 251 updateGutters(cm); |
| 255 regChange(cm); | 252 regChange(cm); |
| 253 setTimeout(function(){alignHorizontally(cm);}, 20); |
| 256 } | 254 } |
| 257 | 255 |
| 258 function updateGutters(cm) { | 256 function updateGutters(cm) { |
| 259 var gutters = cm.display.gutters, specs = cm.options.gutters; | 257 var gutters = cm.display.gutters, specs = cm.options.gutters; |
| 260 removeChildren(gutters); | 258 removeChildren(gutters); |
| 261 for (var i = 0; i < specs.length; ++i) { | 259 for (var i = 0; i < specs.length; ++i) { |
| 262 var gutterClass = specs[i]; | 260 var gutterClass = specs[i]; |
| 263 var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + gut
terClass)); | 261 var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + gut
terClass)); |
| 264 if (gutterClass == "CodeMirror-linenumbers") { | 262 if (gutterClass == "CodeMirror-linenumbers") { |
| 265 cm.display.lineGutter = gElt; | 263 cm.display.lineGutter = gElt; |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 312 } | 310 } |
| 313 } | 311 } |
| 314 if (!found && options.lineNumbers) | 312 if (!found && options.lineNumbers) |
| 315 options.gutters.push("CodeMirror-linenumbers"); | 313 options.gutters.push("CodeMirror-linenumbers"); |
| 316 } | 314 } |
| 317 | 315 |
| 318 // SCROLLBARS | 316 // SCROLLBARS |
| 319 | 317 |
| 320 // Re-synchronize the fake scrollbars with the actual size of the | 318 // Re-synchronize the fake scrollbars with the actual size of the |
| 321 // content. Optionally force a scrollTop. | 319 // content. Optionally force a scrollTop. |
| 322 function updateScrollbars(d /* display */, docHeight) { | 320 function updateScrollbars(cm) { |
| 321 var d = cm.display, docHeight = cm.doc.height; |
| 323 var totalHeight = docHeight + paddingVert(d); | 322 var totalHeight = docHeight + paddingVert(d); |
| 324 d.sizer.style.minHeight = d.heightForcer.style.top = totalHeight + "px"; | 323 d.sizer.style.minHeight = d.heightForcer.style.top = totalHeight + "px"; |
| 324 d.gutters.style.height = Math.max(totalHeight, d.scroller.clientHeight - scr
ollerCutOff) + "px"; |
| 325 var scrollHeight = Math.max(totalHeight, d.scroller.scrollHeight); | 325 var scrollHeight = Math.max(totalHeight, d.scroller.scrollHeight); |
| 326 var needsH = d.scroller.scrollWidth > d.scroller.clientWidth; | 326 var needsH = d.scroller.scrollWidth > d.scroller.clientWidth; |
| 327 var needsV = scrollHeight > d.scroller.clientHeight; | 327 var needsV = scrollHeight > d.scroller.clientHeight; |
| 328 if (needsV) { | 328 if (needsV) { |
| 329 d.scrollbarV.style.display = "block"; | 329 d.scrollbarV.style.display = "block"; |
| 330 d.scrollbarV.style.bottom = needsH ? scrollbarWidth(d.measure) + "px" : "0
"; | 330 d.scrollbarV.style.bottom = needsH ? scrollbarWidth(d.measure) + "px" : "0
"; |
| 331 d.scrollbarV.firstChild.style.height = | 331 d.scrollbarV.firstChild.style.height = |
| 332 (scrollHeight - d.scroller.clientHeight + d.scrollbarV.clientHeight) + "
px"; | 332 (scrollHeight - d.scroller.clientHeight + d.scrollbarV.clientHeight) + "
px"; |
| 333 } else d.scrollbarV.style.display = ""; | 333 } else d.scrollbarV.style.display = ""; |
| 334 if (needsH) { | 334 if (needsH) { |
| 335 d.scrollbarH.style.display = "block"; | 335 d.scrollbarH.style.display = "block"; |
| 336 d.scrollbarH.style.right = needsV ? scrollbarWidth(d.measure) + "px" : "0"
; | 336 d.scrollbarH.style.right = needsV ? scrollbarWidth(d.measure) + "px" : "0"
; |
| 337 d.scrollbarH.firstChild.style.width = | 337 d.scrollbarH.firstChild.style.width = |
| 338 (d.scroller.scrollWidth - d.scroller.clientWidth + d.scrollbarH.clientWi
dth) + "px"; | 338 (d.scroller.scrollWidth - d.scroller.clientWidth + d.scrollbarH.clientWi
dth) + "px"; |
| 339 } else d.scrollbarH.style.display = ""; | 339 } else d.scrollbarH.style.display = ""; |
| 340 if (needsH && needsV) { | 340 if (needsH && needsV) { |
| 341 d.scrollbarFiller.style.display = "block"; | 341 d.scrollbarFiller.style.display = "block"; |
| 342 d.scrollbarFiller.style.height = d.scrollbarFiller.style.width = scrollbar
Width(d.measure) + "px"; | 342 d.scrollbarFiller.style.height = d.scrollbarFiller.style.width = scrollbar
Width(d.measure) + "px"; |
| 343 } else d.scrollbarFiller.style.display = ""; | 343 } else d.scrollbarFiller.style.display = ""; |
| 344 if (needsH && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutte
r) { |
| 345 d.gutterFiller.style.display = "block"; |
| 346 d.gutterFiller.style.height = scrollbarWidth(d.measure) + "px"; |
| 347 d.gutterFiller.style.width = d.gutters.offsetWidth + "px"; |
| 348 } else d.gutterFiller.style.display = ""; |
| 344 | 349 |
| 345 if (mac_geLion && scrollbarWidth(d.measure) === 0) | 350 if (mac_geLion && scrollbarWidth(d.measure) === 0) |
| 346 d.scrollbarV.style.minWidth = d.scrollbarH.style.minHeight = mac_geMountai
nLion ? "18px" : "12px"; | 351 d.scrollbarV.style.minWidth = d.scrollbarH.style.minHeight = mac_geMountai
nLion ? "18px" : "12px"; |
| 347 } | 352 } |
| 348 | 353 |
| 349 function visibleLines(display, doc, viewPort) { | 354 function visibleLines(display, doc, viewPort) { |
| 350 var top = display.scroller.scrollTop, height = display.wrapper.clientHeight; | 355 var top = display.scroller.scrollTop, height = display.wrapper.clientHeight; |
| 351 if (typeof viewPort == "number") top = viewPort; | 356 if (typeof viewPort == "number") top = viewPort; |
| 352 else if (viewPort) {top = viewPort.top; height = viewPort.bottom - viewPort.
top;} | 357 else if (viewPort) {top = viewPort.top; height = viewPort.bottom - viewPort.
top;} |
| 353 top = Math.floor(top - paddingTop(display)); | 358 top = Math.floor(top - paddingTop(display)); |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 392 function compensateForHScroll(display) { | 397 function compensateForHScroll(display) { |
| 393 return getRect(display.scroller).left - getRect(display.sizer).left; | 398 return getRect(display.scroller).left - getRect(display.sizer).left; |
| 394 } | 399 } |
| 395 | 400 |
| 396 // DISPLAY DRAWING | 401 // DISPLAY DRAWING |
| 397 | 402 |
| 398 function updateDisplay(cm, changes, viewPort) { | 403 function updateDisplay(cm, changes, viewPort) { |
| 399 var oldFrom = cm.display.showingFrom, oldTo = cm.display.showingTo, updated; | 404 var oldFrom = cm.display.showingFrom, oldTo = cm.display.showingTo, updated; |
| 400 var visible = visibleLines(cm.display, cm.doc, viewPort); | 405 var visible = visibleLines(cm.display, cm.doc, viewPort); |
| 401 for (;;) { | 406 for (;;) { |
| 402 if (updateDisplayInner(cm, changes, visible)) { | 407 if (!updateDisplayInner(cm, changes, visible)) break; |
| 403 updated = true; | 408 updated = true; |
| 404 signalLater(cm, "update", cm); | |
| 405 if (cm.display.showingFrom != oldFrom || cm.display.showingTo != oldTo) | |
| 406 signalLater(cm, "viewportChange", cm, cm.display.showingFrom, cm.displ
ay.showingTo); | |
| 407 } else break; | |
| 408 updateSelection(cm); | 409 updateSelection(cm); |
| 409 updateScrollbars(cm.display, cm.doc.height); | 410 updateScrollbars(cm); |
| 410 | 411 |
| 411 // Clip forced viewport to actual scrollable area | 412 // Clip forced viewport to actual scrollable area |
| 412 if (viewPort) | 413 if (viewPort) |
| 413 viewPort = Math.min(cm.display.scroller.scrollHeight - cm.display.scroll
er.clientHeight, | 414 viewPort = Math.min(cm.display.scroller.scrollHeight - cm.display.scroll
er.clientHeight, |
| 414 typeof viewPort == "number" ? viewPort : viewPort.to
p); | 415 typeof viewPort == "number" ? viewPort : viewPort.to
p); |
| 415 visible = visibleLines(cm.display, cm.doc, viewPort); | 416 visible = visibleLines(cm.display, cm.doc, viewPort); |
| 416 if (visible.from >= cm.display.showingFrom && visible.to <= cm.display.sho
wingTo) | 417 if (visible.from >= cm.display.showingFrom && visible.to <= cm.display.sho
wingTo) |
| 417 break; | 418 break; |
| 418 changes = []; | 419 changes = []; |
| 419 } | 420 } |
| 420 | 421 |
| 422 if (updated) { |
| 423 signalLater(cm, "update", cm); |
| 424 if (cm.display.showingFrom != oldFrom || cm.display.showingTo != oldTo) |
| 425 signalLater(cm, "viewportChange", cm, cm.display.showingFrom, cm.display
.showingTo); |
| 426 } |
| 421 return updated; | 427 return updated; |
| 422 } | 428 } |
| 423 | 429 |
| 424 // Uses a set of changes plus the current scroll position to | 430 // Uses a set of changes plus the current scroll position to |
| 425 // determine which DOM updates have to be made, and makes the | 431 // determine which DOM updates have to be made, and makes the |
| 426 // updates. | 432 // updates. |
| 427 function updateDisplayInner(cm, changes, visible) { | 433 function updateDisplayInner(cm, changes, visible) { |
| 428 var display = cm.display, doc = cm.doc; | 434 var display = cm.display, doc = cm.doc; |
| 429 if (!display.wrapper.clientWidth) { | 435 if (!display.wrapper.clientWidth) { |
| 430 display.showingFrom = display.showingTo = doc.first; | 436 display.showingFrom = display.showingTo = doc.first; |
| (...skipping 540 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 971 top: r.top, bottom: r.bottom}; | 977 top: r.top, bottom: r.bottom}; |
| 972 } | 978 } |
| 973 | 979 |
| 974 function findCachedMeasurement(cm, line) { | 980 function findCachedMeasurement(cm, line) { |
| 975 var cache = cm.display.measureLineCache; | 981 var cache = cm.display.measureLineCache; |
| 976 for (var i = 0; i < cache.length; ++i) { | 982 for (var i = 0; i < cache.length; ++i) { |
| 977 var memo = cache[i]; | 983 var memo = cache[i]; |
| 978 if (memo.text == line.text && memo.markedSpans == line.markedSpans && | 984 if (memo.text == line.text && memo.markedSpans == line.markedSpans && |
| 979 cm.display.scroller.clientWidth == memo.width && | 985 cm.display.scroller.clientWidth == memo.width && |
| 980 memo.classes == line.textClass + "|" + line.bgClass + "|" + line.wrapC
lass) | 986 memo.classes == line.textClass + "|" + line.bgClass + "|" + line.wrapC
lass) |
| 981 return memo.measure; | 987 return memo; |
| 982 } | 988 } |
| 983 } | 989 } |
| 984 | 990 |
| 991 function clearCachedMeasurement(cm, line) { |
| 992 var exists = findCachedMeasurement(cm, line); |
| 993 if (exists) exists.text = exists.measure = exists.markedSpans = null; |
| 994 } |
| 995 |
| 985 function measureLine(cm, line) { | 996 function measureLine(cm, line) { |
| 986 // First look in the cache | 997 // First look in the cache |
| 987 var measure = findCachedMeasurement(cm, line); | 998 var cached = findCachedMeasurement(cm, line); |
| 988 if (!measure) { | 999 if (cached) return cached.measure; |
| 989 // Failing that, recompute and store result in cache | 1000 |
| 990 measure = measureLineInner(cm, line); | 1001 // Failing that, recompute and store result in cache |
| 991 var cache = cm.display.measureLineCache; | 1002 var measure = measureLineInner(cm, line); |
| 992 var memo = {text: line.text, width: cm.display.scroller.clientWidth, | 1003 var cache = cm.display.measureLineCache; |
| 993 markedSpans: line.markedSpans, measure: measure, | 1004 var memo = {text: line.text, width: cm.display.scroller.clientWidth, |
| 994 classes: line.textClass + "|" + line.bgClass + "|" + line.wrap
Class}; | 1005 markedSpans: line.markedSpans, measure: measure, |
| 995 if (cache.length == 16) cache[++cm.display.measureLineCachePos % 16] = mem
o; | 1006 classes: line.textClass + "|" + line.bgClass + "|" + line.wrapCl
ass}; |
| 996 else cache.push(memo); | 1007 if (cache.length == 16) cache[++cm.display.measureLineCachePos % 16] = memo; |
| 997 } | 1008 else cache.push(memo); |
| 998 return measure; | 1009 return measure; |
| 999 } | 1010 } |
| 1000 | 1011 |
| 1001 function measureLineInner(cm, line) { | 1012 function measureLineInner(cm, line) { |
| 1002 var display = cm.display, measure = emptyArray(line.text.length); | 1013 var display = cm.display, measure = emptyArray(line.text.length); |
| 1003 var pre = lineContent(cm, line, measure); | 1014 var pre = lineContent(cm, line, measure); |
| 1004 | 1015 |
| 1005 // IE does not cache element positions of inline elements between | 1016 // IE does not cache element positions of inline elements between |
| 1006 // calls to getBoundingClientRect. This makes the loop below, | 1017 // calls to getBoundingClientRect. This makes the loop below, |
| 1007 // which gathers the positions of all the characters on the line, | 1018 // which gathers the positions of all the characters on the line, |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1063 return data; | 1074 return data; |
| 1064 } | 1075 } |
| 1065 | 1076 |
| 1066 function measureLineWidth(cm, line) { | 1077 function measureLineWidth(cm, line) { |
| 1067 var hasBadSpan = false; | 1078 var hasBadSpan = false; |
| 1068 if (line.markedSpans) for (var i = 0; i < line.markedSpans; ++i) { | 1079 if (line.markedSpans) for (var i = 0; i < line.markedSpans; ++i) { |
| 1069 var sp = line.markedSpans[i]; | 1080 var sp = line.markedSpans[i]; |
| 1070 if (sp.collapsed && (sp.to == null || sp.to == line.text.length)) hasBadSp
an = true; | 1081 if (sp.collapsed && (sp.to == null || sp.to == line.text.length)) hasBadSp
an = true; |
| 1071 } | 1082 } |
| 1072 var cached = !hasBadSpan && findCachedMeasurement(cm, line); | 1083 var cached = !hasBadSpan && findCachedMeasurement(cm, line); |
| 1073 if (cached) return measureChar(cm, line, line.text.length, cached).right; | 1084 if (cached) return measureChar(cm, line, line.text.length, cached.measure).r
ight; |
| 1074 | 1085 |
| 1075 var pre = lineContent(cm, line); | 1086 var pre = lineContent(cm, line); |
| 1076 var end = pre.appendChild(zeroWidthElement(cm.display.measure)); | 1087 var end = pre.appendChild(zeroWidthElement(cm.display.measure)); |
| 1077 removeChildrenAndAdd(cm.display.measure, pre); | 1088 removeChildrenAndAdd(cm.display.measure, pre); |
| 1078 return getRect(end).right - getRect(cm.display.lineDiv).left; | 1089 return getRect(end).right - getRect(cm.display.lineDiv).left; |
| 1079 } | 1090 } |
| 1080 | 1091 |
| 1081 function clearCaches(cm) { | 1092 function clearCaches(cm) { |
| 1082 cm.display.measureLineCache.length = cm.display.measureLineCachePos = 0; | 1093 cm.display.measureLineCache.length = cm.display.measureLineCachePos = 0; |
| 1083 cm.display.cachedCharWidth = cm.display.cachedTextHeight = null; | 1094 cm.display.cachedCharWidth = cm.display.cachedTextHeight = null; |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1131 } | 1142 } |
| 1132 | 1143 |
| 1133 function cursorCoords(cm, pos, context, lineObj, measurement) { | 1144 function cursorCoords(cm, pos, context, lineObj, measurement) { |
| 1134 lineObj = lineObj || getLine(cm.doc, pos.line); | 1145 lineObj = lineObj || getLine(cm.doc, pos.line); |
| 1135 if (!measurement) measurement = measureLine(cm, lineObj); | 1146 if (!measurement) measurement = measureLine(cm, lineObj); |
| 1136 function get(ch, right) { | 1147 function get(ch, right) { |
| 1137 var m = measureChar(cm, lineObj, ch, measurement); | 1148 var m = measureChar(cm, lineObj, ch, measurement); |
| 1138 if (right) m.left = m.right; else m.right = m.left; | 1149 if (right) m.left = m.right; else m.right = m.left; |
| 1139 return intoCoordSystem(cm, lineObj, m, context); | 1150 return intoCoordSystem(cm, lineObj, m, context); |
| 1140 } | 1151 } |
| 1152 function getBidi(ch, partPos) { |
| 1153 var part = order[partPos], right = part.level % 2; |
| 1154 if (ch == bidiLeft(part) && partPos && part.level < order[partPos - 1].lev
el) { |
| 1155 part = order[--partPos]; |
| 1156 ch = bidiRight(part) - (part.level % 2 ? 0 : 1); |
| 1157 right = true; |
| 1158 } else if (ch == bidiRight(part) && partPos < order.length - 1 && part.lev
el < order[partPos + 1].level) { |
| 1159 part = order[++partPos]; |
| 1160 ch = bidiLeft(part) - part.level % 2; |
| 1161 right = false; |
| 1162 } |
| 1163 if (right && ch == part.to && ch > part.from) return get(ch - 1); |
| 1164 return get(ch, right); |
| 1165 } |
| 1141 var order = getOrder(lineObj), ch = pos.ch; | 1166 var order = getOrder(lineObj), ch = pos.ch; |
| 1142 if (!order) return get(ch); | 1167 if (!order) return get(ch); |
| 1143 var main, other, linedir = order[0].level; | 1168 var partPos = getBidiPartAt(order, ch); |
| 1144 for (var i = 0; i < order.length; ++i) { | 1169 var val = getBidi(ch, partPos); |
| 1145 var part = order[i], rtl = part.level % 2, nb, here; | 1170 if (bidiOther != null) val.other = getBidi(ch, bidiOther); |
| 1146 if (part.from < ch && part.to > ch) return get(ch, rtl); | 1171 return val; |
| 1147 var left = rtl ? part.to : part.from, right = rtl ? part.from : part.to; | |
| 1148 if (left == ch) { | |
| 1149 // IE returns bogus offsets and widths for edges where the | |
| 1150 // direction flips, but only for the side with the lower | |
| 1151 // level. So we try to use the side with the higher level. | |
| 1152 if (i && part.level < (nb = order[i-1]).level) here = get(nb.level % 2 ?
nb.from : nb.to - 1, true); | |
| 1153 else here = get(rtl && part.from != part.to ? ch - 1 : ch); | |
| 1154 if (rtl == linedir) main = here; else other = here; | |
| 1155 } else if (right == ch) { | |
| 1156 var nb = i < order.length - 1 && order[i+1]; | |
| 1157 if (!rtl && nb && nb.from == nb.to) continue; | |
| 1158 if (nb && part.level < nb.level) here = get(nb.level % 2 ? nb.to - 1 : n
b.from); | |
| 1159 else here = get(rtl ? ch : ch - 1, true); | |
| 1160 if (rtl == linedir) main = here; else other = here; | |
| 1161 } | |
| 1162 } | |
| 1163 if (linedir && !ch) other = get(order[0].to - 1); | |
| 1164 if (!main) return other; | |
| 1165 if (other) main.other = other; | |
| 1166 return main; | |
| 1167 } | 1172 } |
| 1168 | 1173 |
| 1169 function PosMaybeOutside(line, ch, outside) { | 1174 function PosMaybeOutside(line, ch, outside) { |
| 1170 var pos = new Pos(line, ch); | 1175 var pos = new Pos(line, ch); |
| 1171 if (outside) pos.outside = true; | 1176 if (outside) pos.outside = true; |
| 1172 return pos; | 1177 return pos; |
| 1173 } | 1178 } |
| 1174 | 1179 |
| 1175 // Coords must be lineSpace-local | 1180 // Coords must be lineSpace-local |
| 1176 function coordsChar(cm, x, y) { | 1181 function coordsChar(cm, x, y) { |
| (...skipping 324 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1501 | 1506 |
| 1502 on(d.scroller, "mousewheel", function(e){onScrollWheel(cm, e);}); | 1507 on(d.scroller, "mousewheel", function(e){onScrollWheel(cm, e);}); |
| 1503 on(d.scroller, "DOMMouseScroll", function(e){onScrollWheel(cm, e);}); | 1508 on(d.scroller, "DOMMouseScroll", function(e){onScrollWheel(cm, e);}); |
| 1504 | 1509 |
| 1505 function reFocus() { if (cm.state.focused) setTimeout(bind(focusInput, cm),
0); } | 1510 function reFocus() { if (cm.state.focused) setTimeout(bind(focusInput, cm),
0); } |
| 1506 on(d.scrollbarH, "mousedown", reFocus); | 1511 on(d.scrollbarH, "mousedown", reFocus); |
| 1507 on(d.scrollbarV, "mousedown", reFocus); | 1512 on(d.scrollbarV, "mousedown", reFocus); |
| 1508 // Prevent wrapper from ever scrolling | 1513 // Prevent wrapper from ever scrolling |
| 1509 on(d.wrapper, "scroll", function() { d.wrapper.scrollTop = d.wrapper.scrollL
eft = 0; }); | 1514 on(d.wrapper, "scroll", function() { d.wrapper.scrollTop = d.wrapper.scrollL
eft = 0; }); |
| 1510 | 1515 |
| 1516 var resizeTimer = new Delayed(); |
| 1511 function onResize() { | 1517 function onResize() { |
| 1512 // Might be a text scaling operation, clear size caches. | 1518 resizeTimer.set(function() { |
| 1513 d.cachedCharWidth = d.cachedTextHeight = null; | 1519 // Might be a text scaling operation, clear size caches. |
| 1514 clearCaches(cm); | 1520 d.cachedCharWidth = d.cachedTextHeight = null; |
| 1515 runInOp(cm, bind(regChange, cm)); | 1521 clearCaches(cm); |
| 1522 runInOp(cm, bind(regChange, cm)); |
| 1523 }, 200); |
| 1516 } | 1524 } |
| 1517 on(window, "resize", onResize); | 1525 on(window, "resize", onResize); |
| 1518 // Above handler holds on to the editor and its data structures. | 1526 // Above handler holds on to the editor and its data structures. |
| 1519 // Here we poll to unregister it when the editor is no longer in | 1527 // Here we poll to unregister it when the editor is no longer in |
| 1520 // the document, so that it can be garbage-collected. | 1528 // the document, so that it can be garbage-collected. |
| 1521 function unregister() { | 1529 function unregister() { |
| 1522 for (var p = d.wrapper.parentNode; p && p != document.body; p = p.parentNo
de) {} | 1530 for (var p = d.wrapper.parentNode; p && p != document.body; p = p.parentNo
de) {} |
| 1523 if (p) setTimeout(unregister, 5000); | 1531 if (p) setTimeout(unregister, 5000); |
| 1524 else off(window, "resize", onResize); | 1532 else off(window, "resize", onResize); |
| 1525 } | 1533 } |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1580 n.parentNode == display.sizer && n != display.mover) return true; | 1588 n.parentNode == display.sizer && n != display.mover) return true; |
| 1581 } | 1589 } |
| 1582 } | 1590 } |
| 1583 | 1591 |
| 1584 function posFromMouse(cm, e, liberal) { | 1592 function posFromMouse(cm, e, liberal) { |
| 1585 var display = cm.display; | 1593 var display = cm.display; |
| 1586 if (!liberal) { | 1594 if (!liberal) { |
| 1587 var target = e_target(e); | 1595 var target = e_target(e); |
| 1588 if (target == display.scrollbarH || target == display.scrollbarH.firstChil
d || | 1596 if (target == display.scrollbarH || target == display.scrollbarH.firstChil
d || |
| 1589 target == display.scrollbarV || target == display.scrollbarV.firstChil
d || | 1597 target == display.scrollbarV || target == display.scrollbarV.firstChil
d || |
| 1590 target == display.scrollbarFiller) return null; | 1598 target == display.scrollbarFiller || target == display.gutterFiller) r
eturn null; |
| 1591 } | 1599 } |
| 1592 var x, y, space = getRect(display.lineSpace); | 1600 var x, y, space = getRect(display.lineSpace); |
| 1593 // Fails unpredictably on IE[67] when mouse is dragged around quickly. | 1601 // Fails unpredictably on IE[67] when mouse is dragged around quickly. |
| 1594 try { x = e.clientX; y = e.clientY; } catch (e) { return null; } | 1602 try { x = e.clientX; y = e.clientY; } catch (e) { return null; } |
| 1595 return coordsChar(cm, x - space.left, y - space.top); | 1603 return coordsChar(cm, x - space.left, y - space.top); |
| 1596 } | 1604 } |
| 1597 | 1605 |
| 1598 var lastClick, lastDoubleClick; | 1606 var lastClick, lastDoubleClick; |
| 1599 function onMouseDown(e) { | 1607 function onMouseDown(e) { |
| 1600 var cm = this, display = cm.display, doc = cm.doc, sel = doc.sel; | 1608 var cm = this, display = cm.display, doc = cm.doc, sel = doc.sel; |
| (...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1713 if (outside) setTimeout(operation(cm, function() { | 1721 if (outside) setTimeout(operation(cm, function() { |
| 1714 if (counter != curCount) return; | 1722 if (counter != curCount) return; |
| 1715 display.scroller.scrollTop += outside; | 1723 display.scroller.scrollTop += outside; |
| 1716 extend(e); | 1724 extend(e); |
| 1717 }), 50); | 1725 }), 50); |
| 1718 } | 1726 } |
| 1719 } | 1727 } |
| 1720 | 1728 |
| 1721 function done(e) { | 1729 function done(e) { |
| 1722 counter = Infinity; | 1730 counter = Infinity; |
| 1723 var cur = posFromMouse(cm, e); | |
| 1724 if (cur) doSelect(cur); | |
| 1725 e_preventDefault(e); | 1731 e_preventDefault(e); |
| 1726 focusInput(cm); | 1732 focusInput(cm); |
| 1727 off(document, "mousemove", move); | 1733 off(document, "mousemove", move); |
| 1728 off(document, "mouseup", up); | 1734 off(document, "mouseup", up); |
| 1729 } | 1735 } |
| 1730 | 1736 |
| 1731 var move = operation(cm, function(e) { | 1737 var move = operation(cm, function(e) { |
| 1732 if (!ie && !e_button(e)) done(e); | 1738 if (!ie && !e_button(e)) done(e); |
| 1733 else extend(e); | 1739 else extend(e); |
| 1734 }); | 1740 }); |
| (...skipping 350 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2085 var oldCSS = display.input.style.cssText; | 2091 var oldCSS = display.input.style.cssText; |
| 2086 display.inputDiv.style.position = "absolute"; | 2092 display.inputDiv.style.position = "absolute"; |
| 2087 display.input.style.cssText = "position: fixed; width: 30px; height: 30px; t
op: " + (e.clientY - 5) + | 2093 display.input.style.cssText = "position: fixed; width: 30px; height: 30px; t
op: " + (e.clientY - 5) + |
| 2088 "px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: white; ou
tline: none;" + | 2094 "px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: white; ou
tline: none;" + |
| 2089 "border-width: 0; outline: none; overflow: hidden; opacity: .05; -ms-opaci
ty: .05; filter: alpha(opacity=5);"; | 2095 "border-width: 0; outline: none; overflow: hidden; opacity: .05; -ms-opaci
ty: .05; filter: alpha(opacity=5);"; |
| 2090 focusInput(cm); | 2096 focusInput(cm); |
| 2091 resetInput(cm, true); | 2097 resetInput(cm, true); |
| 2092 // Adds "Select all" to context menu in FF | 2098 // Adds "Select all" to context menu in FF |
| 2093 if (posEq(sel.from, sel.to)) display.input.value = display.prevInput = " "; | 2099 if (posEq(sel.from, sel.to)) display.input.value = display.prevInput = " "; |
| 2094 | 2100 |
| 2101 function prepareSelectAllHack() { |
| 2102 if (display.input.selectionStart != null) { |
| 2103 var extval = display.input.value = " " + (posEq(sel.from, sel.to) ? "" :
display.input.value); |
| 2104 display.prevInput = " "; |
| 2105 display.input.selectionStart = 1; display.input.selectionEnd = extval.le
ngth; |
| 2106 } |
| 2107 } |
| 2095 function rehide() { | 2108 function rehide() { |
| 2096 display.inputDiv.style.position = "relative"; | 2109 display.inputDiv.style.position = "relative"; |
| 2097 display.input.style.cssText = oldCSS; | 2110 display.input.style.cssText = oldCSS; |
| 2098 if (ie_lt9) display.scrollbarV.scrollTop = display.scroller.scrollTop = sc
rollPos; | 2111 if (ie_lt9) display.scrollbarV.scrollTop = display.scroller.scrollTop = sc
rollPos; |
| 2099 slowPoll(cm); | 2112 slowPoll(cm); |
| 2100 | 2113 |
| 2101 // Try to detect the user choosing select-all | 2114 // Try to detect the user choosing select-all |
| 2102 if (display.input.selectionStart != null && (!ie || ie_lt9)) { | 2115 if (display.input.selectionStart != null) { |
| 2116 if (!ie || ie_lt9) prepareSelectAllHack(); |
| 2103 clearTimeout(detectingSelectAll); | 2117 clearTimeout(detectingSelectAll); |
| 2104 var extval = display.input.value = " " + (posEq(sel.from, sel.to) ? "" :
display.input.value), i = 0; | 2118 var i = 0, poll = function(){ |
| 2105 display.prevInput = " "; | |
| 2106 display.input.selectionStart = 1; display.input.selectionEnd = extval.le
ngth; | |
| 2107 var poll = function(){ | |
| 2108 if (display.prevInput == " " && display.input.selectionStart == 0) | 2119 if (display.prevInput == " " && display.input.selectionStart == 0) |
| 2109 operation(cm, commands.selectAll)(cm); | 2120 operation(cm, commands.selectAll)(cm); |
| 2110 else if (i++ < 10) detectingSelectAll = setTimeout(poll, 500); | 2121 else if (i++ < 10) detectingSelectAll = setTimeout(poll, 500); |
| 2111 else resetInput(cm); | 2122 else resetInput(cm); |
| 2112 }; | 2123 }; |
| 2113 detectingSelectAll = setTimeout(poll, 200); | 2124 detectingSelectAll = setTimeout(poll, 200); |
| 2114 } | 2125 } |
| 2115 } | 2126 } |
| 2116 | 2127 |
| 2128 if (ie && !ie_lt9) prepareSelectAllHack(); |
| 2117 if (captureMiddleClick) { | 2129 if (captureMiddleClick) { |
| 2118 e_stop(e); | 2130 e_stop(e); |
| 2119 var mouseup = function() { | 2131 var mouseup = function() { |
| 2120 off(window, "mouseup", mouseup); | 2132 off(window, "mouseup", mouseup); |
| 2121 setTimeout(rehide, 20); | 2133 setTimeout(rehide, 20); |
| 2122 }; | 2134 }; |
| 2123 on(window, "mouseup", mouseup); | 2135 on(window, "mouseup", mouseup); |
| 2124 } else { | 2136 } else { |
| 2125 setTimeout(rehide, 50); | 2137 setTimeout(rehide, 50); |
| 2126 } | 2138 } |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2167 if (posLess(pos, change.from)) return pos; | 2179 if (posLess(pos, change.from)) return pos; |
| 2168 if (!posLess(change.to, pos)) return end; | 2180 if (!posLess(change.to, pos)) return end; |
| 2169 | 2181 |
| 2170 var line = pos.line + change.text.length - (change.to.line - change.from.l
ine) - 1, ch = pos.ch; | 2182 var line = pos.line + change.text.length - (change.to.line - change.from.l
ine) - 1, ch = pos.ch; |
| 2171 if (pos.line == change.to.line) ch += end.ch - change.to.ch; | 2183 if (pos.line == change.to.line) ch += end.ch - change.to.ch; |
| 2172 return Pos(line, ch); | 2184 return Pos(line, ch); |
| 2173 }; | 2185 }; |
| 2174 return {anchor: adjustPos(doc.sel.anchor), head: adjustPos(doc.sel.head)}; | 2186 return {anchor: adjustPos(doc.sel.anchor), head: adjustPos(doc.sel.head)}; |
| 2175 } | 2187 } |
| 2176 | 2188 |
| 2177 function filterChange(doc, change) { | 2189 function filterChange(doc, change, update) { |
| 2178 var obj = { | 2190 var obj = { |
| 2179 canceled: false, | 2191 canceled: false, |
| 2180 from: change.from, | 2192 from: change.from, |
| 2181 to: change.to, | 2193 to: change.to, |
| 2182 text: change.text, | 2194 text: change.text, |
| 2183 origin: change.origin, | 2195 origin: change.origin, |
| 2184 update: function(from, to, text, origin) { | |
| 2185 if (from) this.from = clipPos(doc, from); | |
| 2186 if (to) this.to = clipPos(doc, to); | |
| 2187 if (text) this.text = text; | |
| 2188 if (origin !== undefined) this.origin = origin; | |
| 2189 }, | |
| 2190 cancel: function() { this.canceled = true; } | 2196 cancel: function() { this.canceled = true; } |
| 2191 }; | 2197 }; |
| 2198 if (update) obj.update = function(from, to, text, origin) { |
| 2199 if (from) this.from = clipPos(doc, from); |
| 2200 if (to) this.to = clipPos(doc, to); |
| 2201 if (text) this.text = text; |
| 2202 if (origin !== undefined) this.origin = origin; |
| 2203 }; |
| 2192 signal(doc, "beforeChange", doc, obj); | 2204 signal(doc, "beforeChange", doc, obj); |
| 2193 if (doc.cm) signal(doc.cm, "beforeChange", doc.cm, obj); | 2205 if (doc.cm) signal(doc.cm, "beforeChange", doc.cm, obj); |
| 2194 | 2206 |
| 2195 if (obj.canceled) return null; | 2207 if (obj.canceled) return null; |
| 2196 return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin}; | 2208 return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin}; |
| 2197 } | 2209 } |
| 2198 | 2210 |
| 2199 // Replace the range from from to to by the strings in replacement. | 2211 // Replace the range from from to to by the strings in replacement. |
| 2200 // change is a {from, to, text [, origin]} object | 2212 // change is a {from, to, text [, origin]} object |
| 2201 function makeChange(doc, change, selUpdate, ignoreReadOnly) { | 2213 function makeChange(doc, change, selUpdate, ignoreReadOnly) { |
| 2202 if (doc.cm) { | 2214 if (doc.cm) { |
| 2203 if (!doc.cm.curOp) return operation(doc.cm, makeChange)(doc, change, selUp
date, ignoreReadOnly); | 2215 if (!doc.cm.curOp) return operation(doc.cm, makeChange)(doc, change, selUp
date, ignoreReadOnly); |
| 2204 if (doc.cm.state.suppressEdits) return; | 2216 if (doc.cm.state.suppressEdits) return; |
| 2205 } | 2217 } |
| 2206 | 2218 |
| 2207 if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeC
hange")) { | 2219 if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeC
hange")) { |
| 2208 change = filterChange(doc, change); | 2220 change = filterChange(doc, change, true); |
| 2209 if (!change) return; | 2221 if (!change) return; |
| 2210 } | 2222 } |
| 2211 | 2223 |
| 2212 // Possibly split or suppress the update based on the presence | 2224 // Possibly split or suppress the update based on the presence |
| 2213 // of read-only spans in its range. | 2225 // of read-only spans in its range. |
| 2214 var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc,
change.from, change.to); | 2226 var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc,
change.from, change.to); |
| 2215 if (split) { | 2227 if (split) { |
| 2216 for (var i = split.length - 1; i >= 1; --i) | 2228 for (var i = split.length - 1; i >= 1; --i) |
| 2217 makeChangeNoReadonly(doc, {from: split[i].from, to: split[i].to, text: [
""]}); | 2229 makeChangeNoReadonly(doc, {from: split[i].from, to: split[i].to, text: [
""]}); |
| 2218 if (split.length) | 2230 if (split.length) |
| (...skipping 24 matching lines...) Expand all Loading... |
| 2243 | 2255 |
| 2244 var hist = doc.history; | 2256 var hist = doc.history; |
| 2245 var event = (type == "undo" ? hist.done : hist.undone).pop(); | 2257 var event = (type == "undo" ? hist.done : hist.undone).pop(); |
| 2246 if (!event) return; | 2258 if (!event) return; |
| 2247 hist.dirtyCounter += type == "undo" ? -1 : 1; | 2259 hist.dirtyCounter += type == "undo" ? -1 : 1; |
| 2248 | 2260 |
| 2249 var anti = {changes: [], anchorBefore: event.anchorAfter, headBefore: event.
headAfter, | 2261 var anti = {changes: [], anchorBefore: event.anchorAfter, headBefore: event.
headAfter, |
| 2250 anchorAfter: event.anchorBefore, headAfter: event.headBefore}; | 2262 anchorAfter: event.anchorBefore, headAfter: event.headBefore}; |
| 2251 (type == "undo" ? hist.undone : hist.done).push(anti); | 2263 (type == "undo" ? hist.undone : hist.done).push(anti); |
| 2252 | 2264 |
| 2265 var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm,
"beforeChange"); |
| 2266 |
| 2253 for (var i = event.changes.length - 1; i >= 0; --i) { | 2267 for (var i = event.changes.length - 1; i >= 0; --i) { |
| 2254 var change = event.changes[i]; | 2268 var change = event.changes[i]; |
| 2255 change.origin = type; | 2269 change.origin = type; |
| 2270 if (filter && !filterChange(doc, change, false)) { |
| 2271 (type == "undo" ? hist.done : hist.undone).length = 0; |
| 2272 return; |
| 2273 } |
| 2274 |
| 2256 anti.changes.push(historyChangeFromChange(doc, change)); | 2275 anti.changes.push(historyChangeFromChange(doc, change)); |
| 2257 | 2276 |
| 2258 var after = i ? computeSelAfterChange(doc, change, null) | 2277 var after = i ? computeSelAfterChange(doc, change, null) |
| 2259 : {anchor: event.anchorBefore, head: event.headBefore}; | 2278 : {anchor: event.anchorBefore, head: event.headBefore}; |
| 2260 makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change)); | 2279 makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change)); |
| 2261 var rebased = []; | 2280 var rebased = []; |
| 2262 | 2281 |
| 2263 linkedDocs(doc, function(doc, sharedHist) { | 2282 linkedDocs(doc, function(doc, sharedHist) { |
| 2264 if (!sharedHist && indexOf(rebased, doc.history) == -1) { | 2283 if (!sharedHist && indexOf(rebased, doc.history) == -1) { |
| 2265 rebaseHist(doc.history, change); | 2284 rebaseHist(doc.history, change); |
| (...skipping 241 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2507 } | 2526 } |
| 2508 } | 2527 } |
| 2509 } | 2528 } |
| 2510 return curPos; | 2529 return curPos; |
| 2511 } | 2530 } |
| 2512 } | 2531 } |
| 2513 | 2532 |
| 2514 // SCROLLING | 2533 // SCROLLING |
| 2515 | 2534 |
| 2516 function scrollCursorIntoView(cm) { | 2535 function scrollCursorIntoView(cm) { |
| 2517 var coords = scrollPosIntoView(cm, cm.doc.sel.head); | 2536 var coords = scrollPosIntoView(cm, cm.doc.sel.head, cm.options.cursorScrollM
argin); |
| 2518 if (!cm.state.focused) return; | 2537 if (!cm.state.focused) return; |
| 2519 var display = cm.display, box = getRect(display.sizer), doScroll = null, pTo
p = paddingTop(cm.display); | 2538 var display = cm.display, box = getRect(display.sizer), doScroll = null, pTo
p = paddingTop(cm.display); |
| 2520 if (coords.top + pTop + box.top < 0) doScroll = true; | 2539 if (coords.top + pTop + box.top < 0) doScroll = true; |
| 2521 else if (coords.bottom + pTop + box.top > (window.innerHeight || document.do
cumentElement.clientHeight)) doScroll = false; | 2540 else if (coords.bottom + pTop + box.top > (window.innerHeight || document.do
cumentElement.clientHeight)) doScroll = false; |
| 2522 if (doScroll != null && !phantom) { | 2541 if (doScroll != null && !phantom) { |
| 2523 var hidden = display.cursor.style.display == "none"; | 2542 var hidden = display.cursor.style.display == "none"; |
| 2524 if (hidden) { | 2543 if (hidden) { |
| 2525 display.cursor.style.display = ""; | 2544 display.cursor.style.display = ""; |
| 2526 display.cursor.style.left = coords.left + "px"; | 2545 display.cursor.style.left = coords.left + "px"; |
| 2527 display.cursor.style.top = (coords.top - display.viewOffset) + "px"; | 2546 display.cursor.style.top = (coords.top - display.viewOffset) + "px"; |
| (...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2638 var no = handle, line = handle, doc = cm.doc; | 2657 var no = handle, line = handle, doc = cm.doc; |
| 2639 if (typeof handle == "number") line = getLine(doc, clipLine(doc, handle)); | 2658 if (typeof handle == "number") line = getLine(doc, clipLine(doc, handle)); |
| 2640 else no = lineNo(handle); | 2659 else no = lineNo(handle); |
| 2641 if (no == null) return null; | 2660 if (no == null) return null; |
| 2642 if (op(line, no)) regChange(cm, no, no + 1); | 2661 if (op(line, no)) regChange(cm, no, no + 1); |
| 2643 else return null; | 2662 else return null; |
| 2644 return line; | 2663 return line; |
| 2645 } | 2664 } |
| 2646 | 2665 |
| 2647 function findPosH(doc, pos, dir, unit, visually) { | 2666 function findPosH(doc, pos, dir, unit, visually) { |
| 2648 var line = pos.line, ch = pos.ch; | 2667 var line = pos.line, ch = pos.ch, origDir = dir; |
| 2649 var lineObj = getLine(doc, line); | 2668 var lineObj = getLine(doc, line); |
| 2650 var possible = true; | 2669 var possible = true; |
| 2651 function findNextLine() { | 2670 function findNextLine() { |
| 2652 var l = line + dir; | 2671 var l = line + dir; |
| 2653 if (l < doc.first || l >= doc.first + doc.size) return (possible = false); | 2672 if (l < doc.first || l >= doc.first + doc.size) return (possible = false); |
| 2654 line = l; | 2673 line = l; |
| 2655 return lineObj = getLine(doc, l); | 2674 return lineObj = getLine(doc, l); |
| 2656 } | 2675 } |
| 2657 function moveOnce(boundToLine) { | 2676 function moveOnce(boundToLine) { |
| 2658 var next = (visually ? moveVisually : moveLogically)(lineObj, ch, dir, tru
e); | 2677 var next = (visually ? moveVisually : moveLogically)(lineObj, ch, dir, tru
e); |
| (...skipping 18 matching lines...) Expand all Loading... |
| 2677 : /\s/.test(cur) ? null | 2696 : /\s/.test(cur) ? null |
| 2678 : "p"; | 2697 : "p"; |
| 2679 if (sawType && sawType != type) { | 2698 if (sawType && sawType != type) { |
| 2680 if (dir < 0) {dir = 1; moveOnce();} | 2699 if (dir < 0) {dir = 1; moveOnce();} |
| 2681 break; | 2700 break; |
| 2682 } | 2701 } |
| 2683 if (type) sawType = type; | 2702 if (type) sawType = type; |
| 2684 if (dir > 0 && !moveOnce(!first)) break; | 2703 if (dir > 0 && !moveOnce(!first)) break; |
| 2685 } | 2704 } |
| 2686 } | 2705 } |
| 2687 var result = skipAtomic(doc, Pos(line, ch), dir, true); | 2706 var result = skipAtomic(doc, Pos(line, ch), origDir, true); |
| 2688 if (!possible) result.hitSide = true; | 2707 if (!possible) result.hitSide = true; |
| 2689 return result; | 2708 return result; |
| 2690 } | 2709 } |
| 2691 | 2710 |
| 2692 function findPosV(cm, pos, dir, unit) { | 2711 function findPosV(cm, pos, dir, unit) { |
| 2693 var doc = cm.doc, x = pos.left, y; | 2712 var doc = cm.doc, x = pos.left, y; |
| 2694 if (unit == "page") { | 2713 if (unit == "page") { |
| 2695 var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeigh
t || document.documentElement.clientHeight); | 2714 var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeigh
t || document.documentElement.clientHeight); |
| 2696 y = pos.top + dir * (pageSize - (dir < 0 ? 1.5 : .5) * textHeight(cm.displ
ay)); | 2715 y = pos.top + dir * (pageSize - (dir < 0 ? 1.5 : .5) * textHeight(cm.displ
ay)); |
| 2697 } else if (unit == "line") { | 2716 } else if (unit == "line") { |
| (...skipping 25 matching lines...) Expand all Loading... |
| 2723 function selectLine(cm, line) { | 2742 function selectLine(cm, line) { |
| 2724 extendSelection(cm.doc, Pos(line, 0), clipPos(cm.doc, Pos(line + 1, 0))); | 2743 extendSelection(cm.doc, Pos(line, 0), clipPos(cm.doc, Pos(line + 1, 0))); |
| 2725 } | 2744 } |
| 2726 | 2745 |
| 2727 // PROTOTYPE | 2746 // PROTOTYPE |
| 2728 | 2747 |
| 2729 // The publicly visible API. Note that operation(null, f) means | 2748 // The publicly visible API. Note that operation(null, f) means |
| 2730 // 'wrap f in an operation, performed on its `this` parameter' | 2749 // 'wrap f in an operation, performed on its `this` parameter' |
| 2731 | 2750 |
| 2732 CodeMirror.prototype = { | 2751 CodeMirror.prototype = { |
| 2752 constructor: CodeMirror, |
| 2733 focus: function(){window.focus(); focusInput(this); onFocus(this); fastPoll(
this);}, | 2753 focus: function(){window.focus(); focusInput(this); onFocus(this); fastPoll(
this);}, |
| 2734 | 2754 |
| 2735 setOption: function(option, value) { | 2755 setOption: function(option, value) { |
| 2736 var options = this.options, old = options[option]; | 2756 var options = this.options, old = options[option]; |
| 2737 if (options[option] == value && option != "mode") return; | 2757 if (options[option] == value && option != "mode") return; |
| 2738 options[option] = value; | 2758 options[option] = value; |
| 2739 if (optionHandlers.hasOwnProperty(option)) | 2759 if (optionHandlers.hasOwnProperty(option)) |
| 2740 operation(this, optionHandlers[option])(this, value, old); | 2760 operation(this, optionHandlers[option])(this, value, old); |
| 2741 }, | 2761 }, |
| 2742 | 2762 |
| (...skipping 15 matching lines...) Expand all Loading... |
| 2758 addOverlay: operation(null, function(spec, options) { | 2778 addOverlay: operation(null, function(spec, options) { |
| 2759 var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec); | 2779 var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec); |
| 2760 if (mode.startState) throw new Error("Overlays may not be stateful."); | 2780 if (mode.startState) throw new Error("Overlays may not be stateful."); |
| 2761 this.state.overlays.push({mode: mode, modeSpec: spec, opaque: options && o
ptions.opaque}); | 2781 this.state.overlays.push({mode: mode, modeSpec: spec, opaque: options && o
ptions.opaque}); |
| 2762 this.state.modeGen++; | 2782 this.state.modeGen++; |
| 2763 regChange(this); | 2783 regChange(this); |
| 2764 }), | 2784 }), |
| 2765 removeOverlay: operation(null, function(spec) { | 2785 removeOverlay: operation(null, function(spec) { |
| 2766 var overlays = this.state.overlays; | 2786 var overlays = this.state.overlays; |
| 2767 for (var i = 0; i < overlays.length; ++i) { | 2787 for (var i = 0; i < overlays.length; ++i) { |
| 2768 if (overlays[i].modeSpec == spec) { | 2788 var cur = overlays[i].modeSpec; |
| 2789 if (cur == spec || typeof spec == "string" && cur.name == spec) { |
| 2769 overlays.splice(i, 1); | 2790 overlays.splice(i, 1); |
| 2770 this.state.modeGen++; | 2791 this.state.modeGen++; |
| 2771 regChange(this); | 2792 regChange(this); |
| 2772 return; | 2793 return; |
| 2773 } | 2794 } |
| 2774 } | 2795 } |
| 2775 }), | 2796 }), |
| 2776 | 2797 |
| 2777 indentLine: operation(null, function(n, dir, aggressive) { | 2798 indentLine: operation(null, function(n, dir, aggressive) { |
| 2778 if (typeof dir != "string") { | 2799 if (typeof dir != "string") { |
| (...skipping 204 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2983 var sel = this.doc.sel; | 3004 var sel = this.doc.sel; |
| 2984 var pos = cursorCoords(this, sel.head, "div"); | 3005 var pos = cursorCoords(this, sel.head, "div"); |
| 2985 if (sel.goalColumn != null) pos.left = sel.goalColumn; | 3006 if (sel.goalColumn != null) pos.left = sel.goalColumn; |
| 2986 var target = findPosV(this, pos, dir, unit); | 3007 var target = findPosV(this, pos, dir, unit); |
| 2987 | 3008 |
| 2988 if (unit == "page") addToScrollPos(this, 0, charCoords(this, target, "div"
).top - pos.top); | 3009 if (unit == "page") addToScrollPos(this, 0, charCoords(this, target, "div"
).top - pos.top); |
| 2989 extendSelection(this.doc, target, target, dir); | 3010 extendSelection(this.doc, target, target, dir); |
| 2990 sel.goalColumn = pos.left; | 3011 sel.goalColumn = pos.left; |
| 2991 }), | 3012 }), |
| 2992 | 3013 |
| 2993 toggleOverwrite: function() { | 3014 toggleOverwrite: function(value) { |
| 3015 if (value != null && value == this.state.overwrite) return; |
| 2994 if (this.state.overwrite = !this.state.overwrite) | 3016 if (this.state.overwrite = !this.state.overwrite) |
| 2995 this.display.cursor.className += " CodeMirror-overwrite"; | 3017 this.display.cursor.className += " CodeMirror-overwrite"; |
| 2996 else | 3018 else |
| 2997 this.display.cursor.className = this.display.cursor.className.replace("
CodeMirror-overwrite", ""); | 3019 this.display.cursor.className = this.display.cursor.className.replace("
CodeMirror-overwrite", ""); |
| 2998 }, | 3020 }, |
| 2999 hasFocus: function() { return this.state.focused; }, | 3021 hasFocus: function() { return this.state.focused; }, |
| 3000 | 3022 |
| 3001 scrollTo: operation(null, function(x, y) { | 3023 scrollTo: operation(null, function(x, y) { |
| 3002 updateScrollPos(this, x, y); | 3024 updateScrollPos(this, x, y); |
| 3003 }), | 3025 }), |
| (...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3106 | 3128 |
| 3107 option("lineWrapping", false, wrappingChanged, true); | 3129 option("lineWrapping", false, wrappingChanged, true); |
| 3108 option("gutters", [], function(cm) { | 3130 option("gutters", [], function(cm) { |
| 3109 setGuttersForLineNumbers(cm.options); | 3131 setGuttersForLineNumbers(cm.options); |
| 3110 guttersChanged(cm); | 3132 guttersChanged(cm); |
| 3111 }, true); | 3133 }, true); |
| 3112 option("fixedGutter", true, function(cm, val) { | 3134 option("fixedGutter", true, function(cm, val) { |
| 3113 cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px
" : "0"; | 3135 cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px
" : "0"; |
| 3114 cm.refresh(); | 3136 cm.refresh(); |
| 3115 }, true); | 3137 }, true); |
| 3138 option("coverGutterNextToScrollbar", false, updateScrollbars, true); |
| 3116 option("lineNumbers", false, function(cm) { | 3139 option("lineNumbers", false, function(cm) { |
| 3117 setGuttersForLineNumbers(cm.options); | 3140 setGuttersForLineNumbers(cm.options); |
| 3118 guttersChanged(cm); | 3141 guttersChanged(cm); |
| 3119 }, true); | 3142 }, true); |
| 3120 option("firstLineNumber", 1, guttersChanged, true); | 3143 option("firstLineNumber", 1, guttersChanged, true); |
| 3121 option("lineNumberFormatter", function(integer) {return integer;}, guttersChan
ged, true); | 3144 option("lineNumberFormatter", function(integer) {return integer;}, guttersChan
ged, true); |
| 3122 option("showCursorWhenSelecting", false, updateSelection, true); | 3145 option("showCursorWhenSelecting", false, updateSelection, true); |
| 3123 | 3146 |
| 3124 option("readOnly", false, function(cm, val) { | 3147 option("readOnly", false, function(cm, val) { |
| 3125 if (val == "nocursor") {onBlur(cm); cm.display.input.blur();} | 3148 if (val == "nocursor") {onBlur(cm); cm.display.input.blur();} |
| 3126 else if (!val) resetInput(cm, true); | 3149 else if (!val) resetInput(cm, true); |
| 3127 }); | 3150 }); |
| 3128 option("dragDrop", true); | 3151 option("dragDrop", true); |
| 3129 | 3152 |
| 3130 option("cursorBlinkRate", 530); | 3153 option("cursorBlinkRate", 530); |
| 3154 option("cursorScrollMargin", 0); |
| 3131 option("cursorHeight", 1); | 3155 option("cursorHeight", 1); |
| 3132 option("workTime", 100); | 3156 option("workTime", 100); |
| 3133 option("workDelay", 100); | 3157 option("workDelay", 100); |
| 3134 option("flattenSpans", true); | 3158 option("flattenSpans", true); |
| 3135 option("pollInterval", 100); | 3159 option("pollInterval", 100); |
| 3136 option("undoDepth", 40, function(cm, val){cm.doc.history.undoDepth = val;}); | 3160 option("undoDepth", 40, function(cm, val){cm.doc.history.undoDepth = val;}); |
| 3137 option("historyEventDelay", 500); | 3161 option("historyEventDelay", 500); |
| 3138 option("viewportMargin", 10, function(cm){cm.refresh();}, true); | 3162 option("viewportMargin", 10, function(cm){cm.refresh();}, true); |
| 3139 option("maxHighlightLength", 10000, function(cm){loadMode(cm); cm.refresh();},
true); | 3163 option("maxHighlightLength", 10000, function(cm){loadMode(cm); cm.refresh();},
true); |
| 3140 option("moveInputWithCursor", true, function(cm, val) { | 3164 option("moveInputWithCursor", true, function(cm, val) { |
| (...skipping 17 matching lines...) Expand all Loading... |
| 3158 for (var i = 2; i < arguments.length; ++i) mode.dependencies.push(argument
s[i]); | 3182 for (var i = 2; i < arguments.length; ++i) mode.dependencies.push(argument
s[i]); |
| 3159 } | 3183 } |
| 3160 modes[name] = mode; | 3184 modes[name] = mode; |
| 3161 }; | 3185 }; |
| 3162 | 3186 |
| 3163 CodeMirror.defineMIME = function(mime, spec) { | 3187 CodeMirror.defineMIME = function(mime, spec) { |
| 3164 mimeModes[mime] = spec; | 3188 mimeModes[mime] = spec; |
| 3165 }; | 3189 }; |
| 3166 | 3190 |
| 3167 CodeMirror.resolveMode = function(spec) { | 3191 CodeMirror.resolveMode = function(spec) { |
| 3168 if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) | 3192 if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) { |
| 3169 spec = mimeModes[spec]; | 3193 spec = mimeModes[spec]; |
| 3170 else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) | 3194 } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(
spec.name)) { |
| 3195 var found = mimeModes[spec.name]; |
| 3196 spec = createObj(found, spec); |
| 3197 spec.name = found.name; |
| 3198 } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec))
{ |
| 3171 return CodeMirror.resolveMode("application/xml"); | 3199 return CodeMirror.resolveMode("application/xml"); |
| 3200 } |
| 3172 if (typeof spec == "string") return {name: spec}; | 3201 if (typeof spec == "string") return {name: spec}; |
| 3173 else return spec || {name: "null"}; | 3202 else return spec || {name: "null"}; |
| 3174 }; | 3203 }; |
| 3175 | 3204 |
| 3176 CodeMirror.getMode = function(options, spec) { | 3205 CodeMirror.getMode = function(options, spec) { |
| 3177 spec = CodeMirror.resolveMode(spec); | 3206 spec = CodeMirror.resolveMode(spec); |
| 3178 var mfactory = modes[spec.name]; | 3207 var mfactory = modes[spec.name]; |
| 3179 if (!mfactory) return CodeMirror.getMode(options, "text/plain"); | 3208 if (!mfactory) return CodeMirror.getMode(options, "text/plain"); |
| 3180 var modeObj = mfactory(options, spec); | 3209 var modeObj = mfactory(options, spec); |
| 3181 if (modeExtensions.hasOwnProperty(spec.name)) { | 3210 if (modeExtensions.hasOwnProperty(spec.name)) { |
| (...skipping 409 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3591 if (span.from != null || span.to != null) { | 3620 if (span.from != null || span.to != null) { |
| 3592 var found = lineNo(line); | 3621 var found = lineNo(line); |
| 3593 if (span.from != null) from = Pos(found, span.from); | 3622 if (span.from != null) from = Pos(found, span.from); |
| 3594 if (span.to != null) to = Pos(found, span.to); | 3623 if (span.to != null) to = Pos(found, span.to); |
| 3595 } | 3624 } |
| 3596 } | 3625 } |
| 3597 if (this.type == "bookmark") return from; | 3626 if (this.type == "bookmark") return from; |
| 3598 return from && {from: from, to: to}; | 3627 return from && {from: from, to: to}; |
| 3599 }; | 3628 }; |
| 3600 | 3629 |
| 3601 TextMarker.prototype.getOptions = function(copyWidget) { | 3630 TextMarker.prototype.changed = function() { |
| 3602 var repl = this.replacedWith; | 3631 var pos = this.find(), cm = this.doc.cm; |
| 3603 return {className: this.className, | 3632 if (!pos || !cm) return; |
| 3604 inclusiveLeft: this.inclusiveLeft, inclusiveRight: this.inclusiveRig
ht, | 3633 var line = getLine(this.doc, pos.from.line); |
| 3605 atomic: this.atomic, | 3634 clearCachedMeasurement(cm, line); |
| 3606 collapsed: this.collapsed, | 3635 if (pos.from.line >= cm.display.showingFrom && pos.from.line < cm.display.sh
owingTo) { |
| 3607 replacedWith: copyWidget ? repl && repl.cloneNode(true) : repl, | 3636 for (var node = cm.display.lineDiv.firstChild; node; node = node.nextSibli
ng) if (node.lineObj == line) { |
| 3608 readOnly: this.readOnly, | 3637 if (node.offsetHeight != line.height) updateLineHeight(line, node.offset
Height); |
| 3609 startStyle: this.startStyle, endStyle: this.endStyle}; | 3638 break; |
| 3639 } |
| 3640 runInOp(cm, function() { cm.curOp.selectionChanged = true; }); |
| 3641 } |
| 3610 }; | 3642 }; |
| 3611 | 3643 |
| 3612 TextMarker.prototype.attachLine = function(line) { | 3644 TextMarker.prototype.attachLine = function(line) { |
| 3613 if (!this.lines.length && this.doc.cm) { | 3645 if (!this.lines.length && this.doc.cm) { |
| 3614 var op = this.doc.cm.curOp; | 3646 var op = this.doc.cm.curOp; |
| 3615 if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1) | 3647 if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1) |
| 3616 (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this); | 3648 (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this); |
| 3617 } | 3649 } |
| 3618 this.lines.push(line); | 3650 this.lines.push(line); |
| 3619 }; | 3651 }; |
| (...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3699 SharedTextMarker.prototype.clear = function() { | 3731 SharedTextMarker.prototype.clear = function() { |
| 3700 if (this.explicitlyCleared) return; | 3732 if (this.explicitlyCleared) return; |
| 3701 this.explicitlyCleared = true; | 3733 this.explicitlyCleared = true; |
| 3702 for (var i = 0; i < this.markers.length; ++i) | 3734 for (var i = 0; i < this.markers.length; ++i) |
| 3703 this.markers[i].clear(); | 3735 this.markers[i].clear(); |
| 3704 signalLater(this, "clear"); | 3736 signalLater(this, "clear"); |
| 3705 }; | 3737 }; |
| 3706 SharedTextMarker.prototype.find = function() { | 3738 SharedTextMarker.prototype.find = function() { |
| 3707 return this.primary.find(); | 3739 return this.primary.find(); |
| 3708 }; | 3740 }; |
| 3709 SharedTextMarker.prototype.getOptions = function(copyWidget) { | |
| 3710 var inner = this.primary.getOptions(copyWidget); | |
| 3711 inner.shared = true; | |
| 3712 return inner; | |
| 3713 }; | |
| 3714 | 3741 |
| 3715 function markTextShared(doc, from, to, options, type) { | 3742 function markTextShared(doc, from, to, options, type) { |
| 3716 options = copyObj(options); | 3743 options = copyObj(options); |
| 3717 options.shared = false; | 3744 options.shared = false; |
| 3718 var markers = [markText(doc, from, to, options, type)], primary = markers[0]
; | 3745 var markers = [markText(doc, from, to, options, type)], primary = markers[0]
; |
| 3719 var widget = options.replacedWith; | 3746 var widget = options.replacedWith; |
| 3720 linkedDocs(doc, function(doc) { | 3747 linkedDocs(doc, function(doc) { |
| 3721 if (widget) options.replacedWith = widget.cloneNode(true); | 3748 if (widget) options.replacedWith = widget.cloneNode(true); |
| 3722 markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options,
type)); | 3749 markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options,
type)); |
| 3723 for (var i = 0; i < doc.linked.length; ++i) | 3750 for (var i = 0; i < doc.linked.length; ++i) |
| (...skipping 487 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 4211 builder.pre.appendChild(content); | 4238 builder.pre.appendChild(content); |
| 4212 } | 4239 } |
| 4213 | 4240 |
| 4214 function buildTokenMeasure(builder, text, style, startStyle, endStyle) { | 4241 function buildTokenMeasure(builder, text, style, startStyle, endStyle) { |
| 4215 var wrapping = builder.cm.options.lineWrapping; | 4242 var wrapping = builder.cm.options.lineWrapping; |
| 4216 for (var i = 0; i < text.length; ++i) { | 4243 for (var i = 0; i < text.length; ++i) { |
| 4217 var ch = text.charAt(i), start = i == 0; | 4244 var ch = text.charAt(i), start = i == 0; |
| 4218 if (ch >= "\ud800" && ch < "\udbff" && i < text.length - 1) { | 4245 if (ch >= "\ud800" && ch < "\udbff" && i < text.length - 1) { |
| 4219 ch = text.slice(i, i + 2); | 4246 ch = text.slice(i, i + 2); |
| 4220 ++i; | 4247 ++i; |
| 4221 } else if (i && wrapping && | 4248 } else if (i && wrapping && spanAffectsWrapping(text, i)) { |
| 4222 spanAffectsWrapping.test(text.slice(i - 1, i + 1))) { | |
| 4223 builder.pre.appendChild(elt("wbr")); | 4249 builder.pre.appendChild(elt("wbr")); |
| 4224 } | 4250 } |
| 4225 var span = builder.measure[builder.pos] = | 4251 var span = builder.measure[builder.pos] = |
| 4226 buildToken(builder, ch, style, | 4252 buildToken(builder, ch, style, |
| 4227 start && startStyle, i == text.length - 1 && endStyle); | 4253 start && startStyle, i == text.length - 1 && endStyle); |
| 4228 // In IE single-space nodes wrap differently than spaces | 4254 // In IE single-space nodes wrap differently than spaces |
| 4229 // embedded in larger text nodes, except when set to | 4255 // embedded in larger text nodes, except when set to |
| 4230 // white-space: normal (issue #1268). | 4256 // white-space: normal (issue #1268). |
| 4231 if (ie && wrapping && ch == " " && i && !/\s/.test(text.charAt(i - 1)) && | 4257 if (ie && wrapping && ch == " " && i && !/\s/.test(text.charAt(i - 1)) && |
| 4232 i < text.length - 1 && !/\s/.test(text.charAt(i + 1))) | 4258 i < text.length - 1 && !/\s/.test(text.charAt(i + 1))) |
| (...skipping 278 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 4511 var start = Pos(firstLine, 0); | 4537 var start = Pos(firstLine, 0); |
| 4512 this.sel = {from: start, to: start, head: start, anchor: start, shift: false
, extend: false, goalColumn: null}; | 4538 this.sel = {from: start, to: start, head: start, anchor: start, shift: false
, extend: false, goalColumn: null}; |
| 4513 this.id = ++nextDocId; | 4539 this.id = ++nextDocId; |
| 4514 this.modeOption = mode; | 4540 this.modeOption = mode; |
| 4515 | 4541 |
| 4516 if (typeof text == "string") text = splitLines(text); | 4542 if (typeof text == "string") text = splitLines(text); |
| 4517 updateDoc(this, {from: start, to: start, text: text}, null, {head: start, an
chor: start}); | 4543 updateDoc(this, {from: start, to: start, text: text}, null, {head: start, an
chor: start}); |
| 4518 }; | 4544 }; |
| 4519 | 4545 |
| 4520 Doc.prototype = createObj(BranchChunk.prototype, { | 4546 Doc.prototype = createObj(BranchChunk.prototype, { |
| 4547 constructor: Doc, |
| 4521 iter: function(from, to, op) { | 4548 iter: function(from, to, op) { |
| 4522 if (op) this.iterN(from - this.first, to - from, op); | 4549 if (op) this.iterN(from - this.first, to - from, op); |
| 4523 else this.iterN(this.first, this.first + this.size, from); | 4550 else this.iterN(this.first, this.first + this.size, from); |
| 4524 }, | 4551 }, |
| 4525 | 4552 |
| 4526 insert: function(at, lines) { | 4553 insert: function(at, lines) { |
| 4527 var height = 0; | 4554 var height = 0; |
| 4528 for (var i = 0, e = lines.length; i < e; ++i) height += lines[i].height; | 4555 for (var i = 0, e = lines.length; i < e; ++i) height += lines[i].height; |
| 4529 this.insertInner(at - this.first, lines, height); | 4556 this.insertInner(at - this.first, lines, height); |
| 4530 }, | 4557 }, |
| (...skipping 644 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 5175 function emptyArray(size) { | 5202 function emptyArray(size) { |
| 5176 for (var a = [], i = 0; i < size; ++i) a.push(undefined); | 5203 for (var a = [], i = 0; i < size; ++i) a.push(undefined); |
| 5177 return a; | 5204 return a; |
| 5178 } | 5205 } |
| 5179 | 5206 |
| 5180 function bind(f) { | 5207 function bind(f) { |
| 5181 var args = Array.prototype.slice.call(arguments, 1); | 5208 var args = Array.prototype.slice.call(arguments, 1); |
| 5182 return function(){return f.apply(null, args);}; | 5209 return function(){return f.apply(null, args);}; |
| 5183 } | 5210 } |
| 5184 | 5211 |
| 5185 var nonASCIISingleCaseWordChar = /[\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e
00-\u9fcc]/; | 5212 var nonASCIISingleCaseWordChar = /[\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e
00-\u9fcc\uac00-\ud7af]/; |
| 5186 function isWordChar(ch) { | 5213 function isWordChar(ch) { |
| 5187 return /\w/.test(ch) || ch > "\x80" && | 5214 return /\w/.test(ch) || ch > "\x80" && |
| 5188 (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(c
h)); | 5215 (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(c
h)); |
| 5189 } | 5216 } |
| 5190 | 5217 |
| 5191 function isEmpty(obj) { | 5218 function isEmpty(obj) { |
| 5192 for (var n in obj) if (obj.hasOwnProperty(n) && obj[n]) return false; | 5219 for (var n in obj) if (obj.hasOwnProperty(n) && obj[n]) return false; |
| 5193 return true; | 5220 return true; |
| 5194 } | 5221 } |
| 5195 | 5222 |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 5236 // couldn't get it to work yet. | 5263 // couldn't get it to work yet. |
| 5237 if (ie_lt9) return false; | 5264 if (ie_lt9) return false; |
| 5238 var div = elt('div'); | 5265 var div = elt('div'); |
| 5239 return "draggable" in div || "dragDrop" in div; | 5266 return "draggable" in div || "dragDrop" in div; |
| 5240 }(); | 5267 }(); |
| 5241 | 5268 |
| 5242 // For a reason I have yet to figure out, some browsers disallow | 5269 // For a reason I have yet to figure out, some browsers disallow |
| 5243 // word wrapping between certain characters *only* if a new inline | 5270 // word wrapping between certain characters *only* if a new inline |
| 5244 // element is started between them. This makes it hard to reliably | 5271 // element is started between them. This makes it hard to reliably |
| 5245 // measure the position of things, since that requires inserting an | 5272 // measure the position of things, since that requires inserting an |
| 5246 // extra span. This terribly fragile set of regexps matches the | 5273 // extra span. This terribly fragile set of tests matches the |
| 5247 // character combinations that suffer from this phenomenon on the | 5274 // character combinations that suffer from this phenomenon on the |
| 5248 // various browsers. | 5275 // various browsers. |
| 5249 var spanAffectsWrapping = /^$/; // Won't match any two-character string | 5276 function spanAffectsWrapping() { return false; } |
| 5250 if (gecko) spanAffectsWrapping = /$'/; | 5277 if (gecko) // Only for "$'" |
| 5251 else if (safari && !/Version\/([6-9]|\d\d)\b/.test(navigator.userAgent)) spanA
ffectsWrapping = /\-[^ \-?]|\?[^ !'\"\),.\-\/:;\?\]\}]/; | 5278 spanAffectsWrapping = function(str, i) { |
| 5252 else if (webkit) spanAffectsWrapping = /[~!#%&*)=+}\]|\"\.>,:;][({[<]|-[^\-?\.
]|\?[\w~`@#$%\^&*(_=+{[|><]/; | 5279 return str.charCodeAt(i - 1) == 36 && str.charCodeAt(i) == 39; |
| 5280 }; |
| 5281 else if (safari && !/Version\/([6-9]|\d\d)\b/.test(navigator.userAgent)) |
| 5282 spanAffectsWrapping = function(str, i) { |
| 5283 return /\-[^ \-?]|\?[^ !\'\"\),.\-\/:;\?\]\}]/.test(str.slice(i - 1, i + 1
)); |
| 5284 }; |
| 5285 else if (webkit) |
| 5286 spanAffectsWrapping = function(str, i) { |
| 5287 if (i > 1 && str.charCodeAt(i - 1) == 45 && /\w/.test(str.charAt(i - 2)) &
& /[^\-?\.]/.test(str.charAt(i))) |
| 5288 return true; |
| 5289 return /[~!#%&*)=+}\]|\"\.>,:;][({[<]|\?[\w~`@#$%\^&*(_=+{[|><]/.test(str.
slice(i - 1, i + 1)); |
| 5290 }; |
| 5253 | 5291 |
| 5254 var knownScrollbarWidth; | 5292 var knownScrollbarWidth; |
| 5255 function scrollbarWidth(measure) { | 5293 function scrollbarWidth(measure) { |
| 5256 if (knownScrollbarWidth != null) return knownScrollbarWidth; | 5294 if (knownScrollbarWidth != null) return knownScrollbarWidth; |
| 5257 var test = elt("div", null, null, "width: 50px; height: 50px; overflow-x: sc
roll"); | 5295 var test = elt("div", null, null, "width: 50px; height: 50px; overflow-x: sc
roll"); |
| 5258 removeChildrenAndAdd(measure, test); | 5296 removeChildrenAndAdd(measure, test); |
| 5259 if (test.offsetWidth) | 5297 if (test.offsetWidth) |
| 5260 knownScrollbarWidth = test.offsetHeight - test.clientHeight; | 5298 knownScrollbarWidth = test.offsetHeight - test.clientHeight; |
| 5261 return knownScrollbarWidth || 0; | 5299 return knownScrollbarWidth || 0; |
| 5262 } | 5300 } |
| (...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 5361 } | 5399 } |
| 5362 function lineEnd(cm, lineN) { | 5400 function lineEnd(cm, lineN) { |
| 5363 var merged, line; | 5401 var merged, line; |
| 5364 while (merged = collapsedSpanAtEnd(line = getLine(cm.doc, lineN))) | 5402 while (merged = collapsedSpanAtEnd(line = getLine(cm.doc, lineN))) |
| 5365 lineN = merged.find().to.line; | 5403 lineN = merged.find().to.line; |
| 5366 var order = getOrder(line); | 5404 var order = getOrder(line); |
| 5367 var ch = !order ? line.text.length : order[0].level % 2 ? lineLeft(line) : l
ineRight(line); | 5405 var ch = !order ? line.text.length : order[0].level % 2 ? lineLeft(line) : l
ineRight(line); |
| 5368 return Pos(lineN, ch); | 5406 return Pos(lineN, ch); |
| 5369 } | 5407 } |
| 5370 | 5408 |
| 5409 function compareBidiLevel(order, a, b) { |
| 5410 var linedir = order[0].level; |
| 5411 if (a == linedir) return true; |
| 5412 if (b == linedir) return false; |
| 5413 return a < b; |
| 5414 } |
| 5415 var bidiOther; |
| 5416 function getBidiPartAt(order, pos) { |
| 5417 for (var i = 0, found; i < order.length; ++i) { |
| 5418 var cur = order[i]; |
| 5419 if (cur.from < pos && cur.to > pos) { bidiOther = null; return i; } |
| 5420 if (cur.from == pos || cur.to == pos) { |
| 5421 if (found == null) { |
| 5422 found = i; |
| 5423 } else if (compareBidiLevel(order, cur.level, order[found].level)) { |
| 5424 bidiOther = found; |
| 5425 return i; |
| 5426 } else { |
| 5427 bidiOther = i; |
| 5428 return found; |
| 5429 } |
| 5430 } |
| 5431 } |
| 5432 bidiOther = null; |
| 5433 return found; |
| 5434 } |
| 5435 |
| 5436 function moveInLine(line, pos, dir, byUnit) { |
| 5437 if (!byUnit) return pos + dir; |
| 5438 do pos += dir; |
| 5439 while (pos > 0 && isExtendingChar.test(line.text.charAt(pos))); |
| 5440 return pos; |
| 5441 } |
| 5442 |
| 5371 // This is somewhat involved. It is needed in order to move | 5443 // This is somewhat involved. It is needed in order to move |
| 5372 // 'visually' through bi-directional text -- i.e., pressing left | 5444 // 'visually' through bi-directional text -- i.e., pressing left |
| 5373 // should make the cursor go left, even when in RTL text. The | 5445 // should make the cursor go left, even when in RTL text. The |
| 5374 // tricky part is the 'jumps', where RTL and LTR text touch each | 5446 // tricky part is the 'jumps', where RTL and LTR text touch each |
| 5375 // other. This often requires the cursor offset to move more than | 5447 // other. This often requires the cursor offset to move more than |
| 5376 // one unit, in order to visually move one unit. | 5448 // one unit, in order to visually move one unit. |
| 5377 function moveVisually(line, start, dir, byUnit) { | 5449 function moveVisually(line, start, dir, byUnit) { |
| 5378 var bidi = getOrder(line); | 5450 var bidi = getOrder(line); |
| 5379 if (!bidi) return moveLogically(line, start, dir, byUnit); | 5451 if (!bidi) return moveLogically(line, start, dir, byUnit); |
| 5380 var moveOneUnit = byUnit ? function(pos, dir) { | 5452 var pos = getBidiPartAt(bidi, start), part = bidi[pos]; |
| 5381 do pos += dir; | 5453 var target = moveInLine(line, start, part.level % 2 ? -dir : dir, byUnit); |
| 5382 while (pos > 0 && isExtendingChar.test(line.text.charAt(pos))); | |
| 5383 return pos; | |
| 5384 } : function(pos, dir) { return pos + dir; }; | |
| 5385 var linedir = bidi[0].level; | |
| 5386 for (var i = 0; i < bidi.length; ++i) { | |
| 5387 var part = bidi[i], sticky = part.level % 2 == linedir; | |
| 5388 if ((part.from < start && part.to > start) || | |
| 5389 (sticky && (part.from == start || part.to == start))) break; | |
| 5390 } | |
| 5391 var target = moveOneUnit(start, part.level % 2 ? -dir : dir); | |
| 5392 | 5454 |
| 5393 while (target != null) { | 5455 for (;;) { |
| 5394 if (part.level % 2 == linedir) { | 5456 if (target > part.from && target < part.to) return target; |
| 5395 if (target < part.from || target > part.to) { | 5457 if (target == part.from || target == part.to) { |
| 5396 part = bidi[i += dir]; | 5458 if (getBidiPartAt(bidi, target) == pos) return target; |
| 5397 target = part && (dir > 0 == part.level % 2 ? moveOneUnit(part.to, -1)
: moveOneUnit(part.from, 1)); | 5459 part = bidi[pos += dir]; |
| 5398 } else break; | 5460 return (dir > 0) == part.level % 2 ? part.to : part.from; |
| 5399 } else { | 5461 } else { |
| 5400 if (target == bidiLeft(part)) { | 5462 part = bidi[pos += dir]; |
| 5401 part = bidi[--i]; | 5463 if (!part) return null; |
| 5402 target = part && bidiRight(part); | 5464 if ((dir > 0) == part.level % 2) |
| 5403 } else if (target == bidiRight(part)) { | 5465 target = moveInLine(line, part.to, -1, byUnit); |
| 5404 part = bidi[++i]; | 5466 else |
| 5405 target = part && bidiLeft(part); | 5467 target = moveInLine(line, part.from, 1, byUnit); |
| 5406 } else break; | |
| 5407 } | 5468 } |
| 5408 } | 5469 } |
| 5409 | |
| 5410 return target < 0 || target > line.text.length ? null : target; | |
| 5411 } | 5470 } |
| 5412 | 5471 |
| 5413 function moveLogically(line, start, dir, byUnit) { | 5472 function moveLogically(line, start, dir, byUnit) { |
| 5414 var target = start + dir; | 5473 var target = start + dir; |
| 5415 if (byUnit) while (target > 0 && isExtendingChar.test(line.text.charAt(targe
t))) target += dir; | 5474 if (byUnit) while (target > 0 && isExtendingChar.test(line.text.charAt(targe
t))) target += dir; |
| 5416 return target < 0 || target > line.text.length ? null : target; | 5475 return target < 0 || target > line.text.length ? null : target; |
| 5417 } | 5476 } |
| 5418 | 5477 |
| 5419 // Bidirectional ordering algorithm | 5478 // Bidirectional ordering algorithm |
| 5420 // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm | 5479 // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm |
| (...skipping 151 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 5572 } | 5631 } |
| 5573 if (order[0].level != lst(order).level) | 5632 if (order[0].level != lst(order).level) |
| 5574 order.push({from: len, to: len, level: order[0].level}); | 5633 order.push({from: len, to: len, level: order[0].level}); |
| 5575 | 5634 |
| 5576 return order; | 5635 return order; |
| 5577 }; | 5636 }; |
| 5578 })(); | 5637 })(); |
| 5579 | 5638 |
| 5580 // THE END | 5639 // THE END |
| 5581 | 5640 |
| 5582 CodeMirror.version = "3.12"; | 5641 CodeMirror.version = "3.13 +"; |
| 5583 | 5642 |
| 5584 return CodeMirror; | 5643 return CodeMirror; |
| 5585 })(); | 5644 })(); |
| OLD | NEW |