1.1 --- a/dew/src/main/resources/org/apidesign/bck2brwsr/dew/js/codemirror/codemirror.js Sat Oct 12 09:05:08 2013 +0200
1.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
1.3 @@ -1,4553 +0,0 @@
1.4 -// CodeMirror version 3.0
1.5 -//
1.6 -// CodeMirror is the only global var we claim
1.7 -window.CodeMirror = (function() {
1.8 - "use strict";
1.9 -
1.10 - // BROWSER SNIFFING
1.11 -
1.12 - // Crude, but necessary to handle a number of hard-to-feature-detect
1.13 - // bugs and behavior differences.
1.14 - var gecko = /gecko\/\d/i.test(navigator.userAgent);
1.15 - var ie = /MSIE \d/.test(navigator.userAgent);
1.16 - var ie_lt8 = /MSIE [1-7]\b/.test(navigator.userAgent);
1.17 - var ie_lt9 = /MSIE [1-8]\b/.test(navigator.userAgent);
1.18 - var webkit = /WebKit\//.test(navigator.userAgent);
1.19 - var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(navigator.userAgent);
1.20 - var chrome = /Chrome\//.test(navigator.userAgent);
1.21 - var opera = /Opera\//.test(navigator.userAgent);
1.22 - var safari = /Apple Computer/.test(navigator.vendor);
1.23 - var khtml = /KHTML\//.test(navigator.userAgent);
1.24 - var mac_geLion = /Mac OS X 1\d\D([7-9]|\d\d)\D/.test(navigator.userAgent);
1.25 - var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(navigator.userAgent);
1.26 - var phantom = /PhantomJS/.test(navigator.userAgent);
1.27 -
1.28 - var ios = /AppleWebKit/.test(navigator.userAgent) && /Mobile\/\w+/.test(navigator.userAgent);
1.29 - // This is woefully incomplete. Suggestions for alternative methods welcome.
1.30 - var mobile = ios || /Android|webOS|BlackBerry|Opera Mini|IEMobile/i.test(navigator.userAgent);
1.31 - var mac = ios || /Mac/.test(navigator.platform);
1.32 -
1.33 - // Optimize some code when these features are not used
1.34 - var sawReadOnlySpans = false, sawCollapsedSpans = false;
1.35 -
1.36 - // CONSTRUCTOR
1.37 -
1.38 - function CodeMirror(place, options) {
1.39 - if (!(this instanceof CodeMirror)) return new CodeMirror(place, options);
1.40 -
1.41 - this.options = options = options || {};
1.42 - // Determine effective options based on given values and defaults.
1.43 - for (var opt in defaults) if (!options.hasOwnProperty(opt) && defaults.hasOwnProperty(opt))
1.44 - options[opt] = defaults[opt];
1.45 - setGuttersForLineNumbers(options);
1.46 -
1.47 - var display = this.display = makeDisplay(place);
1.48 - display.wrapper.CodeMirror = this;
1.49 - updateGutters(this);
1.50 - if (options.autofocus && !mobile) focusInput(this);
1.51 -
1.52 - this.view = makeView(new BranchChunk([new LeafChunk([makeLine("", null, textHeight(display))])]));
1.53 - this.nextOpId = 0;
1.54 - loadMode(this);
1.55 - themeChanged(this);
1.56 - if (options.lineWrapping)
1.57 - this.display.wrapper.className += " CodeMirror-wrap";
1.58 -
1.59 - // Initialize the content.
1.60 - this.setValue(options.value || "");
1.61 - // Override magic textarea content restore that IE sometimes does
1.62 - // on our hidden textarea on reload
1.63 - if (ie) setTimeout(bind(resetInput, this, true), 20);
1.64 - this.view.history = makeHistory();
1.65 -
1.66 - registerEventHandlers(this);
1.67 - // IE throws unspecified error in certain cases, when
1.68 - // trying to access activeElement before onload
1.69 - var hasFocus; try { hasFocus = (document.activeElement == display.input); } catch(e) { }
1.70 - if (hasFocus || (options.autofocus && !mobile)) setTimeout(bind(onFocus, this), 20);
1.71 - else onBlur(this);
1.72 -
1.73 - operation(this, function() {
1.74 - for (var opt in optionHandlers)
1.75 - if (optionHandlers.propertyIsEnumerable(opt))
1.76 - optionHandlers[opt](this, options[opt], Init);
1.77 - for (var i = 0; i < initHooks.length; ++i) initHooks[i](this);
1.78 - })();
1.79 - }
1.80 -
1.81 - // DISPLAY CONSTRUCTOR
1.82 -
1.83 - function makeDisplay(place) {
1.84 - var d = {};
1.85 - var input = d.input = elt("textarea", null, null, "position: absolute; padding: 0; width: 1px; height: 1em; outline: none;");
1.86 - input.setAttribute("wrap", "off"); input.setAttribute("autocorrect", "off"); input.setAttribute("autocapitalize", "off");
1.87 - // Wraps and hides input textarea
1.88 - d.inputDiv = elt("div", [input], null, "overflow: hidden; position: relative; width: 3px; height: 0px;");
1.89 - // The actual fake scrollbars.
1.90 - d.scrollbarH = elt("div", [elt("div", null, null, "height: 1px")], "CodeMirror-hscrollbar");
1.91 - d.scrollbarV = elt("div", [elt("div", null, null, "width: 1px")], "CodeMirror-vscrollbar");
1.92 - d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler");
1.93 - // DIVs containing the selection and the actual code
1.94 - d.lineDiv = elt("div");
1.95 - d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1");
1.96 - // Blinky cursor, and element used to ensure cursor fits at the end of a line
1.97 - d.cursor = elt("pre", "\u00a0", "CodeMirror-cursor");
1.98 - // Secondary cursor, shown when on a 'jump' in bi-directional text
1.99 - d.otherCursor = elt("pre", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor");
1.100 - // Used to measure text size
1.101 - d.measure = elt("div", null, "CodeMirror-measure");
1.102 - // Wraps everything that needs to exist inside the vertically-padded coordinate system
1.103 - d.lineSpace = elt("div", [d.measure, d.selectionDiv, d.lineDiv, d.cursor, d.otherCursor],
1.104 - null, "position: relative; outline: none");
1.105 - // Moved around its parent to cover visible view
1.106 - d.mover = elt("div", [elt("div", [d.lineSpace], "CodeMirror-lines")], null, "position: relative");
1.107 - // Set to the height of the text, causes scrolling
1.108 - d.sizer = elt("div", [d.mover], "CodeMirror-sizer");
1.109 - // D is needed because behavior of elts with overflow: auto and padding is inconsistent across browsers
1.110 - d.heightForcer = elt("div", "\u00a0", null, "position: absolute; height: " + scrollerCutOff + "px");
1.111 - // Will contain the gutters, if any
1.112 - d.gutters = elt("div", null, "CodeMirror-gutters");
1.113 - d.lineGutter = null;
1.114 - // Helper element to properly size the gutter backgrounds
1.115 - var scrollerInner = elt("div", [d.sizer, d.heightForcer, d.gutters], null, "position: relative; min-height: 100%");
1.116 - // Provides scrolling
1.117 - d.scroller = elt("div", [scrollerInner], "CodeMirror-scroll");
1.118 - d.scroller.setAttribute("tabIndex", "-1");
1.119 - // The element in which the editor lives.
1.120 - d.wrapper = elt("div", [d.inputDiv, d.scrollbarH, d.scrollbarV,
1.121 - d.scrollbarFiller, d.scroller], "CodeMirror");
1.122 - // Work around IE7 z-index bug
1.123 - if (ie_lt8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; }
1.124 - if (place.appendChild) place.appendChild(d.wrapper); else place(d.wrapper);
1.125 -
1.126 - // Needed to hide big blue blinking cursor on Mobile Safari
1.127 - if (ios) input.style.width = "0px";
1.128 - if (!webkit) d.scroller.draggable = true;
1.129 - // Needed to handle Tab key in KHTML
1.130 - if (khtml) { d.inputDiv.style.height = "1px"; d.inputDiv.style.position = "absolute"; }
1.131 - // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
1.132 - else if (ie_lt8) d.scrollbarH.style.minWidth = d.scrollbarV.style.minWidth = "18px";
1.133 -
1.134 - // Current visible range (may be bigger than the view window).
1.135 - d.viewOffset = d.showingFrom = d.showingTo = d.lastSizeC = 0;
1.136 -
1.137 - // Used to only resize the line number gutter when necessary (when
1.138 - // the amount of lines crosses a boundary that makes its width change)
1.139 - d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null;
1.140 - // See readInput and resetInput
1.141 - d.prevInput = "";
1.142 - // Set to true when a non-horizontal-scrolling widget is added. As
1.143 - // an optimization, widget aligning is skipped when d is false.
1.144 - d.alignWidgets = false;
1.145 - // Flag that indicates whether we currently expect input to appear
1.146 - // (after some event like 'keypress' or 'input') and are polling
1.147 - // intensively.
1.148 - d.pollingFast = false;
1.149 - // Self-resetting timeout for the poller
1.150 - d.poll = new Delayed();
1.151 - // True when a drag from the editor is active
1.152 - d.draggingText = false;
1.153 -
1.154 - d.cachedCharWidth = d.cachedTextHeight = null;
1.155 - d.measureLineCache = [];
1.156 - d.measureLineCachePos = 0;
1.157 -
1.158 - // Tracks when resetInput has punted to just putting a short
1.159 - // string instead of the (large) selection.
1.160 - d.inaccurateSelection = false;
1.161 -
1.162 - // Used to adjust overwrite behaviour when a paste has been
1.163 - // detected
1.164 - d.pasteIncoming = false;
1.165 -
1.166 - return d;
1.167 - }
1.168 -
1.169 - // VIEW CONSTRUCTOR
1.170 -
1.171 - function makeView(doc) {
1.172 - var selPos = {line: 0, ch: 0};
1.173 - return {
1.174 - doc: doc,
1.175 - // frontier is the point up to which the content has been parsed,
1.176 - frontier: 0, highlight: new Delayed(),
1.177 - sel: {from: selPos, to: selPos, head: selPos, anchor: selPos, shift: false, extend: false},
1.178 - scrollTop: 0, scrollLeft: 0,
1.179 - overwrite: false, focused: false,
1.180 - // Tracks the maximum line length so that
1.181 - // the horizontal scrollbar can be kept
1.182 - // static when scrolling.
1.183 - maxLine: getLine(doc, 0),
1.184 - maxLineLength: 0,
1.185 - maxLineChanged: false,
1.186 - suppressEdits: false,
1.187 - goalColumn: null,
1.188 - cantEdit: false,
1.189 - keyMaps: []
1.190 - };
1.191 - }
1.192 -
1.193 - // STATE UPDATES
1.194 -
1.195 - // Used to get the editor into a consistent state again when options change.
1.196 -
1.197 - function loadMode(cm) {
1.198 - var doc = cm.view.doc;
1.199 - cm.view.mode = CodeMirror.getMode(cm.options, cm.options.mode);
1.200 - doc.iter(0, doc.size, function(line) { line.stateAfter = null; });
1.201 - cm.view.frontier = 0;
1.202 - startWorker(cm, 100);
1.203 - }
1.204 -
1.205 - function wrappingChanged(cm) {
1.206 - var doc = cm.view.doc, th = textHeight(cm.display);
1.207 - if (cm.options.lineWrapping) {
1.208 - cm.display.wrapper.className += " CodeMirror-wrap";
1.209 - var perLine = cm.display.scroller.clientWidth / charWidth(cm.display) - 3;
1.210 - doc.iter(0, doc.size, function(line) {
1.211 - if (line.height == 0) return;
1.212 - var guess = Math.ceil(line.text.length / perLine) || 1;
1.213 - if (guess != 1) updateLineHeight(line, guess * th);
1.214 - });
1.215 - cm.display.sizer.style.minWidth = "";
1.216 - } else {
1.217 - cm.display.wrapper.className = cm.display.wrapper.className.replace(" CodeMirror-wrap", "");
1.218 - computeMaxLength(cm.view);
1.219 - doc.iter(0, doc.size, function(line) {
1.220 - if (line.height != 0) updateLineHeight(line, th);
1.221 - });
1.222 - }
1.223 - regChange(cm, 0, doc.size);
1.224 - clearCaches(cm);
1.225 - setTimeout(function(){updateScrollbars(cm.display, cm.view.doc.height);}, 100);
1.226 - }
1.227 -
1.228 - function keyMapChanged(cm) {
1.229 - var style = keyMap[cm.options.keyMap].style;
1.230 - cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-keymap-\S+/g, "") +
1.231 - (style ? " cm-keymap-" + style : "");
1.232 - }
1.233 -
1.234 - function themeChanged(cm) {
1.235 - cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") +
1.236 - cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-");
1.237 - clearCaches(cm);
1.238 - }
1.239 -
1.240 - function guttersChanged(cm) {
1.241 - updateGutters(cm);
1.242 - updateDisplay(cm, true);
1.243 - }
1.244 -
1.245 - function updateGutters(cm) {
1.246 - var gutters = cm.display.gutters, specs = cm.options.gutters;
1.247 - removeChildren(gutters);
1.248 - for (var i = 0; i < specs.length; ++i) {
1.249 - var gutterClass = specs[i];
1.250 - var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + gutterClass));
1.251 - if (gutterClass == "CodeMirror-linenumbers") {
1.252 - cm.display.lineGutter = gElt;
1.253 - gElt.style.width = (cm.display.lineNumWidth || 1) + "px";
1.254 - }
1.255 - }
1.256 - gutters.style.display = i ? "" : "none";
1.257 - }
1.258 -
1.259 - function lineLength(doc, line) {
1.260 - if (line.height == 0) return 0;
1.261 - var len = line.text.length, merged, cur = line;
1.262 - while (merged = collapsedSpanAtStart(cur)) {
1.263 - var found = merged.find();
1.264 - cur = getLine(doc, found.from.line);
1.265 - len += found.from.ch - found.to.ch;
1.266 - }
1.267 - cur = line;
1.268 - while (merged = collapsedSpanAtEnd(cur)) {
1.269 - var found = merged.find();
1.270 - len -= cur.text.length - found.from.ch;
1.271 - cur = getLine(doc, found.to.line);
1.272 - len += cur.text.length - found.to.ch;
1.273 - }
1.274 - return len;
1.275 - }
1.276 -
1.277 - function computeMaxLength(view) {
1.278 - view.maxLine = getLine(view.doc, 0);
1.279 - view.maxLineLength = lineLength(view.doc, view.maxLine);
1.280 - view.maxLineChanged = true;
1.281 - view.doc.iter(1, view.doc.size, function(line) {
1.282 - var len = lineLength(view.doc, line);
1.283 - if (len > view.maxLineLength) {
1.284 - view.maxLineLength = len;
1.285 - view.maxLine = line;
1.286 - }
1.287 - });
1.288 - }
1.289 -
1.290 - // Make sure the gutters options contains the element
1.291 - // "CodeMirror-linenumbers" when the lineNumbers option is true.
1.292 - function setGuttersForLineNumbers(options) {
1.293 - var found = false;
1.294 - for (var i = 0; i < options.gutters.length; ++i) {
1.295 - if (options.gutters[i] == "CodeMirror-linenumbers") {
1.296 - if (options.lineNumbers) found = true;
1.297 - else options.gutters.splice(i--, 1);
1.298 - }
1.299 - }
1.300 - if (!found && options.lineNumbers)
1.301 - options.gutters.push("CodeMirror-linenumbers");
1.302 - }
1.303 -
1.304 - // SCROLLBARS
1.305 -
1.306 - // Re-synchronize the fake scrollbars with the actual size of the
1.307 - // content. Optionally force a scrollTop.
1.308 - function updateScrollbars(d /* display */, docHeight) {
1.309 - var totalHeight = docHeight + 2 * paddingTop(d);
1.310 - d.sizer.style.minHeight = d.heightForcer.style.top = totalHeight + "px";
1.311 - var scrollHeight = Math.max(totalHeight, d.scroller.scrollHeight);
1.312 - var needsH = d.scroller.scrollWidth > d.scroller.clientWidth;
1.313 - var needsV = scrollHeight > d.scroller.clientHeight;
1.314 - if (needsV) {
1.315 - d.scrollbarV.style.display = "block";
1.316 - d.scrollbarV.style.bottom = needsH ? scrollbarWidth(d.measure) + "px" : "0";
1.317 - d.scrollbarV.firstChild.style.height =
1.318 - (scrollHeight - d.scroller.clientHeight + d.scrollbarV.clientHeight) + "px";
1.319 - } else d.scrollbarV.style.display = "";
1.320 - if (needsH) {
1.321 - d.scrollbarH.style.display = "block";
1.322 - d.scrollbarH.style.right = needsV ? scrollbarWidth(d.measure) + "px" : "0";
1.323 - d.scrollbarH.firstChild.style.width =
1.324 - (d.scroller.scrollWidth - d.scroller.clientWidth + d.scrollbarH.clientWidth) + "px";
1.325 - } else d.scrollbarH.style.display = "";
1.326 - if (needsH && needsV) {
1.327 - d.scrollbarFiller.style.display = "block";
1.328 - d.scrollbarFiller.style.height = d.scrollbarFiller.style.width = scrollbarWidth(d.measure) + "px";
1.329 - } else d.scrollbarFiller.style.display = "";
1.330 -
1.331 - if (mac_geLion && scrollbarWidth(d.measure) === 0)
1.332 - d.scrollbarV.style.minWidth = d.scrollbarH.style.minHeight = mac_geMountainLion ? "18px" : "12px";
1.333 - }
1.334 -
1.335 - function visibleLines(display, doc, viewPort) {
1.336 - var top = display.scroller.scrollTop, height = display.wrapper.clientHeight;
1.337 - if (typeof viewPort == "number") top = viewPort;
1.338 - else if (viewPort) {top = viewPort.top; height = viewPort.bottom - viewPort.top;}
1.339 - top = Math.floor(top - paddingTop(display));
1.340 - var bottom = Math.ceil(top + height);
1.341 - return {from: lineAtHeight(doc, top), to: lineAtHeight(doc, bottom)};
1.342 - }
1.343 -
1.344 - // LINE NUMBERS
1.345 -
1.346 - function alignHorizontally(cm) {
1.347 - var display = cm.display;
1.348 - if (!display.alignWidgets && !display.gutters.firstChild) return;
1.349 - var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.view.scrollLeft;
1.350 - var gutterW = display.gutters.offsetWidth, l = comp + "px";
1.351 - for (var n = display.lineDiv.firstChild; n; n = n.nextSibling) if (n.alignable) {
1.352 - for (var i = 0, a = n.alignable; i < a.length; ++i) a[i].style.left = l;
1.353 - }
1.354 - display.gutters.style.left = (comp + gutterW) + "px";
1.355 - }
1.356 -
1.357 - function maybeUpdateLineNumberWidth(cm) {
1.358 - if (!cm.options.lineNumbers) return false;
1.359 - var doc = cm.view.doc, last = lineNumberFor(cm.options, doc.size - 1), display = cm.display;
1.360 - if (last.length != display.lineNumChars) {
1.361 - var test = display.measure.appendChild(elt("div", [elt("div", last)],
1.362 - "CodeMirror-linenumber CodeMirror-gutter-elt"));
1.363 - var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW;
1.364 - display.lineGutter.style.width = "";
1.365 - display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding);
1.366 - display.lineNumWidth = display.lineNumInnerWidth + padding;
1.367 - display.lineNumChars = display.lineNumInnerWidth ? last.length : -1;
1.368 - display.lineGutter.style.width = display.lineNumWidth + "px";
1.369 - return true;
1.370 - }
1.371 - return false;
1.372 - }
1.373 -
1.374 - function lineNumberFor(options, i) {
1.375 - return String(options.lineNumberFormatter(i + options.firstLineNumber));
1.376 - }
1.377 - function compensateForHScroll(display) {
1.378 - return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left;
1.379 - }
1.380 -
1.381 - // DISPLAY DRAWING
1.382 -
1.383 - function updateDisplay(cm, changes, viewPort) {
1.384 - var oldFrom = cm.display.showingFrom, oldTo = cm.display.showingTo;
1.385 - var updated = updateDisplayInner(cm, changes, viewPort);
1.386 - if (updated) {
1.387 - signalLater(cm, cm, "update", cm);
1.388 - if (cm.display.showingFrom != oldFrom || cm.display.showingTo != oldTo)
1.389 - signalLater(cm, cm, "viewportChange", cm, cm.display.showingFrom, cm.display.showingTo);
1.390 - }
1.391 - updateSelection(cm);
1.392 - updateScrollbars(cm.display, cm.view.doc.height);
1.393 -
1.394 - return updated;
1.395 - }
1.396 -
1.397 - // Uses a set of changes plus the current scroll position to
1.398 - // determine which DOM updates have to be made, and makes the
1.399 - // updates.
1.400 - function updateDisplayInner(cm, changes, viewPort) {
1.401 - var display = cm.display, doc = cm.view.doc;
1.402 - if (!display.wrapper.clientWidth) {
1.403 - display.showingFrom = display.showingTo = display.viewOffset = 0;
1.404 - return;
1.405 - }
1.406 -
1.407 - // Compute the new visible window
1.408 - // If scrollTop is specified, use that to determine which lines
1.409 - // to render instead of the current scrollbar position.
1.410 - var visible = visibleLines(display, doc, viewPort);
1.411 - // Bail out if the visible area is already rendered and nothing changed.
1.412 - if (changes !== true && changes.length == 0 &&
1.413 - visible.from > display.showingFrom && visible.to < display.showingTo)
1.414 - return;
1.415 -
1.416 - if (changes && maybeUpdateLineNumberWidth(cm))
1.417 - changes = true;
1.418 - display.sizer.style.marginLeft = display.scrollbarH.style.left = display.gutters.offsetWidth + "px";
1.419 -
1.420 - // When merged lines are present, the line that needs to be
1.421 - // redrawn might not be the one that was changed.
1.422 - if (changes !== true && sawCollapsedSpans)
1.423 - for (var i = 0; i < changes.length; ++i) {
1.424 - var ch = changes[i], merged;
1.425 - while (merged = collapsedSpanAtStart(getLine(doc, ch.from))) {
1.426 - var from = merged.find().from.line;
1.427 - if (ch.diff) ch.diff -= ch.from - from;
1.428 - ch.from = from;
1.429 - }
1.430 - }
1.431 -
1.432 - // Used to determine which lines need their line numbers updated
1.433 - var positionsChangedFrom = changes === true ? 0 : Infinity;
1.434 - if (cm.options.lineNumbers && changes && changes !== true)
1.435 - for (var i = 0; i < changes.length; ++i)
1.436 - if (changes[i].diff) { positionsChangedFrom = changes[i].from; break; }
1.437 -
1.438 - var from = Math.max(visible.from - cm.options.viewportMargin, 0);
1.439 - var to = Math.min(doc.size, visible.to + cm.options.viewportMargin);
1.440 - if (display.showingFrom < from && from - display.showingFrom < 20) from = display.showingFrom;
1.441 - if (display.showingTo > to && display.showingTo - to < 20) to = Math.min(doc.size, display.showingTo);
1.442 - if (sawCollapsedSpans) {
1.443 - from = lineNo(visualLine(doc, getLine(doc, from)));
1.444 - while (to < doc.size && lineIsHidden(getLine(doc, to))) ++to;
1.445 - }
1.446 -
1.447 - // Create a range of theoretically intact lines, and punch holes
1.448 - // in that using the change info.
1.449 - var intact = changes === true ? [] :
1.450 - computeIntact([{from: display.showingFrom, to: display.showingTo}], changes);
1.451 - // Clip off the parts that won't be visible
1.452 - var intactLines = 0;
1.453 - for (var i = 0; i < intact.length; ++i) {
1.454 - var range = intact[i];
1.455 - if (range.from < from) range.from = from;
1.456 - if (range.to > to) range.to = to;
1.457 - if (range.from >= range.to) intact.splice(i--, 1);
1.458 - else intactLines += range.to - range.from;
1.459 - }
1.460 - if (intactLines == to - from && from == display.showingFrom && to == display.showingTo)
1.461 - return;
1.462 - intact.sort(function(a, b) {return a.from - b.from;});
1.463 -
1.464 - if (intactLines < (to - from) * .7) display.lineDiv.style.display = "none";
1.465 - patchDisplay(cm, from, to, intact, positionsChangedFrom);
1.466 - display.lineDiv.style.display = "";
1.467 -
1.468 - var different = from != display.showingFrom || to != display.showingTo ||
1.469 - display.lastSizeC != display.wrapper.clientHeight;
1.470 - // This is just a bogus formula that detects when the editor is
1.471 - // resized or the font size changes.
1.472 - if (different) display.lastSizeC = display.wrapper.clientHeight;
1.473 - display.showingFrom = from; display.showingTo = to;
1.474 - startWorker(cm, 100);
1.475 -
1.476 - var prevBottom = display.lineDiv.offsetTop;
1.477 - for (var node = display.lineDiv.firstChild, height; node; node = node.nextSibling) if (node.lineObj) {
1.478 - if (ie_lt8) {
1.479 - var bot = node.offsetTop + node.offsetHeight;
1.480 - height = bot - prevBottom;
1.481 - prevBottom = bot;
1.482 - } else {
1.483 - var box = node.getBoundingClientRect();
1.484 - height = box.bottom - box.top;
1.485 - }
1.486 - var diff = node.lineObj.height - height;
1.487 - if (height < 2) height = textHeight(display);
1.488 - if (diff > .001 || diff < -.001)
1.489 - updateLineHeight(node.lineObj, height);
1.490 - }
1.491 - display.viewOffset = heightAtLine(cm, getLine(doc, from));
1.492 - // Position the mover div to align with the current virtual scroll position
1.493 - display.mover.style.top = display.viewOffset + "px";
1.494 - return true;
1.495 - }
1.496 -
1.497 - function computeIntact(intact, changes) {
1.498 - for (var i = 0, l = changes.length || 0; i < l; ++i) {
1.499 - var change = changes[i], intact2 = [], diff = change.diff || 0;
1.500 - for (var j = 0, l2 = intact.length; j < l2; ++j) {
1.501 - var range = intact[j];
1.502 - if (change.to <= range.from && change.diff) {
1.503 - intact2.push({from: range.from + diff, to: range.to + diff});
1.504 - } else if (change.to <= range.from || change.from >= range.to) {
1.505 - intact2.push(range);
1.506 - } else {
1.507 - if (change.from > range.from)
1.508 - intact2.push({from: range.from, to: change.from});
1.509 - if (change.to < range.to)
1.510 - intact2.push({from: change.to + diff, to: range.to + diff});
1.511 - }
1.512 - }
1.513 - intact = intact2;
1.514 - }
1.515 - return intact;
1.516 - }
1.517 -
1.518 - function getDimensions(cm) {
1.519 - var d = cm.display, left = {}, width = {};
1.520 - for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) {
1.521 - left[cm.options.gutters[i]] = n.offsetLeft;
1.522 - width[cm.options.gutters[i]] = n.offsetWidth;
1.523 - }
1.524 - return {fixedPos: compensateForHScroll(d),
1.525 - gutterTotalWidth: d.gutters.offsetWidth,
1.526 - gutterLeft: left,
1.527 - gutterWidth: width,
1.528 - wrapperWidth: d.wrapper.clientWidth};
1.529 - }
1.530 -
1.531 - function patchDisplay(cm, from, to, intact, updateNumbersFrom) {
1.532 - var dims = getDimensions(cm);
1.533 - var display = cm.display, lineNumbers = cm.options.lineNumbers;
1.534 - // IE does bad things to nodes when .innerHTML = "" is used on a parent
1.535 - // we still need widgets and markers intact to add back to the new content later
1.536 - if (!intact.length && !ie && (!webkit || !cm.display.currentWheelTarget))
1.537 - removeChildren(display.lineDiv);
1.538 - var container = display.lineDiv, cur = container.firstChild;
1.539 -
1.540 - function rm(node) {
1.541 - var next = node.nextSibling;
1.542 - if (webkit && mac && cm.display.currentWheelTarget == node) {
1.543 - node.style.display = "none";
1.544 - node.lineObj = null;
1.545 - } else {
1.546 - container.removeChild(node);
1.547 - }
1.548 - return next;
1.549 - }
1.550 -
1.551 - var nextIntact = intact.shift(), lineNo = from;
1.552 - cm.view.doc.iter(from, to, function(line) {
1.553 - if (nextIntact && nextIntact.to == lineNo) nextIntact = intact.shift();
1.554 - if (lineIsHidden(line)) {
1.555 - if (line.height != 0) updateLineHeight(line, 0);
1.556 - } else if (nextIntact && nextIntact.from <= lineNo && nextIntact.to > lineNo) {
1.557 - // This line is intact. Skip to the actual node. Update its
1.558 - // line number if needed.
1.559 - while (cur.lineObj != line) cur = rm(cur);
1.560 - if (lineNumbers && updateNumbersFrom <= lineNo && cur.lineNumber)
1.561 - setTextContent(cur.lineNumber, lineNumberFor(cm.options, lineNo));
1.562 - cur = cur.nextSibling;
1.563 - } else {
1.564 - // This line needs to be generated.
1.565 - var lineNode = buildLineElement(cm, line, lineNo, dims);
1.566 - container.insertBefore(lineNode, cur);
1.567 - lineNode.lineObj = line;
1.568 - }
1.569 - ++lineNo;
1.570 - });
1.571 - while (cur) cur = rm(cur);
1.572 - }
1.573 -
1.574 - function buildLineElement(cm, line, lineNo, dims) {
1.575 - var lineElement = lineContent(cm, line);
1.576 - var markers = line.gutterMarkers, display = cm.display;
1.577 -
1.578 - if (!cm.options.lineNumbers && !markers && !line.bgClass && !line.wrapClass &&
1.579 - (!line.widgets || !line.widgets.length)) return lineElement;
1.580 -
1.581 - // Lines with gutter elements or a background class need
1.582 - // to be wrapped again, and have the extra elements added
1.583 - // to the wrapper div
1.584 -
1.585 - var wrap = elt("div", null, line.wrapClass, "position: relative");
1.586 - if (cm.options.lineNumbers || markers) {
1.587 - var gutterWrap = wrap.appendChild(elt("div", null, null, "position: absolute; left: " +
1.588 - dims.fixedPos + "px"));
1.589 - wrap.alignable = [gutterWrap];
1.590 - if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"]))
1.591 - wrap.lineNumber = gutterWrap.appendChild(
1.592 - elt("div", lineNumberFor(cm.options, lineNo),
1.593 - "CodeMirror-linenumber CodeMirror-gutter-elt",
1.594 - "left: " + dims.gutterLeft["CodeMirror-linenumbers"] + "px; width: "
1.595 - + display.lineNumInnerWidth + "px"));
1.596 - if (markers)
1.597 - for (var k = 0; k < cm.options.gutters.length; ++k) {
1.598 - var id = cm.options.gutters[k], found = markers.hasOwnProperty(id) && markers[id];
1.599 - if (found)
1.600 - gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt", "left: " +
1.601 - dims.gutterLeft[id] + "px; width: " + dims.gutterWidth[id] + "px"));
1.602 - }
1.603 - }
1.604 - // Kludge to make sure the styled element lies behind the selection (by z-index)
1.605 - if (line.bgClass)
1.606 - wrap.appendChild(elt("div", "\u00a0", line.bgClass + " CodeMirror-linebackground"));
1.607 - wrap.appendChild(lineElement);
1.608 - if (line.widgets)
1.609 - for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
1.610 - var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget");
1.611 - node.widget = widget;
1.612 - if (widget.noHScroll) {
1.613 - (wrap.alignable || (wrap.alignable = [])).push(node);
1.614 - var width = dims.wrapperWidth;
1.615 - node.style.left = dims.fixedPos + "px";
1.616 - if (!widget.coverGutter) {
1.617 - width -= dims.gutterTotalWidth;
1.618 - node.style.paddingLeft = dims.gutterTotalWidth + "px";
1.619 - }
1.620 - node.style.width = width + "px";
1.621 - }
1.622 - if (widget.coverGutter) {
1.623 - node.style.zIndex = 5;
1.624 - node.style.position = "relative";
1.625 - if (!widget.noHScroll) node.style.marginLeft = -dims.gutterTotalWidth + "px";
1.626 - }
1.627 - if (widget.above)
1.628 - wrap.insertBefore(node, cm.options.lineNumbers && line.height != 0 ? gutterWrap : lineElement);
1.629 - else
1.630 - wrap.appendChild(node);
1.631 - }
1.632 -
1.633 - if (ie_lt8) wrap.style.zIndex = 2;
1.634 - return wrap;
1.635 - }
1.636 -
1.637 - // SELECTION / CURSOR
1.638 -
1.639 - function updateSelection(cm) {
1.640 - var display = cm.display;
1.641 - var collapsed = posEq(cm.view.sel.from, cm.view.sel.to);
1.642 - if (collapsed || cm.options.showCursorWhenSelecting)
1.643 - updateSelectionCursor(cm);
1.644 - else
1.645 - display.cursor.style.display = display.otherCursor.style.display = "none";
1.646 - if (!collapsed)
1.647 - updateSelectionRange(cm);
1.648 - else
1.649 - display.selectionDiv.style.display = "none";
1.650 -
1.651 - // Move the hidden textarea near the cursor to prevent scrolling artifacts
1.652 - var headPos = cursorCoords(cm, cm.view.sel.head, "div");
1.653 - var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect();
1.654 - display.inputDiv.style.top = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
1.655 - headPos.top + lineOff.top - wrapOff.top)) + "px";
1.656 - display.inputDiv.style.left = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
1.657 - headPos.left + lineOff.left - wrapOff.left)) + "px";
1.658 - }
1.659 -
1.660 - // No selection, plain cursor
1.661 - function updateSelectionCursor(cm) {
1.662 - var display = cm.display, pos = cursorCoords(cm, cm.view.sel.head, "div");
1.663 - display.cursor.style.left = pos.left + "px";
1.664 - display.cursor.style.top = pos.top + "px";
1.665 - display.cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px";
1.666 - display.cursor.style.display = "";
1.667 -
1.668 - if (pos.other) {
1.669 - display.otherCursor.style.display = "";
1.670 - display.otherCursor.style.left = pos.other.left + "px";
1.671 - display.otherCursor.style.top = pos.other.top + "px";
1.672 - display.otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px";
1.673 - } else { display.otherCursor.style.display = "none"; }
1.674 - }
1.675 -
1.676 - // Highlight selection
1.677 - function updateSelectionRange(cm) {
1.678 - var display = cm.display, doc = cm.view.doc, sel = cm.view.sel;
1.679 - var fragment = document.createDocumentFragment();
1.680 - var clientWidth = display.lineSpace.offsetWidth, pl = paddingLeft(cm.display);
1.681 -
1.682 - function add(left, top, width, bottom) {
1.683 - if (top < 0) top = 0;
1.684 - fragment.appendChild(elt("div", null, "CodeMirror-selected", "position: absolute; left: " + left +
1.685 - "px; top: " + top + "px; width: " + (width == null ? clientWidth - left : width) +
1.686 - "px; height: " + (bottom - top) + "px"));
1.687 - }
1.688 -
1.689 - function drawForLine(line, fromArg, toArg, retTop) {
1.690 - var lineObj = getLine(doc, line);
1.691 - var lineLen = lineObj.text.length, rVal = retTop ? Infinity : -Infinity;
1.692 - function coords(ch) {
1.693 - return charCoords(cm, {line: line, ch: ch}, "div", lineObj);
1.694 - }
1.695 -
1.696 - iterateBidiSections(getOrder(lineObj), fromArg || 0, toArg == null ? lineLen : toArg, function(from, to, dir) {
1.697 - var leftPos = coords(dir == "rtl" ? to - 1 : from);
1.698 - var rightPos = coords(dir == "rtl" ? from : to - 1);
1.699 - var left = leftPos.left, right = rightPos.right;
1.700 - if (rightPos.top - leftPos.top > 3) { // Different lines, draw top part
1.701 - add(left, leftPos.top, null, leftPos.bottom);
1.702 - left = pl;
1.703 - if (leftPos.bottom < rightPos.top) add(left, leftPos.bottom, null, rightPos.top);
1.704 - }
1.705 - if (toArg == null && to == lineLen) right = clientWidth;
1.706 - if (fromArg == null && from == 0) left = pl;
1.707 - rVal = retTop ? Math.min(rightPos.top, rVal) : Math.max(rightPos.bottom, rVal);
1.708 - if (left < pl + 1) left = pl;
1.709 - add(left, rightPos.top, right - left, rightPos.bottom);
1.710 - });
1.711 - return rVal;
1.712 - }
1.713 -
1.714 - if (sel.from.line == sel.to.line) {
1.715 - drawForLine(sel.from.line, sel.from.ch, sel.to.ch);
1.716 - } else {
1.717 - var fromObj = getLine(doc, sel.from.line);
1.718 - var cur = fromObj, merged, path = [sel.from.line, sel.from.ch], singleLine;
1.719 - while (merged = collapsedSpanAtEnd(cur)) {
1.720 - var found = merged.find();
1.721 - path.push(found.from.ch, found.to.line, found.to.ch);
1.722 - if (found.to.line == sel.to.line) {
1.723 - path.push(sel.to.ch);
1.724 - singleLine = true;
1.725 - break;
1.726 - }
1.727 - cur = getLine(doc, found.to.line);
1.728 - }
1.729 -
1.730 - // This is a single, merged line
1.731 - if (singleLine) {
1.732 - for (var i = 0; i < path.length; i += 3)
1.733 - drawForLine(path[i], path[i+1], path[i+2]);
1.734 - } else {
1.735 - var middleTop, middleBot, toObj = getLine(doc, sel.to.line);
1.736 - if (sel.from.ch)
1.737 - // Draw the first line of selection.
1.738 - middleTop = drawForLine(sel.from.line, sel.from.ch, null, false);
1.739 - else
1.740 - // Simply include it in the middle block.
1.741 - middleTop = heightAtLine(cm, fromObj) - display.viewOffset;
1.742 -
1.743 - if (!sel.to.ch)
1.744 - middleBot = heightAtLine(cm, toObj) - display.viewOffset;
1.745 - else
1.746 - middleBot = drawForLine(sel.to.line, collapsedSpanAtStart(toObj) ? null : 0, sel.to.ch, true);
1.747 -
1.748 - if (middleTop < middleBot) add(pl, middleTop, null, middleBot);
1.749 - }
1.750 - }
1.751 -
1.752 - removeChildrenAndAdd(display.selectionDiv, fragment);
1.753 - display.selectionDiv.style.display = "";
1.754 - }
1.755 -
1.756 - // Cursor-blinking
1.757 - function restartBlink(cm) {
1.758 - var display = cm.display;
1.759 - clearInterval(display.blinker);
1.760 - var on = true;
1.761 - display.cursor.style.visibility = display.otherCursor.style.visibility = "";
1.762 - display.blinker = setInterval(function() {
1.763 - if (!display.cursor.offsetHeight) return;
1.764 - display.cursor.style.visibility = display.otherCursor.style.visibility = (on = !on) ? "" : "hidden";
1.765 - }, cm.options.cursorBlinkRate);
1.766 - }
1.767 -
1.768 - // HIGHLIGHT WORKER
1.769 -
1.770 - function startWorker(cm, time) {
1.771 - if (cm.view.frontier < cm.display.showingTo)
1.772 - cm.view.highlight.set(time, bind(highlightWorker, cm));
1.773 - }
1.774 -
1.775 - function highlightWorker(cm) {
1.776 - var view = cm.view, doc = view.doc;
1.777 - if (view.frontier >= cm.display.showingTo) return;
1.778 - var end = +new Date + cm.options.workTime;
1.779 - var state = copyState(view.mode, getStateBefore(cm, view.frontier));
1.780 - var changed = [], prevChange;
1.781 - doc.iter(view.frontier, Math.min(doc.size, cm.display.showingTo + 500), function(line) {
1.782 - if (view.frontier >= cm.display.showingFrom) { // Visible
1.783 - if (highlightLine(cm, line, state) && view.frontier >= cm.display.showingFrom) {
1.784 - if (prevChange && prevChange.end == view.frontier) prevChange.end++;
1.785 - else changed.push(prevChange = {start: view.frontier, end: view.frontier + 1});
1.786 - }
1.787 - line.stateAfter = copyState(view.mode, state);
1.788 - } else {
1.789 - processLine(cm, line, state);
1.790 - line.stateAfter = view.frontier % 5 == 0 ? copyState(view.mode, state) : null;
1.791 - }
1.792 - ++view.frontier;
1.793 - if (+new Date > end) {
1.794 - startWorker(cm, cm.options.workDelay);
1.795 - return true;
1.796 - }
1.797 - });
1.798 - if (changed.length)
1.799 - operation(cm, function() {
1.800 - for (var i = 0; i < changed.length; ++i)
1.801 - regChange(this, changed[i].start, changed[i].end);
1.802 - })();
1.803 - }
1.804 -
1.805 - // Finds the line to start with when starting a parse. Tries to
1.806 - // find a line with a stateAfter, so that it can start with a
1.807 - // valid state. If that fails, it returns the line with the
1.808 - // smallest indentation, which tends to need the least context to
1.809 - // parse correctly.
1.810 - function findStartLine(cm, n) {
1.811 - var minindent, minline, doc = cm.view.doc;
1.812 - for (var search = n, lim = n - 100; search > lim; --search) {
1.813 - if (search == 0) return 0;
1.814 - var line = getLine(doc, search-1);
1.815 - if (line.stateAfter) return search;
1.816 - var indented = countColumn(line.text, null, cm.options.tabSize);
1.817 - if (minline == null || minindent > indented) {
1.818 - minline = search - 1;
1.819 - minindent = indented;
1.820 - }
1.821 - }
1.822 - return minline;
1.823 - }
1.824 -
1.825 - function getStateBefore(cm, n) {
1.826 - var view = cm.view;
1.827 - var pos = findStartLine(cm, n), state = pos && getLine(view.doc, pos-1).stateAfter;
1.828 - if (!state) state = startState(view.mode);
1.829 - else state = copyState(view.mode, state);
1.830 - view.doc.iter(pos, n, function(line) {
1.831 - processLine(cm, line, state);
1.832 - var save = pos == n - 1 || pos % 5 == 0 || pos >= view.showingFrom && pos < view.showingTo;
1.833 - line.stateAfter = save ? copyState(view.mode, state) : null;
1.834 - ++pos;
1.835 - });
1.836 - return state;
1.837 - }
1.838 -
1.839 - // POSITION MEASUREMENT
1.840 -
1.841 - function paddingTop(display) {return display.lineSpace.offsetTop;}
1.842 - function paddingLeft(display) {
1.843 - var e = removeChildrenAndAdd(display.measure, elt("pre")).appendChild(elt("span", "x"));
1.844 - return e.offsetLeft;
1.845 - }
1.846 -
1.847 - function measureChar(cm, line, ch, data) {
1.848 - var data = data || measureLine(cm, line), dir = -1;
1.849 - for (var pos = ch;; pos += dir) {
1.850 - var r = data[pos];
1.851 - if (r) break;
1.852 - if (dir < 0 && pos == 0) dir = 1;
1.853 - }
1.854 - return {left: pos < ch ? r.right : r.left,
1.855 - right: pos > ch ? r.left : r.right,
1.856 - top: r.top, bottom: r.bottom};
1.857 - }
1.858 -
1.859 - function measureLine(cm, line) {
1.860 - // First look in the cache
1.861 - var display = cm.display, cache = cm.display.measureLineCache;
1.862 - for (var i = 0; i < cache.length; ++i) {
1.863 - var memo = cache[i];
1.864 - if (memo.text == line.text && memo.markedSpans == line.markedSpans &&
1.865 - display.scroller.clientWidth == memo.width)
1.866 - return memo.measure;
1.867 - }
1.868 -
1.869 - var measure = measureLineInner(cm, line);
1.870 - // Store result in the cache
1.871 - var memo = {text: line.text, width: display.scroller.clientWidth,
1.872 - markedSpans: line.markedSpans, measure: measure};
1.873 - if (cache.length == 16) cache[++display.measureLineCachePos % 16] = memo;
1.874 - else cache.push(memo);
1.875 - return measure;
1.876 - }
1.877 -
1.878 - function measureLineInner(cm, line) {
1.879 - var display = cm.display, measure = emptyArray(line.text.length);
1.880 - var pre = lineContent(cm, line, measure);
1.881 -
1.882 - // IE does not cache element positions of inline elements between
1.883 - // calls to getBoundingClientRect. This makes the loop below,
1.884 - // which gathers the positions of all the characters on the line,
1.885 - // do an amount of layout work quadratic to the number of
1.886 - // characters. When line wrapping is off, we try to improve things
1.887 - // by first subdividing the line into a bunch of inline blocks, so
1.888 - // that IE can reuse most of the layout information from caches
1.889 - // for those blocks. This does interfere with line wrapping, so it
1.890 - // doesn't work when wrapping is on, but in that case the
1.891 - // situation is slightly better, since IE does cache line-wrapping
1.892 - // information and only recomputes per-line.
1.893 - if (ie && !ie_lt8 && !cm.options.lineWrapping && pre.childNodes.length > 100) {
1.894 - var fragment = document.createDocumentFragment();
1.895 - var chunk = 10, n = pre.childNodes.length;
1.896 - for (var i = 0, chunks = Math.ceil(n / chunk); i < chunks; ++i) {
1.897 - var wrap = elt("div", null, null, "display: inline-block");
1.898 - for (var j = 0; j < chunk && n; ++j) {
1.899 - wrap.appendChild(pre.firstChild);
1.900 - --n;
1.901 - }
1.902 - fragment.appendChild(wrap);
1.903 - }
1.904 - pre.appendChild(fragment);
1.905 - }
1.906 -
1.907 - removeChildrenAndAdd(display.measure, pre);
1.908 -
1.909 - var outer = display.lineDiv.getBoundingClientRect();
1.910 - var vranges = [], data = emptyArray(line.text.length), maxBot = pre.offsetHeight;
1.911 - for (var i = 0, cur; i < measure.length; ++i) if (cur = measure[i]) {
1.912 - var size = cur.getBoundingClientRect();
1.913 - var top = Math.max(0, size.top - outer.top), bot = Math.min(size.bottom - outer.top, maxBot);
1.914 - for (var j = 0; j < vranges.length; j += 2) {
1.915 - var rtop = vranges[j], rbot = vranges[j+1];
1.916 - if (rtop > bot || rbot < top) continue;
1.917 - if (rtop <= top && rbot >= bot ||
1.918 - top <= rtop && bot >= rbot ||
1.919 - Math.min(bot, rbot) - Math.max(top, rtop) >= (bot - top) >> 1) {
1.920 - vranges[j] = Math.min(top, rtop);
1.921 - vranges[j+1] = Math.max(bot, rbot);
1.922 - break;
1.923 - }
1.924 - }
1.925 - if (j == vranges.length) vranges.push(top, bot);
1.926 - data[i] = {left: size.left - outer.left, right: size.right - outer.left, top: j};
1.927 - }
1.928 - for (var i = 0, cur; i < data.length; ++i) if (cur = data[i]) {
1.929 - var vr = cur.top;
1.930 - cur.top = vranges[vr]; cur.bottom = vranges[vr+1];
1.931 - }
1.932 - return data;
1.933 - }
1.934 -
1.935 - function clearCaches(cm) {
1.936 - cm.display.measureLineCache.length = cm.display.measureLineCachePos = 0;
1.937 - cm.display.cachedCharWidth = cm.display.cachedTextHeight = null;
1.938 - cm.view.maxLineChanged = true;
1.939 - }
1.940 -
1.941 - // Context is one of "line", "div" (display.lineDiv), "local"/null (editor), or "page"
1.942 - function intoCoordSystem(cm, lineObj, rect, context) {
1.943 - if (lineObj.widgets) for (var i = 0; i < lineObj.widgets.length; ++i) if (lineObj.widgets[i].above) {
1.944 - var size = lineObj.widgets[i].node.offsetHeight;
1.945 - rect.top += size; rect.bottom += size;
1.946 - }
1.947 - if (context == "line") return rect;
1.948 - if (!context) context = "local";
1.949 - var yOff = heightAtLine(cm, lineObj);
1.950 - if (context != "local") yOff -= cm.display.viewOffset;
1.951 - if (context == "page") {
1.952 - var lOff = cm.display.lineSpace.getBoundingClientRect();
1.953 - yOff += lOff.top + (window.pageYOffset || (document.documentElement || document.body).scrollTop);
1.954 - var xOff = lOff.left + (window.pageXOffset || (document.documentElement || document.body).scrollLeft);
1.955 - rect.left += xOff; rect.right += xOff;
1.956 - }
1.957 - rect.top += yOff; rect.bottom += yOff;
1.958 - return rect;
1.959 - }
1.960 -
1.961 - function charCoords(cm, pos, context, lineObj) {
1.962 - if (!lineObj) lineObj = getLine(cm.view.doc, pos.line);
1.963 - return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch), context);
1.964 - }
1.965 -
1.966 - function cursorCoords(cm, pos, context, lineObj, measurement) {
1.967 - lineObj = lineObj || getLine(cm.view.doc, pos.line);
1.968 - if (!measurement) measurement = measureLine(cm, lineObj);
1.969 - function get(ch, right) {
1.970 - var m = measureChar(cm, lineObj, ch, measurement);
1.971 - if (right) m.left = m.right; else m.right = m.left;
1.972 - return intoCoordSystem(cm, lineObj, m, context);
1.973 - }
1.974 - var order = getOrder(lineObj), ch = pos.ch;
1.975 - if (!order) return get(ch);
1.976 - var main, other, linedir = order[0].level;
1.977 - for (var i = 0; i < order.length; ++i) {
1.978 - var part = order[i], rtl = part.level % 2, nb, here;
1.979 - if (part.from < ch && part.to > ch) return get(ch, rtl);
1.980 - var left = rtl ? part.to : part.from, right = rtl ? part.from : part.to;
1.981 - if (left == ch) {
1.982 - // Opera and IE return bogus offsets and widths for edges
1.983 - // where the direction flips, but only for the side with the
1.984 - // lower level. So we try to use the side with the higher
1.985 - // level.
1.986 - if (i && part.level < (nb = order[i-1]).level) here = get(nb.level % 2 ? nb.from : nb.to - 1, true);
1.987 - else here = get(rtl && part.from != part.to ? ch - 1 : ch);
1.988 - if (rtl == linedir) main = here; else other = here;
1.989 - } else if (right == ch) {
1.990 - var nb = i < order.length - 1 && order[i+1];
1.991 - if (!rtl && nb && nb.from == nb.to) continue;
1.992 - if (nb && part.level < nb.level) here = get(nb.level % 2 ? nb.to - 1 : nb.from);
1.993 - else here = get(rtl ? ch : ch - 1, true);
1.994 - if (rtl == linedir) main = here; else other = here;
1.995 - }
1.996 - }
1.997 - if (linedir && !ch) other = get(order[0].to - 1);
1.998 - if (!main) return other;
1.999 - if (other) main.other = other;
1.1000 - return main;
1.1001 - }
1.1002 -
1.1003 - // Coords must be lineSpace-local
1.1004 - function coordsChar(cm, x, y) {
1.1005 - var doc = cm.view.doc;
1.1006 - y += cm.display.viewOffset;
1.1007 - if (y < 0) return {line: 0, ch: 0, outside: true};
1.1008 - var lineNo = lineAtHeight(doc, y);
1.1009 - if (lineNo >= doc.size) return {line: doc.size - 1, ch: getLine(doc, doc.size - 1).text.length};
1.1010 - if (x < 0) x = 0;
1.1011 -
1.1012 - for (;;) {
1.1013 - var lineObj = getLine(doc, lineNo);
1.1014 - var found = coordsCharInner(cm, lineObj, lineNo, x, y);
1.1015 - var merged = collapsedSpanAtEnd(lineObj);
1.1016 - if (merged && found.ch == lineRight(lineObj))
1.1017 - lineNo = merged.find().to.line;
1.1018 - else
1.1019 - return found;
1.1020 - }
1.1021 - }
1.1022 -
1.1023 - function coordsCharInner(cm, lineObj, lineNo, x, y) {
1.1024 - var innerOff = y - heightAtLine(cm, lineObj);
1.1025 - var wrongLine = false, cWidth = cm.display.wrapper.clientWidth;
1.1026 - var measurement = measureLine(cm, lineObj);
1.1027 -
1.1028 - function getX(ch) {
1.1029 - var sp = cursorCoords(cm, {line: lineNo, ch: ch}, "line",
1.1030 - lineObj, measurement);
1.1031 - wrongLine = true;
1.1032 - if (innerOff > sp.bottom) return Math.max(0, sp.left - cWidth);
1.1033 - else if (innerOff < sp.top) return sp.left + cWidth;
1.1034 - else wrongLine = false;
1.1035 - return sp.left;
1.1036 - }
1.1037 -
1.1038 - var bidi = getOrder(lineObj), dist = lineObj.text.length;
1.1039 - var from = lineLeft(lineObj), to = lineRight(lineObj);
1.1040 - var fromX = paddingLeft(cm.display), toX = getX(to);
1.1041 -
1.1042 - if (x > toX) return {line: lineNo, ch: to, outside: wrongLine};
1.1043 - // Do a binary search between these bounds.
1.1044 - for (;;) {
1.1045 - if (bidi ? to == from || to == moveVisually(lineObj, from, 1) : to - from <= 1) {
1.1046 - var after = x - fromX < toX - x, ch = after ? from : to;
1.1047 - while (isExtendingChar.test(lineObj.text.charAt(ch))) ++ch;
1.1048 - return {line: lineNo, ch: ch, after: after, outside: wrongLine};
1.1049 - }
1.1050 - var step = Math.ceil(dist / 2), middle = from + step;
1.1051 - if (bidi) {
1.1052 - middle = from;
1.1053 - for (var i = 0; i < step; ++i) middle = moveVisually(lineObj, middle, 1);
1.1054 - }
1.1055 - var middleX = getX(middle);
1.1056 - if (middleX > x) {to = middle; toX = middleX; if (wrongLine) toX += 1000; dist -= step;}
1.1057 - else {from = middle; fromX = middleX; dist = step;}
1.1058 - }
1.1059 - }
1.1060 -
1.1061 - var measureText;
1.1062 - function textHeight(display) {
1.1063 - if (display.cachedTextHeight != null) return display.cachedTextHeight;
1.1064 - if (measureText == null) {
1.1065 - measureText = elt("pre");
1.1066 - // Measure a bunch of lines, for browsers that compute
1.1067 - // fractional heights.
1.1068 - for (var i = 0; i < 49; ++i) {
1.1069 - measureText.appendChild(document.createTextNode("x"));
1.1070 - measureText.appendChild(elt("br"));
1.1071 - }
1.1072 - measureText.appendChild(document.createTextNode("x"));
1.1073 - }
1.1074 - removeChildrenAndAdd(display.measure, measureText);
1.1075 - var height = measureText.offsetHeight / 50;
1.1076 - if (height > 3) display.cachedTextHeight = height;
1.1077 - removeChildren(display.measure);
1.1078 - return height || 1;
1.1079 - }
1.1080 -
1.1081 - function charWidth(display) {
1.1082 - if (display.cachedCharWidth != null) return display.cachedCharWidth;
1.1083 - var anchor = elt("span", "x");
1.1084 - var pre = elt("pre", [anchor]);
1.1085 - removeChildrenAndAdd(display.measure, pre);
1.1086 - var width = anchor.offsetWidth;
1.1087 - if (width > 2) display.cachedCharWidth = width;
1.1088 - return width || 10;
1.1089 - }
1.1090 -
1.1091 - // OPERATIONS
1.1092 -
1.1093 - // Operations are used to wrap changes in such a way that each
1.1094 - // change won't have to update the cursor and display (which would
1.1095 - // be awkward, slow, and error-prone), but instead updates are
1.1096 - // batched and then all combined and executed at once.
1.1097 -
1.1098 - function startOperation(cm) {
1.1099 - if (cm.curOp) ++cm.curOp.depth;
1.1100 - else cm.curOp = {
1.1101 - // Nested operations delay update until the outermost one
1.1102 - // finishes.
1.1103 - depth: 1,
1.1104 - // An array of ranges of lines that have to be updated. See
1.1105 - // updateDisplay.
1.1106 - changes: [],
1.1107 - delayedCallbacks: [],
1.1108 - updateInput: null,
1.1109 - userSelChange: null,
1.1110 - textChanged: null,
1.1111 - selectionChanged: false,
1.1112 - updateMaxLine: false,
1.1113 - id: ++cm.nextOpId
1.1114 - };
1.1115 - }
1.1116 -
1.1117 - function endOperation(cm) {
1.1118 - var op = cm.curOp;
1.1119 - if (--op.depth) return;
1.1120 - cm.curOp = null;
1.1121 - var view = cm.view, display = cm.display;
1.1122 - if (op.updateMaxLine) computeMaxLength(view);
1.1123 - if (view.maxLineChanged && !cm.options.lineWrapping) {
1.1124 - var width = measureChar(cm, view.maxLine, view.maxLine.text.length).right;
1.1125 - display.sizer.style.minWidth = (width + 3 + scrollerCutOff) + "px";
1.1126 - view.maxLineChanged = false;
1.1127 - }
1.1128 - var newScrollPos, updated;
1.1129 - if (op.selectionChanged) {
1.1130 - var coords = cursorCoords(cm, view.sel.head);
1.1131 - newScrollPos = calculateScrollPos(cm, coords.left, coords.top, coords.left, coords.bottom);
1.1132 - }
1.1133 - if (op.changes.length || newScrollPos && newScrollPos.scrollTop != null)
1.1134 - updated = updateDisplay(cm, op.changes, newScrollPos && newScrollPos.scrollTop);
1.1135 - if (!updated && op.selectionChanged) updateSelection(cm);
1.1136 - if (newScrollPos) scrollCursorIntoView(cm);
1.1137 - if (op.selectionChanged) restartBlink(cm);
1.1138 -
1.1139 - if (view.focused && op.updateInput)
1.1140 - resetInput(cm, op.userSelChange);
1.1141 -
1.1142 - if (op.textChanged)
1.1143 - signal(cm, "change", cm, op.textChanged);
1.1144 - if (op.selectionChanged) signal(cm, "cursorActivity", cm);
1.1145 - for (var i = 0; i < op.delayedCallbacks.length; ++i) op.delayedCallbacks[i](cm);
1.1146 - }
1.1147 -
1.1148 - // Wraps a function in an operation. Returns the wrapped function.
1.1149 - function operation(cm1, f) {
1.1150 - return function() {
1.1151 - var cm = cm1 || this;
1.1152 - startOperation(cm);
1.1153 - try {var result = f.apply(cm, arguments);}
1.1154 - finally {endOperation(cm);}
1.1155 - return result;
1.1156 - };
1.1157 - }
1.1158 -
1.1159 - function regChange(cm, from, to, lendiff) {
1.1160 - cm.curOp.changes.push({from: from, to: to, diff: lendiff});
1.1161 - }
1.1162 -
1.1163 - // INPUT HANDLING
1.1164 -
1.1165 - function slowPoll(cm) {
1.1166 - if (cm.view.pollingFast) return;
1.1167 - cm.display.poll.set(cm.options.pollInterval, function() {
1.1168 - readInput(cm);
1.1169 - if (cm.view.focused) slowPoll(cm);
1.1170 - });
1.1171 - }
1.1172 -
1.1173 - function fastPoll(cm) {
1.1174 - var missed = false;
1.1175 - cm.display.pollingFast = true;
1.1176 - function p() {
1.1177 - var changed = readInput(cm);
1.1178 - if (!changed && !missed) {missed = true; cm.display.poll.set(60, p);}
1.1179 - else {cm.display.pollingFast = false; slowPoll(cm);}
1.1180 - }
1.1181 - cm.display.poll.set(20, p);
1.1182 - }
1.1183 -
1.1184 - // prevInput is a hack to work with IME. If we reset the textarea
1.1185 - // on every change, that breaks IME. So we look for changes
1.1186 - // compared to the previous content instead. (Modern browsers have
1.1187 - // events that indicate IME taking place, but these are not widely
1.1188 - // supported or compatible enough yet to rely on.)
1.1189 - function readInput(cm) {
1.1190 - var input = cm.display.input, prevInput = cm.display.prevInput, view = cm.view, sel = view.sel;
1.1191 - if (!view.focused || hasSelection(input) || isReadOnly(cm)) return false;
1.1192 - var text = input.value;
1.1193 - if (text == prevInput && posEq(sel.from, sel.to)) return false;
1.1194 - startOperation(cm);
1.1195 - view.sel.shift = false;
1.1196 - var same = 0, l = Math.min(prevInput.length, text.length);
1.1197 - while (same < l && prevInput[same] == text[same]) ++same;
1.1198 - var from = sel.from, to = sel.to;
1.1199 - if (same < prevInput.length)
1.1200 - from = {line: from.line, ch: from.ch - (prevInput.length - same)};
1.1201 - else if (view.overwrite && posEq(from, to) && !cm.display.pasteIncoming)
1.1202 - to = {line: to.line, ch: Math.min(getLine(cm.view.doc, to.line).text.length, to.ch + (text.length - same))};
1.1203 - var updateInput = cm.curOp.updateInput;
1.1204 - updateDoc(cm, from, to, splitLines(text.slice(same)), "end",
1.1205 - cm.display.pasteIncoming ? "paste" : "input", {from: from, to: to});
1.1206 - cm.curOp.updateInput = updateInput;
1.1207 - if (text.length > 1000) input.value = cm.display.prevInput = "";
1.1208 - else cm.display.prevInput = text;
1.1209 - endOperation(cm);
1.1210 - cm.display.pasteIncoming = false;
1.1211 - return true;
1.1212 - }
1.1213 -
1.1214 - function resetInput(cm, user) {
1.1215 - var view = cm.view, minimal, selected;
1.1216 - if (!posEq(view.sel.from, view.sel.to)) {
1.1217 - cm.display.prevInput = "";
1.1218 - minimal = hasCopyEvent &&
1.1219 - (view.sel.to.line - view.sel.from.line > 100 || (selected = cm.getSelection()).length > 1000);
1.1220 - if (minimal) cm.display.input.value = "-";
1.1221 - else cm.display.input.value = selected || cm.getSelection();
1.1222 - if (view.focused) selectInput(cm.display.input);
1.1223 - } else if (user) cm.display.prevInput = cm.display.input.value = "";
1.1224 - cm.display.inaccurateSelection = minimal;
1.1225 - }
1.1226 -
1.1227 - function focusInput(cm) {
1.1228 - if (cm.options.readOnly != "nocursor" && (ie || document.activeElement != cm.display.input))
1.1229 - cm.display.input.focus();
1.1230 - }
1.1231 -
1.1232 - function isReadOnly(cm) {
1.1233 - return cm.options.readOnly || cm.view.cantEdit;
1.1234 - }
1.1235 -
1.1236 - // EVENT HANDLERS
1.1237 -
1.1238 - function registerEventHandlers(cm) {
1.1239 - var d = cm.display;
1.1240 - on(d.scroller, "mousedown", operation(cm, onMouseDown));
1.1241 - on(d.scroller, "dblclick", operation(cm, e_preventDefault));
1.1242 - on(d.lineSpace, "selectstart", function(e) {
1.1243 - if (!mouseEventInWidget(d, e)) e_preventDefault(e);
1.1244 - });
1.1245 - // Gecko browsers fire contextmenu *after* opening the menu, at
1.1246 - // which point we can't mess with it anymore. Context menu is
1.1247 - // handled in onMouseDown for Gecko.
1.1248 - if (!gecko) on(d.scroller, "contextmenu", function(e) {onContextMenu(cm, e);});
1.1249 -
1.1250 - on(d.scroller, "scroll", function() {
1.1251 - setScrollTop(cm, d.scroller.scrollTop);
1.1252 - setScrollLeft(cm, d.scroller.scrollLeft, true);
1.1253 - signal(cm, "scroll", cm);
1.1254 - });
1.1255 - on(d.scrollbarV, "scroll", function() {
1.1256 - setScrollTop(cm, d.scrollbarV.scrollTop);
1.1257 - });
1.1258 - on(d.scrollbarH, "scroll", function() {
1.1259 - setScrollLeft(cm, d.scrollbarH.scrollLeft);
1.1260 - });
1.1261 -
1.1262 - on(d.scroller, "mousewheel", function(e){onScrollWheel(cm, e);});
1.1263 - on(d.scroller, "DOMMouseScroll", function(e){onScrollWheel(cm, e);});
1.1264 -
1.1265 - function reFocus() { if (cm.view.focused) setTimeout(bind(focusInput, cm), 0); }
1.1266 - on(d.scrollbarH, "mousedown", reFocus);
1.1267 - on(d.scrollbarV, "mousedown", reFocus);
1.1268 - // Prevent wrapper from ever scrolling
1.1269 - on(d.wrapper, "scroll", function() { d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; });
1.1270 - on(window, "resize", function resizeHandler() {
1.1271 - // Might be a text scaling operation, clear size caches.
1.1272 - d.cachedCharWidth = d.cachedTextHeight = null;
1.1273 - clearCaches(cm);
1.1274 - if (d.wrapper.parentNode) updateDisplay(cm, true);
1.1275 - else off(window, "resize", resizeHandler);
1.1276 - });
1.1277 -
1.1278 - on(d.input, "keyup", operation(cm, function(e) {
1.1279 - if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
1.1280 - if (e_prop(e, "keyCode") == 16) cm.view.sel.shift = false;
1.1281 - }));
1.1282 - on(d.input, "input", bind(fastPoll, cm));
1.1283 - on(d.input, "keydown", operation(cm, onKeyDown));
1.1284 - on(d.input, "keypress", operation(cm, onKeyPress));
1.1285 - on(d.input, "focus", bind(onFocus, cm));
1.1286 - on(d.input, "blur", bind(onBlur, cm));
1.1287 -
1.1288 - function drag_(e) {
1.1289 - if (cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e))) return;
1.1290 - e_stop(e);
1.1291 - }
1.1292 - if (cm.options.dragDrop) {
1.1293 - on(d.scroller, "dragstart", function(e){onDragStart(cm, e);});
1.1294 - on(d.scroller, "dragenter", drag_);
1.1295 - on(d.scroller, "dragover", drag_);
1.1296 - on(d.scroller, "drop", operation(cm, onDrop));
1.1297 - }
1.1298 - on(d.scroller, "paste", function(){focusInput(cm); fastPoll(cm);});
1.1299 - on(d.input, "paste", function() {
1.1300 - d.pasteIncoming = true;
1.1301 - fastPoll(cm);
1.1302 - });
1.1303 -
1.1304 - function prepareCopy() {
1.1305 - if (d.inaccurateSelection) {
1.1306 - d.prevInput = "";
1.1307 - d.inaccurateSelection = false;
1.1308 - d.input.value = cm.getSelection();
1.1309 - selectInput(d.input);
1.1310 - }
1.1311 - }
1.1312 - on(d.input, "cut", prepareCopy);
1.1313 - on(d.input, "copy", prepareCopy);
1.1314 -
1.1315 - // Needed to handle Tab key in KHTML
1.1316 - if (khtml) on(d.sizer, "mouseup", function() {
1.1317 - if (document.activeElement == d.input) d.input.blur();
1.1318 - focusInput(cm);
1.1319 - });
1.1320 - }
1.1321 -
1.1322 - function mouseEventInWidget(display, e) {
1.1323 - for (var n = e_target(e); n != display.wrapper; n = n.parentNode)
1.1324 - if (/\bCodeMirror-(?:line)?widget\b/.test(n.className) ||
1.1325 - n.parentNode == display.sizer && n != display.mover) return true;
1.1326 - }
1.1327 -
1.1328 - function posFromMouse(cm, e, liberal) {
1.1329 - var display = cm.display;
1.1330 - if (!liberal) {
1.1331 - var target = e_target(e);
1.1332 - if (target == display.scrollbarH || target == display.scrollbarH.firstChild ||
1.1333 - target == display.scrollbarV || target == display.scrollbarV.firstChild ||
1.1334 - target == display.scrollbarFiller) return null;
1.1335 - }
1.1336 - var x, y, space = display.lineSpace.getBoundingClientRect();
1.1337 - // Fails unpredictably on IE[67] when mouse is dragged around quickly.
1.1338 - try { x = e.clientX; y = e.clientY; } catch (e) { return null; }
1.1339 - return coordsChar(cm, x - space.left, y - space.top);
1.1340 - }
1.1341 -
1.1342 - var lastClick, lastDoubleClick;
1.1343 - function onMouseDown(e) {
1.1344 - var cm = this, display = cm.display, view = cm.view, sel = view.sel, doc = view.doc;
1.1345 - sel.shift = e_prop(e, "shiftKey");
1.1346 -
1.1347 - if (mouseEventInWidget(display, e)) {
1.1348 - if (!webkit) {
1.1349 - display.scroller.draggable = false;
1.1350 - setTimeout(function(){display.scroller.draggable = true;}, 100);
1.1351 - }
1.1352 - return;
1.1353 - }
1.1354 - if (clickInGutter(cm, e)) return;
1.1355 - var start = posFromMouse(cm, e);
1.1356 -
1.1357 - switch (e_button(e)) {
1.1358 - case 3:
1.1359 - if (gecko) onContextMenu.call(cm, cm, e);
1.1360 - return;
1.1361 - case 2:
1.1362 - if (start) extendSelection(cm, start);
1.1363 - setTimeout(bind(focusInput, cm), 20);
1.1364 - e_preventDefault(e);
1.1365 - return;
1.1366 - }
1.1367 - // For button 1, if it was clicked inside the editor
1.1368 - // (posFromMouse returning non-null), we have to adjust the
1.1369 - // selection.
1.1370 - if (!start) {if (e_target(e) == display.scroller) e_preventDefault(e); return;}
1.1371 -
1.1372 - if (!view.focused) onFocus(cm);
1.1373 -
1.1374 - var now = +new Date, type = "single";
1.1375 - if (lastDoubleClick && lastDoubleClick.time > now - 400 && posEq(lastDoubleClick.pos, start)) {
1.1376 - type = "triple";
1.1377 - e_preventDefault(e);
1.1378 - setTimeout(bind(focusInput, cm), 20);
1.1379 - selectLine(cm, start.line);
1.1380 - } else if (lastClick && lastClick.time > now - 400 && posEq(lastClick.pos, start)) {
1.1381 - type = "double";
1.1382 - lastDoubleClick = {time: now, pos: start};
1.1383 - e_preventDefault(e);
1.1384 - var word = findWordAt(getLine(doc, start.line).text, start);
1.1385 - extendSelection(cm, word.from, word.to);
1.1386 - } else { lastClick = {time: now, pos: start}; }
1.1387 -
1.1388 - var last = start;
1.1389 - if (cm.options.dragDrop && dragAndDrop && !isReadOnly(cm) && !posEq(sel.from, sel.to) &&
1.1390 - !posLess(start, sel.from) && !posLess(sel.to, start) && type == "single") {
1.1391 - var dragEnd = operation(cm, function(e2) {
1.1392 - if (webkit) display.scroller.draggable = false;
1.1393 - view.draggingText = false;
1.1394 - off(document, "mouseup", dragEnd);
1.1395 - off(display.scroller, "drop", dragEnd);
1.1396 - if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) {
1.1397 - e_preventDefault(e2);
1.1398 - extendSelection(cm, start);
1.1399 - focusInput(cm);
1.1400 - }
1.1401 - });
1.1402 - // Let the drag handler handle this.
1.1403 - if (webkit) display.scroller.draggable = true;
1.1404 - view.draggingText = dragEnd;
1.1405 - // IE's approach to draggable
1.1406 - if (display.scroller.dragDrop) display.scroller.dragDrop();
1.1407 - on(document, "mouseup", dragEnd);
1.1408 - on(display.scroller, "drop", dragEnd);
1.1409 - return;
1.1410 - }
1.1411 - e_preventDefault(e);
1.1412 - if (type == "single") extendSelection(cm, clipPos(doc, start));
1.1413 -
1.1414 - var startstart = sel.from, startend = sel.to;
1.1415 -
1.1416 - function doSelect(cur) {
1.1417 - if (type == "single") {
1.1418 - extendSelection(cm, clipPos(doc, start), cur);
1.1419 - return;
1.1420 - }
1.1421 -
1.1422 - startstart = clipPos(doc, startstart);
1.1423 - startend = clipPos(doc, startend);
1.1424 - if (type == "double") {
1.1425 - var word = findWordAt(getLine(doc, cur.line).text, cur);
1.1426 - if (posLess(cur, startstart)) extendSelection(cm, word.from, startend);
1.1427 - else extendSelection(cm, startstart, word.to);
1.1428 - } else if (type == "triple") {
1.1429 - if (posLess(cur, startstart)) extendSelection(cm, startend, clipPos(doc, {line: cur.line, ch: 0}));
1.1430 - else extendSelection(cm, startstart, clipPos(doc, {line: cur.line + 1, ch: 0}));
1.1431 - }
1.1432 - }
1.1433 -
1.1434 - var editorSize = display.wrapper.getBoundingClientRect();
1.1435 - // Used to ensure timeout re-tries don't fire when another extend
1.1436 - // happened in the meantime (clearTimeout isn't reliable -- at
1.1437 - // least on Chrome, the timeouts still happen even when cleared,
1.1438 - // if the clear happens after their scheduled firing time).
1.1439 - var counter = 0;
1.1440 -
1.1441 - function extend(e) {
1.1442 - var curCount = ++counter;
1.1443 - var cur = posFromMouse(cm, e, true);
1.1444 - if (!cur) return;
1.1445 - if (!posEq(cur, last)) {
1.1446 - if (!view.focused) onFocus(cm);
1.1447 - last = cur;
1.1448 - doSelect(cur);
1.1449 - var visible = visibleLines(display, doc);
1.1450 - if (cur.line >= visible.to || cur.line < visible.from)
1.1451 - setTimeout(operation(cm, function(){if (counter == curCount) extend(e);}), 150);
1.1452 - } else {
1.1453 - var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0;
1.1454 - if (outside) setTimeout(operation(cm, function() {
1.1455 - if (counter != curCount) return;
1.1456 - display.scroller.scrollTop += outside;
1.1457 - extend(e);
1.1458 - }), 50);
1.1459 - }
1.1460 - }
1.1461 -
1.1462 - function done(e) {
1.1463 - counter = Infinity;
1.1464 - var cur = posFromMouse(cm, e);
1.1465 - if (cur) doSelect(cur);
1.1466 - e_preventDefault(e);
1.1467 - focusInput(cm);
1.1468 - off(document, "mousemove", move);
1.1469 - off(document, "mouseup", up);
1.1470 - }
1.1471 -
1.1472 - var move = operation(cm, function(e) {
1.1473 - if (!ie && !e_button(e)) done(e);
1.1474 - else extend(e);
1.1475 - });
1.1476 - var up = operation(cm, done);
1.1477 - on(document, "mousemove", move);
1.1478 - on(document, "mouseup", up);
1.1479 - }
1.1480 -
1.1481 - function onDrop(e) {
1.1482 - var cm = this;
1.1483 - if (cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e))) return;
1.1484 - e_preventDefault(e);
1.1485 - var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files;
1.1486 - if (!pos || isReadOnly(cm)) return;
1.1487 - if (files && files.length && window.FileReader && window.File) {
1.1488 - var n = files.length, text = Array(n), read = 0;
1.1489 - var loadFile = function(file, i) {
1.1490 - var reader = new FileReader;
1.1491 - reader.onload = function() {
1.1492 - text[i] = reader.result;
1.1493 - if (++read == n) {
1.1494 - pos = clipPos(cm.view.doc, pos);
1.1495 - operation(cm, function() {
1.1496 - var end = replaceRange(cm, text.join(""), pos, pos, "paste");
1.1497 - setSelection(cm, pos, end);
1.1498 - })();
1.1499 - }
1.1500 - };
1.1501 - reader.readAsText(file);
1.1502 - };
1.1503 - for (var i = 0; i < n; ++i) loadFile(files[i], i);
1.1504 - } else {
1.1505 - // Don't do a replace if the drop happened inside of the selected text.
1.1506 - if (cm.view.draggingText && !(posLess(pos, cm.view.sel.from) || posLess(cm.view.sel.to, pos))) {
1.1507 - cm.view.draggingText(e);
1.1508 - if (ie) setTimeout(bind(focusInput, cm), 50);
1.1509 - return;
1.1510 - }
1.1511 - try {
1.1512 - var text = e.dataTransfer.getData("Text");
1.1513 - if (text) {
1.1514 - var curFrom = cm.view.sel.from, curTo = cm.view.sel.to;
1.1515 - setSelection(cm, pos, pos);
1.1516 - if (cm.view.draggingText) replaceRange(cm, "", curFrom, curTo, "paste");
1.1517 - cm.replaceSelection(text, null, "paste");
1.1518 - focusInput(cm);
1.1519 - onFocus(cm);
1.1520 - }
1.1521 - }
1.1522 - catch(e){}
1.1523 - }
1.1524 - }
1.1525 -
1.1526 - function clickInGutter(cm, e) {
1.1527 - var display = cm.display;
1.1528 - try { var mX = e.clientX, mY = e.clientY; }
1.1529 - catch(e) { return false; }
1.1530 -
1.1531 - if (mX >= Math.floor(display.gutters.getBoundingClientRect().right)) return false;
1.1532 - e_preventDefault(e);
1.1533 - if (!hasHandler(cm, "gutterClick")) return true;
1.1534 -
1.1535 - var lineBox = display.lineDiv.getBoundingClientRect();
1.1536 - if (mY > lineBox.bottom) return true;
1.1537 - mY -= lineBox.top - display.viewOffset;
1.1538 -
1.1539 - for (var i = 0; i < cm.options.gutters.length; ++i) {
1.1540 - var g = display.gutters.childNodes[i];
1.1541 - if (g && g.getBoundingClientRect().right >= mX) {
1.1542 - var line = lineAtHeight(cm.view.doc, mY);
1.1543 - var gutter = cm.options.gutters[i];
1.1544 - signalLater(cm, cm, "gutterClick", cm, line, gutter, e);
1.1545 - break;
1.1546 - }
1.1547 - }
1.1548 - return true;
1.1549 - }
1.1550 -
1.1551 - function onDragStart(cm, e) {
1.1552 - var txt = cm.getSelection();
1.1553 - e.dataTransfer.setData("Text", txt);
1.1554 -
1.1555 - // Use dummy image instead of default browsers image.
1.1556 - // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
1.1557 - if (e.dataTransfer.setDragImage && !safari)
1.1558 - e.dataTransfer.setDragImage(elt('img'), 0, 0);
1.1559 - }
1.1560 -
1.1561 - function setScrollTop(cm, val) {
1.1562 - if (Math.abs(cm.view.scrollTop - val) < 2) return;
1.1563 - cm.view.scrollTop = val;
1.1564 - if (!gecko) updateDisplay(cm, [], val);
1.1565 - if (cm.display.scroller.scrollTop != val) cm.display.scroller.scrollTop = val;
1.1566 - if (cm.display.scrollbarV.scrollTop != val) cm.display.scrollbarV.scrollTop = val;
1.1567 - if (gecko) updateDisplay(cm, []);
1.1568 - }
1.1569 - function setScrollLeft(cm, val, isScroller) {
1.1570 - if (isScroller ? val == cm.view.scrollLeft : Math.abs(cm.view.scrollLeft - val) < 2) return;
1.1571 - cm.view.scrollLeft = val;
1.1572 - alignHorizontally(cm);
1.1573 - if (cm.display.scroller.scrollLeft != val) cm.display.scroller.scrollLeft = val;
1.1574 - if (cm.display.scrollbarH.scrollLeft != val) cm.display.scrollbarH.scrollLeft = val;
1.1575 - }
1.1576 -
1.1577 - // Since the delta values reported on mouse wheel events are
1.1578 - // unstandardized between browsers and even browser versions, and
1.1579 - // generally horribly unpredictable, this code starts by measuring
1.1580 - // the scroll effect that the first few mouse wheel events have,
1.1581 - // and, from that, detects the way it can convert deltas to pixel
1.1582 - // offsets afterwards.
1.1583 - //
1.1584 - // The reason we want to know the amount a wheel event will scroll
1.1585 - // is that it gives us a chance to update the display before the
1.1586 - // actual scrolling happens, reducing flickering.
1.1587 -
1.1588 - var wheelSamples = 0, wheelDX, wheelDY, wheelStartX, wheelStartY, wheelPixelsPerUnit = null;
1.1589 - // Fill in a browser-detected starting value on browsers where we
1.1590 - // know one. These don't have to be accurate -- the result of them
1.1591 - // being wrong would just be a slight flicker on the first wheel
1.1592 - // scroll (if it is large enough).
1.1593 - if (ie) wheelPixelsPerUnit = -.53;
1.1594 - else if (gecko) wheelPixelsPerUnit = 15;
1.1595 - else if (chrome) wheelPixelsPerUnit = -.7;
1.1596 - else if (safari) wheelPixelsPerUnit = -1/3;
1.1597 -
1.1598 - function onScrollWheel(cm, e) {
1.1599 - var dx = e.wheelDeltaX, dy = e.wheelDeltaY;
1.1600 - if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) dx = e.detail;
1.1601 - if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) dy = e.detail;
1.1602 - else if (dy == null) dy = e.wheelDelta;
1.1603 -
1.1604 - // Webkit browsers on OS X abort momentum scrolls when the target
1.1605 - // of the scroll event is removed from the scrollable element.
1.1606 - // This hack (see related code in patchDisplay) makes sure the
1.1607 - // element is kept around.
1.1608 - if (dy && mac && webkit) {
1.1609 - for (var cur = e.target; cur != scroll; cur = cur.parentNode) {
1.1610 - if (cur.lineObj) {
1.1611 - cm.display.currentWheelTarget = cur;
1.1612 - break;
1.1613 - }
1.1614 - }
1.1615 - }
1.1616 -
1.1617 - var scroll = cm.display.scroller;
1.1618 - // On some browsers, horizontal scrolling will cause redraws to
1.1619 - // happen before the gutter has been realigned, causing it to
1.1620 - // wriggle around in a most unseemly way. When we have an
1.1621 - // estimated pixels/delta value, we just handle horizontal
1.1622 - // scrolling entirely here. It'll be slightly off from native, but
1.1623 - // better than glitching out.
1.1624 - if (dx && !gecko && !opera && wheelPixelsPerUnit != null) {
1.1625 - if (dy)
1.1626 - setScrollTop(cm, Math.max(0, Math.min(scroll.scrollTop + dy * wheelPixelsPerUnit, scroll.scrollHeight - scroll.clientHeight)));
1.1627 - setScrollLeft(cm, Math.max(0, Math.min(scroll.scrollLeft + dx * wheelPixelsPerUnit, scroll.scrollWidth - scroll.clientWidth)));
1.1628 - e_preventDefault(e);
1.1629 - wheelStartX = null; // Abort measurement, if in progress
1.1630 - return;
1.1631 - }
1.1632 -
1.1633 - if (dy && wheelPixelsPerUnit != null) {
1.1634 - var pixels = dy * wheelPixelsPerUnit;
1.1635 - var top = cm.view.scrollTop, bot = top + cm.display.wrapper.clientHeight;
1.1636 - if (pixels < 0) top = Math.max(0, top + pixels - 50);
1.1637 - else bot = Math.min(cm.view.doc.height, bot + pixels + 50);
1.1638 - updateDisplay(cm, [], {top: top, bottom: bot});
1.1639 - }
1.1640 -
1.1641 - if (wheelSamples < 20) {
1.1642 - if (wheelStartX == null) {
1.1643 - wheelStartX = scroll.scrollLeft; wheelStartY = scroll.scrollTop;
1.1644 - wheelDX = dx; wheelDY = dy;
1.1645 - setTimeout(function() {
1.1646 - if (wheelStartX == null) return;
1.1647 - var movedX = scroll.scrollLeft - wheelStartX;
1.1648 - var movedY = scroll.scrollTop - wheelStartY;
1.1649 - var sample = (movedY && wheelDY && movedY / wheelDY) ||
1.1650 - (movedX && wheelDX && movedX / wheelDX);
1.1651 - wheelStartX = wheelStartY = null;
1.1652 - if (!sample) return;
1.1653 - wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1);
1.1654 - ++wheelSamples;
1.1655 - }, 200);
1.1656 - } else {
1.1657 - wheelDX += dx; wheelDY += dy;
1.1658 - }
1.1659 - }
1.1660 - }
1.1661 -
1.1662 - function doHandleBinding(cm, bound, dropShift) {
1.1663 - if (typeof bound == "string") {
1.1664 - bound = commands[bound];
1.1665 - if (!bound) return false;
1.1666 - }
1.1667 - // Ensure previous input has been read, so that the handler sees a
1.1668 - // consistent view of the document
1.1669 - if (cm.display.pollingFast && readInput(cm)) cm.display.pollingFast = false;
1.1670 - var view = cm.view, prevShift = view.sel.shift;
1.1671 - try {
1.1672 - if (isReadOnly(cm)) view.suppressEdits = true;
1.1673 - if (dropShift) view.sel.shift = false;
1.1674 - bound(cm);
1.1675 - } catch(e) {
1.1676 - if (e != Pass) throw e;
1.1677 - return false;
1.1678 - } finally {
1.1679 - view.sel.shift = prevShift;
1.1680 - view.suppressEdits = false;
1.1681 - }
1.1682 - return true;
1.1683 - }
1.1684 -
1.1685 - function allKeyMaps(cm) {
1.1686 - var maps = cm.view.keyMaps.slice(0);
1.1687 - maps.push(cm.options.keyMap);
1.1688 - if (cm.options.extraKeys) maps.unshift(cm.options.extraKeys);
1.1689 - return maps;
1.1690 - }
1.1691 -
1.1692 - var maybeTransition;
1.1693 - function handleKeyBinding(cm, e) {
1.1694 - // Handle auto keymap transitions
1.1695 - var startMap = getKeyMap(cm.options.keyMap), next = startMap.auto;
1.1696 - clearTimeout(maybeTransition);
1.1697 - if (next && !isModifierKey(e)) maybeTransition = setTimeout(function() {
1.1698 - if (getKeyMap(cm.options.keyMap) == startMap)
1.1699 - cm.options.keyMap = (next.call ? next.call(null, cm) : next);
1.1700 - }, 50);
1.1701 -
1.1702 - var name = keyNames[e_prop(e, "keyCode")], handled = false;
1.1703 - var flipCtrlCmd = mac && (opera || qtwebkit);
1.1704 - if (name == null || e.altGraphKey) return false;
1.1705 - if (e_prop(e, "altKey")) name = "Alt-" + name;
1.1706 - if (e_prop(e, flipCtrlCmd ? "metaKey" : "ctrlKey")) name = "Ctrl-" + name;
1.1707 - if (e_prop(e, flipCtrlCmd ? "ctrlKey" : "metaKey")) name = "Cmd-" + name;
1.1708 -
1.1709 - var stopped = false;
1.1710 - function stop() { stopped = true; }
1.1711 - var keymaps = allKeyMaps(cm);
1.1712 -
1.1713 - if (e_prop(e, "shiftKey")) {
1.1714 - handled = lookupKey("Shift-" + name, keymaps,
1.1715 - function(b) {return doHandleBinding(cm, b, true);}, stop)
1.1716 - || lookupKey(name, keymaps, function(b) {
1.1717 - if (typeof b == "string" && /^go[A-Z]/.test(b)) return doHandleBinding(cm, b);
1.1718 - }, stop);
1.1719 - } else {
1.1720 - handled = lookupKey(name, keymaps,
1.1721 - function(b) { return doHandleBinding(cm, b); }, stop);
1.1722 - }
1.1723 - if (stopped) handled = false;
1.1724 - if (handled) {
1.1725 - e_preventDefault(e);
1.1726 - restartBlink(cm);
1.1727 - if (ie_lt9) { e.oldKeyCode = e.keyCode; e.keyCode = 0; }
1.1728 - }
1.1729 - return handled;
1.1730 - }
1.1731 -
1.1732 - function handleCharBinding(cm, e, ch) {
1.1733 - var handled = lookupKey("'" + ch + "'", allKeyMaps(cm),
1.1734 - function(b) { return doHandleBinding(cm, b, true); });
1.1735 - if (handled) {
1.1736 - e_preventDefault(e);
1.1737 - restartBlink(cm);
1.1738 - }
1.1739 - return handled;
1.1740 - }
1.1741 -
1.1742 - var lastStoppedKey = null;
1.1743 - function onKeyDown(e) {
1.1744 - var cm = this;
1.1745 - if (!cm.view.focused) onFocus(cm);
1.1746 - if (ie && e.keyCode == 27) { e.returnValue = false; }
1.1747 - if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
1.1748 - var code = e_prop(e, "keyCode");
1.1749 - // IE does strange things with escape.
1.1750 - cm.view.sel.shift = code == 16 || e_prop(e, "shiftKey");
1.1751 - // First give onKeyEvent option a chance to handle this.
1.1752 - var handled = handleKeyBinding(cm, e);
1.1753 - if (opera) {
1.1754 - lastStoppedKey = handled ? code : null;
1.1755 - // Opera has no cut event... we try to at least catch the key combo
1.1756 - if (!handled && code == 88 && !hasCopyEvent && e_prop(e, mac ? "metaKey" : "ctrlKey"))
1.1757 - cm.replaceSelection("");
1.1758 - }
1.1759 - }
1.1760 -
1.1761 - function onKeyPress(e) {
1.1762 - var cm = this;
1.1763 - if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
1.1764 - var keyCode = e_prop(e, "keyCode"), charCode = e_prop(e, "charCode");
1.1765 - if (opera && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;}
1.1766 - if (((opera && (!e.which || e.which < 10)) || khtml) && handleKeyBinding(cm, e)) return;
1.1767 - var ch = String.fromCharCode(charCode == null ? keyCode : charCode);
1.1768 - if (this.options.electricChars && this.view.mode.electricChars &&
1.1769 - this.options.smartIndent && !isReadOnly(this) &&
1.1770 - this.view.mode.electricChars.indexOf(ch) > -1)
1.1771 - setTimeout(operation(cm, function() {indentLine(cm, cm.view.sel.to.line, "smart");}), 75);
1.1772 - if (handleCharBinding(cm, e, ch)) return;
1.1773 - fastPoll(cm);
1.1774 - }
1.1775 -
1.1776 - function onFocus(cm) {
1.1777 - if (cm.options.readOnly == "nocursor") return;
1.1778 - if (!cm.view.focused) {
1.1779 - signal(cm, "focus", cm);
1.1780 - cm.view.focused = true;
1.1781 - if (cm.display.scroller.className.search(/\bCodeMirror-focused\b/) == -1)
1.1782 - cm.display.scroller.className += " CodeMirror-focused";
1.1783 - resetInput(cm, true);
1.1784 - }
1.1785 - slowPoll(cm);
1.1786 - restartBlink(cm);
1.1787 - }
1.1788 - function onBlur(cm) {
1.1789 - if (cm.view.focused) {
1.1790 - signal(cm, "blur", cm);
1.1791 - cm.view.focused = false;
1.1792 - cm.display.scroller.className = cm.display.scroller.className.replace(" CodeMirror-focused", "");
1.1793 - }
1.1794 - clearInterval(cm.display.blinker);
1.1795 - setTimeout(function() {if (!cm.view.focused) cm.view.sel.shift = false;}, 150);
1.1796 - }
1.1797 -
1.1798 - var detectingSelectAll;
1.1799 - function onContextMenu(cm, e) {
1.1800 - var display = cm.display, sel = cm.view.sel;
1.1801 - var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop;
1.1802 - if (!pos || opera) return; // Opera is difficult.
1.1803 - if (posEq(sel.from, sel.to) || posLess(pos, sel.from) || !posLess(pos, sel.to))
1.1804 - operation(cm, setSelection)(cm, pos, pos);
1.1805 -
1.1806 - var oldCSS = display.input.style.cssText;
1.1807 - display.inputDiv.style.position = "absolute";
1.1808 - display.input.style.cssText = "position: fixed; width: 30px; height: 30px; top: " + (e.clientY - 5) +
1.1809 - "px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: white; outline: none;" +
1.1810 - "border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);";
1.1811 - focusInput(cm);
1.1812 - resetInput(cm, true);
1.1813 - // Adds "Select all" to context menu in FF
1.1814 - if (posEq(sel.from, sel.to)) display.input.value = display.prevInput = " ";
1.1815 -
1.1816 - function rehide() {
1.1817 - display.inputDiv.style.position = "relative";
1.1818 - display.input.style.cssText = oldCSS;
1.1819 - if (ie_lt9) display.scrollbarV.scrollTop = display.scroller.scrollTop = scrollPos;
1.1820 - slowPoll(cm);
1.1821 -
1.1822 - // Try to detect the user choosing select-all
1.1823 - if (display.input.selectionStart != null) {
1.1824 - clearTimeout(detectingSelectAll);
1.1825 - var extval = display.input.value = " " + (posEq(sel.from, sel.to) ? "" : display.input.value), i = 0;
1.1826 - display.prevInput = " ";
1.1827 - display.input.selectionStart = 1; display.input.selectionEnd = extval.length;
1.1828 - detectingSelectAll = setTimeout(function poll(){
1.1829 - if (display.prevInput == " " && display.input.selectionStart == 0)
1.1830 - operation(cm, commands.selectAll)(cm);
1.1831 - else if (i++ < 10) detectingSelectAll = setTimeout(poll, 500);
1.1832 - else resetInput(cm);
1.1833 - }, 200);
1.1834 - }
1.1835 - }
1.1836 -
1.1837 - if (gecko) {
1.1838 - e_stop(e);
1.1839 - on(window, "mouseup", function mouseup() {
1.1840 - off(window, "mouseup", mouseup);
1.1841 - setTimeout(rehide, 20);
1.1842 - });
1.1843 - } else {
1.1844 - setTimeout(rehide, 50);
1.1845 - }
1.1846 - }
1.1847 -
1.1848 - // UPDATING
1.1849 -
1.1850 - // Replace the range from from to to by the strings in newText.
1.1851 - // Afterwards, set the selection to selFrom, selTo.
1.1852 - function updateDoc(cm, from, to, newText, selUpdate, origin) {
1.1853 - // Possibly split or suppress the update based on the presence
1.1854 - // of read-only spans in its range.
1.1855 - var split = sawReadOnlySpans &&
1.1856 - removeReadOnlyRanges(cm.view.doc, from, to);
1.1857 - if (split) {
1.1858 - for (var i = split.length - 1; i >= 1; --i)
1.1859 - updateDocInner(cm, split[i].from, split[i].to, [""], origin);
1.1860 - if (split.length)
1.1861 - return updateDocInner(cm, split[0].from, split[0].to, newText, selUpdate, origin);
1.1862 - } else {
1.1863 - return updateDocInner(cm, from, to, newText, selUpdate, origin);
1.1864 - }
1.1865 - }
1.1866 -
1.1867 - function updateDocInner(cm, from, to, newText, selUpdate, origin) {
1.1868 - if (cm.view.suppressEdits) return;
1.1869 -
1.1870 - var view = cm.view, doc = view.doc, old = [];
1.1871 - doc.iter(from.line, to.line + 1, function(line) {
1.1872 - old.push(newHL(line.text, line.markedSpans));
1.1873 - });
1.1874 - var startSelFrom = view.sel.from, startSelTo = view.sel.to;
1.1875 - var lines = updateMarkedSpans(hlSpans(old[0]), hlSpans(lst(old)), from.ch, to.ch, newText);
1.1876 - var retval = updateDocNoUndo(cm, from, to, lines, selUpdate, origin);
1.1877 - if (view.history) addChange(cm, from.line, newText.length, old, origin,
1.1878 - startSelFrom, startSelTo, view.sel.from, view.sel.to);
1.1879 - return retval;
1.1880 - }
1.1881 -
1.1882 - function unredoHelper(cm, type) {
1.1883 - var doc = cm.view.doc, hist = cm.view.history;
1.1884 - var set = (type == "undo" ? hist.done : hist.undone).pop();
1.1885 - if (!set) return;
1.1886 - var anti = {events: [], fromBefore: set.fromAfter, toBefore: set.toAfter,
1.1887 - fromAfter: set.fromBefore, toAfter: set.toBefore};
1.1888 - for (var i = set.events.length - 1; i >= 0; i -= 1) {
1.1889 - hist.dirtyCounter += type == "undo" ? -1 : 1;
1.1890 - var change = set.events[i];
1.1891 - var replaced = [], end = change.start + change.added;
1.1892 - doc.iter(change.start, end, function(line) { replaced.push(newHL(line.text, line.markedSpans)); });
1.1893 - anti.events.push({start: change.start, added: change.old.length, old: replaced});
1.1894 - var selPos = i ? null : {from: set.fromBefore, to: set.toBefore};
1.1895 - updateDocNoUndo(cm, {line: change.start, ch: 0}, {line: end - 1, ch: getLine(doc, end-1).text.length},
1.1896 - change.old, selPos, type);
1.1897 - }
1.1898 - (type == "undo" ? hist.undone : hist.done).push(anti);
1.1899 - }
1.1900 -
1.1901 - function updateDocNoUndo(cm, from, to, lines, selUpdate, origin) {
1.1902 - var view = cm.view, doc = view.doc, display = cm.display;
1.1903 - if (view.suppressEdits) return;
1.1904 -
1.1905 - var nlines = to.line - from.line, firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line);
1.1906 - var recomputeMaxLength = false, checkWidthStart = from.line;
1.1907 - if (!cm.options.lineWrapping) {
1.1908 - checkWidthStart = lineNo(visualLine(doc, firstLine));
1.1909 - doc.iter(checkWidthStart, to.line + 1, function(line) {
1.1910 - if (lineLength(doc, line) == view.maxLineLength) {
1.1911 - recomputeMaxLength = true;
1.1912 - return true;
1.1913 - }
1.1914 - });
1.1915 - }
1.1916 -
1.1917 - var lastHL = lst(lines), th = textHeight(display);
1.1918 -
1.1919 - // First adjust the line structure
1.1920 - if (from.ch == 0 && to.ch == 0 && hlText(lastHL) == "") {
1.1921 - // This is a whole-line replace. Treated specially to make
1.1922 - // sure line objects move the way they are supposed to.
1.1923 - var added = [];
1.1924 - for (var i = 0, e = lines.length - 1; i < e; ++i)
1.1925 - added.push(makeLine(hlText(lines[i]), hlSpans(lines[i]), th));
1.1926 - updateLine(cm, lastLine, lastLine.text, hlSpans(lastHL));
1.1927 - if (nlines) doc.remove(from.line, nlines, cm);
1.1928 - if (added.length) doc.insert(from.line, added);
1.1929 - } else if (firstLine == lastLine) {
1.1930 - if (lines.length == 1) {
1.1931 - updateLine(cm, firstLine, firstLine.text.slice(0, from.ch) + hlText(lines[0]) +
1.1932 - firstLine.text.slice(to.ch), hlSpans(lines[0]));
1.1933 - } else {
1.1934 - for (var added = [], i = 1, e = lines.length - 1; i < e; ++i)
1.1935 - added.push(makeLine(hlText(lines[i]), hlSpans(lines[i]), th));
1.1936 - added.push(makeLine(hlText(lastHL) + firstLine.text.slice(to.ch), hlSpans(lastHL), th));
1.1937 - updateLine(cm, firstLine, firstLine.text.slice(0, from.ch) + hlText(lines[0]), hlSpans(lines[0]));
1.1938 - doc.insert(from.line + 1, added);
1.1939 - }
1.1940 - } else if (lines.length == 1) {
1.1941 - updateLine(cm, firstLine, firstLine.text.slice(0, from.ch) + hlText(lines[0]) +
1.1942 - lastLine.text.slice(to.ch), hlSpans(lines[0]));
1.1943 - doc.remove(from.line + 1, nlines, cm);
1.1944 - } else {
1.1945 - var added = [];
1.1946 - updateLine(cm, firstLine, firstLine.text.slice(0, from.ch) + hlText(lines[0]), hlSpans(lines[0]));
1.1947 - updateLine(cm, lastLine, hlText(lastHL) + lastLine.text.slice(to.ch), hlSpans(lastHL));
1.1948 - for (var i = 1, e = lines.length - 1; i < e; ++i)
1.1949 - added.push(makeLine(hlText(lines[i]), hlSpans(lines[i]), th));
1.1950 - if (nlines > 1) doc.remove(from.line + 1, nlines - 1, cm);
1.1951 - doc.insert(from.line + 1, added);
1.1952 - }
1.1953 -
1.1954 - if (cm.options.lineWrapping) {
1.1955 - var perLine = Math.max(5, display.scroller.clientWidth / charWidth(display) - 3);
1.1956 - doc.iter(from.line, from.line + lines.length, function(line) {
1.1957 - if (line.height == 0) return;
1.1958 - var guess = (Math.ceil(line.text.length / perLine) || 1) * th;
1.1959 - if (guess != line.height) updateLineHeight(line, guess);
1.1960 - });
1.1961 - } else {
1.1962 - doc.iter(checkWidthStart, from.line + lines.length, function(line) {
1.1963 - var len = lineLength(doc, line);
1.1964 - if (len > view.maxLineLength) {
1.1965 - view.maxLine = line;
1.1966 - view.maxLineLength = len;
1.1967 - view.maxLineChanged = true;
1.1968 - recomputeMaxLength = false;
1.1969 - }
1.1970 - });
1.1971 - if (recomputeMaxLength) cm.curOp.updateMaxLine = true;
1.1972 - }
1.1973 -
1.1974 - // Adjust frontier, schedule worker
1.1975 - view.frontier = Math.min(view.frontier, from.line);
1.1976 - startWorker(cm, 400);
1.1977 -
1.1978 - var lendiff = lines.length - nlines - 1;
1.1979 - // Remember that these lines changed, for updating the display
1.1980 - regChange(cm, from.line, to.line + 1, lendiff);
1.1981 - if (hasHandler(cm, "change")) {
1.1982 - // Normalize lines to contain only strings, since that's what
1.1983 - // the change event handler expects
1.1984 - for (var i = 0; i < lines.length; ++i)
1.1985 - if (typeof lines[i] != "string") lines[i] = lines[i].text;
1.1986 - var changeObj = {from: from, to: to, text: lines, origin: origin};
1.1987 - if (cm.curOp.textChanged) {
1.1988 - for (var cur = cm.curOp.textChanged; cur.next; cur = cur.next) {}
1.1989 - cur.next = changeObj;
1.1990 - } else cm.curOp.textChanged = changeObj;
1.1991 - }
1.1992 -
1.1993 - // Update the selection
1.1994 - var newSelFrom, newSelTo, end = {line: from.line + lines.length - 1,
1.1995 - ch: hlText(lastHL).length + (lines.length == 1 ? from.ch : 0)};
1.1996 - if (selUpdate && typeof selUpdate != "string") {
1.1997 - if (selUpdate.from) { newSelFrom = selUpdate.from; newSelTo = selUpdate.to; }
1.1998 - else newSelFrom = newSelTo = selUpdate;
1.1999 - } else if (selUpdate == "end") {
1.2000 - newSelFrom = newSelTo = end;
1.2001 - } else if (selUpdate == "start") {
1.2002 - newSelFrom = newSelTo = from;
1.2003 - } else if (selUpdate == "around") {
1.2004 - newSelFrom = from; newSelTo = end;
1.2005 - } else {
1.2006 - var adjustPos = function(pos) {
1.2007 - if (posLess(pos, from)) return pos;
1.2008 - if (!posLess(to, pos)) return end;
1.2009 - var line = pos.line + lendiff;
1.2010 - var ch = pos.ch;
1.2011 - if (pos.line == to.line)
1.2012 - ch += hlText(lastHL).length - (to.ch - (to.line == from.line ? from.ch : 0));
1.2013 - return {line: line, ch: ch};
1.2014 - };
1.2015 - newSelFrom = adjustPos(view.sel.from);
1.2016 - newSelTo = adjustPos(view.sel.to);
1.2017 - }
1.2018 - setSelection(cm, newSelFrom, newSelTo, null, true);
1.2019 - return end;
1.2020 - }
1.2021 -
1.2022 - function replaceRange(cm, code, from, to, origin) {
1.2023 - if (!to) to = from;
1.2024 - if (posLess(to, from)) { var tmp = to; to = from; from = tmp; }
1.2025 - return updateDoc(cm, from, to, splitLines(code), null, origin);
1.2026 - }
1.2027 -
1.2028 - // SELECTION
1.2029 -
1.2030 - function posEq(a, b) {return a.line == b.line && a.ch == b.ch;}
1.2031 - function posLess(a, b) {return a.line < b.line || (a.line == b.line && a.ch < b.ch);}
1.2032 - function copyPos(x) {return {line: x.line, ch: x.ch};}
1.2033 -
1.2034 - function clipLine(doc, n) {return Math.max(0, Math.min(n, doc.size-1));}
1.2035 - function clipPos(doc, pos) {
1.2036 - if (pos.line < 0) return {line: 0, ch: 0};
1.2037 - if (pos.line >= doc.size) return {line: doc.size-1, ch: getLine(doc, doc.size-1).text.length};
1.2038 - var ch = pos.ch, linelen = getLine(doc, pos.line).text.length;
1.2039 - if (ch == null || ch > linelen) return {line: pos.line, ch: linelen};
1.2040 - else if (ch < 0) return {line: pos.line, ch: 0};
1.2041 - else return pos;
1.2042 - }
1.2043 - function isLine(doc, l) {return l >= 0 && l < doc.size;}
1.2044 -
1.2045 - // If shift is held, this will move the selection anchor. Otherwise,
1.2046 - // it'll set the whole selection.
1.2047 - function extendSelection(cm, pos, other, bias) {
1.2048 - var sel = cm.view.sel;
1.2049 - if (sel.shift || sel.extend) {
1.2050 - var anchor = sel.anchor;
1.2051 - if (other) {
1.2052 - var posBefore = posLess(pos, anchor);
1.2053 - if (posBefore != posLess(other, anchor)) {
1.2054 - anchor = pos;
1.2055 - pos = other;
1.2056 - } else if (posBefore != posLess(pos, other)) {
1.2057 - pos = other;
1.2058 - }
1.2059 - }
1.2060 - setSelection(cm, anchor, pos, bias);
1.2061 - } else {
1.2062 - setSelection(cm, pos, other || pos, bias);
1.2063 - }
1.2064 - cm.curOp.userSelChange = true;
1.2065 - }
1.2066 -
1.2067 - // Update the selection. Last two args are only used by
1.2068 - // updateDoc, since they have to be expressed in the line
1.2069 - // numbers before the update.
1.2070 - function setSelection(cm, anchor, head, bias, checkAtomic) {
1.2071 - cm.view.goalColumn = null;
1.2072 - var sel = cm.view.sel;
1.2073 - // Skip over atomic spans.
1.2074 - if (checkAtomic || !posEq(anchor, sel.anchor))
1.2075 - anchor = skipAtomic(cm, anchor, bias, checkAtomic != "push");
1.2076 - if (checkAtomic || !posEq(head, sel.head))
1.2077 - head = skipAtomic(cm, head, bias, checkAtomic != "push");
1.2078 -
1.2079 - if (posEq(sel.anchor, anchor) && posEq(sel.head, head)) return;
1.2080 -
1.2081 - sel.anchor = anchor; sel.head = head;
1.2082 - var inv = posLess(head, anchor);
1.2083 - sel.from = inv ? head : anchor;
1.2084 - sel.to = inv ? anchor : head;
1.2085 -
1.2086 - cm.curOp.updateInput = true;
1.2087 - cm.curOp.selectionChanged = true;
1.2088 - }
1.2089 -
1.2090 - function reCheckSelection(cm) {
1.2091 - setSelection(cm, cm.view.sel.from, cm.view.sel.to, null, "push");
1.2092 - }
1.2093 -
1.2094 - function skipAtomic(cm, pos, bias, mayClear) {
1.2095 - var doc = cm.view.doc, flipped = false, curPos = pos;
1.2096 - var dir = bias || 1;
1.2097 - cm.view.cantEdit = false;
1.2098 - search: for (;;) {
1.2099 - var line = getLine(doc, curPos.line), toClear;
1.2100 - if (line.markedSpans) {
1.2101 - for (var i = 0; i < line.markedSpans.length; ++i) {
1.2102 - var sp = line.markedSpans[i], m = sp.marker;
1.2103 - if ((sp.from == null || (m.inclusiveLeft ? sp.from <= curPos.ch : sp.from < curPos.ch)) &&
1.2104 - (sp.to == null || (m.inclusiveRight ? sp.to >= curPos.ch : sp.to > curPos.ch))) {
1.2105 - if (mayClear && m.clearOnEnter) {
1.2106 - (toClear || (toClear = [])).push(m);
1.2107 - continue;
1.2108 - } else if (!m.atomic) continue;
1.2109 - var newPos = m.find()[dir < 0 ? "from" : "to"];
1.2110 - if (posEq(newPos, curPos)) {
1.2111 - newPos.ch += dir;
1.2112 - if (newPos.ch < 0) {
1.2113 - if (newPos.line) newPos = clipPos(doc, {line: newPos.line - 1});
1.2114 - else newPos = null;
1.2115 - } else if (newPos.ch > line.text.length) {
1.2116 - if (newPos.line < doc.size - 1) newPos = {line: newPos.line + 1, ch: 0};
1.2117 - else newPos = null;
1.2118 - }
1.2119 - if (!newPos) {
1.2120 - if (flipped) {
1.2121 - // Driven in a corner -- no valid cursor position found at all
1.2122 - // -- try again *with* clearing, if we didn't already
1.2123 - if (!mayClear) return skipAtomic(cm, pos, bias, true);
1.2124 - // Otherwise, turn off editing until further notice, and return the start of the doc
1.2125 - cm.view.cantEdit = true;
1.2126 - return {line: 0, ch: 0};
1.2127 - }
1.2128 - flipped = true; newPos = pos; dir = -dir;
1.2129 - }
1.2130 - }
1.2131 - curPos = newPos;
1.2132 - continue search;
1.2133 - }
1.2134 - }
1.2135 - if (toClear) for (var i = 0; i < toClear.length; ++i) toClear[i].clear();
1.2136 - }
1.2137 - return curPos;
1.2138 - }
1.2139 - }
1.2140 -
1.2141 - // SCROLLING
1.2142 -
1.2143 - function scrollCursorIntoView(cm) {
1.2144 - var view = cm.view;
1.2145 - var coords = scrollPosIntoView(cm, view.sel.head);
1.2146 - if (!view.focused) return;
1.2147 - var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null;
1.2148 - if (coords.top + box.top < 0) doScroll = true;
1.2149 - else if (coords.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false;
1.2150 - if (doScroll != null && !phantom) {
1.2151 - var hidden = display.cursor.style.display == "none";
1.2152 - if (hidden) {
1.2153 - display.cursor.style.display = "";
1.2154 - display.cursor.style.left = coords.left + "px";
1.2155 - display.cursor.style.top = (coords.top - display.viewOffset) + "px";
1.2156 - }
1.2157 - display.cursor.scrollIntoView(doScroll);
1.2158 - if (hidden) display.cursor.style.display = "none";
1.2159 - }
1.2160 - }
1.2161 -
1.2162 - function scrollPosIntoView(cm, pos) {
1.2163 - for (;;) {
1.2164 - var changed = false, coords = cursorCoords(cm, pos);
1.2165 - var scrollPos = calculateScrollPos(cm, coords.left, coords.top, coords.left, coords.bottom);
1.2166 - var startTop = cm.view.scrollTop, startLeft = cm.view.scrollLeft;
1.2167 - if (scrollPos.scrollTop != null) {
1.2168 - setScrollTop(cm, scrollPos.scrollTop);
1.2169 - if (Math.abs(cm.view.scrollTop - startTop) > 1) changed = true;
1.2170 - }
1.2171 - if (scrollPos.scrollLeft != null) {
1.2172 - setScrollLeft(cm, scrollPos.scrollLeft);
1.2173 - if (Math.abs(cm.view.scrollLeft - startLeft) > 1) changed = true;
1.2174 - }
1.2175 - if (!changed) return coords;
1.2176 - }
1.2177 - }
1.2178 -
1.2179 - function scrollIntoView(cm, x1, y1, x2, y2) {
1.2180 - var scrollPos = calculateScrollPos(cm, x1, y1, x2, y2);
1.2181 - if (scrollPos.scrollTop != null) setScrollTop(cm, scrollPos.scrollTop);
1.2182 - if (scrollPos.scrollLeft != null) setScrollLeft(cm, scrollPos.scrollLeft);
1.2183 - }
1.2184 -
1.2185 - function calculateScrollPos(cm, x1, y1, x2, y2) {
1.2186 - var display = cm.display, pt = paddingTop(display);
1.2187 - y1 += pt; y2 += pt;
1.2188 - var screen = display.scroller.clientHeight - scrollerCutOff, screentop = display.scroller.scrollTop, result = {};
1.2189 - var docBottom = cm.view.doc.height + 2 * pt;
1.2190 - var atTop = y1 < pt + 10, atBottom = y2 + pt > docBottom - 10;
1.2191 - if (y1 < screentop) result.scrollTop = atTop ? 0 : Math.max(0, y1);
1.2192 - else if (y2 > screentop + screen) result.scrollTop = (atBottom ? docBottom : y2) - screen;
1.2193 -
1.2194 - var screenw = display.scroller.clientWidth - scrollerCutOff, screenleft = display.scroller.scrollLeft;
1.2195 - x1 += display.gutters.offsetWidth; x2 += display.gutters.offsetWidth;
1.2196 - var gutterw = display.gutters.offsetWidth;
1.2197 - var atLeft = x1 < gutterw + 10;
1.2198 - if (x1 < screenleft + gutterw || atLeft) {
1.2199 - if (atLeft) x1 = 0;
1.2200 - result.scrollLeft = Math.max(0, x1 - 10 - gutterw);
1.2201 - } else if (x2 > screenw + screenleft - 3) {
1.2202 - result.scrollLeft = x2 + 10 - screenw;
1.2203 - }
1.2204 - return result;
1.2205 - }
1.2206 -
1.2207 - // API UTILITIES
1.2208 -
1.2209 - function indentLine(cm, n, how, aggressive) {
1.2210 - var doc = cm.view.doc;
1.2211 - if (!how) how = "add";
1.2212 - if (how == "smart") {
1.2213 - if (!cm.view.mode.indent) how = "prev";
1.2214 - else var state = getStateBefore(cm, n);
1.2215 - }
1.2216 -
1.2217 - var tabSize = cm.options.tabSize;
1.2218 - var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize);
1.2219 - var curSpaceString = line.text.match(/^\s*/)[0], indentation;
1.2220 - if (how == "smart") {
1.2221 - indentation = cm.view.mode.indent(state, line.text.slice(curSpaceString.length), line.text);
1.2222 - if (indentation == Pass) {
1.2223 - if (!aggressive) return;
1.2224 - how = "prev";
1.2225 - }
1.2226 - }
1.2227 - if (how == "prev") {
1.2228 - if (n) indentation = countColumn(getLine(doc, n-1).text, null, tabSize);
1.2229 - else indentation = 0;
1.2230 - }
1.2231 - else if (how == "add") indentation = curSpace + cm.options.indentUnit;
1.2232 - else if (how == "subtract") indentation = curSpace - cm.options.indentUnit;
1.2233 - indentation = Math.max(0, indentation);
1.2234 -
1.2235 - var indentString = "", pos = 0;
1.2236 - if (cm.options.indentWithTabs)
1.2237 - for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";}
1.2238 - if (pos < indentation) indentString += spaceStr(indentation - pos);
1.2239 -
1.2240 - if (indentString != curSpaceString)
1.2241 - replaceRange(cm, indentString, {line: n, ch: 0}, {line: n, ch: curSpaceString.length}, "input");
1.2242 - line.stateAfter = null;
1.2243 - }
1.2244 -
1.2245 - function changeLine(cm, handle, op) {
1.2246 - var no = handle, line = handle, doc = cm.view.doc;
1.2247 - if (typeof handle == "number") line = getLine(doc, clipLine(doc, handle));
1.2248 - else no = lineNo(handle);
1.2249 - if (no == null) return null;
1.2250 - if (op(line, no)) regChange(cm, no, no + 1);
1.2251 - else return null;
1.2252 - return line;
1.2253 - }
1.2254 -
1.2255 - function findPosH(cm, dir, unit, visually) {
1.2256 - var doc = cm.view.doc, end = cm.view.sel.head, line = end.line, ch = end.ch;
1.2257 - var lineObj = getLine(doc, line);
1.2258 - function findNextLine() {
1.2259 - var l = line + dir;
1.2260 - if (l < 0 || l == doc.size) return false;
1.2261 - line = l;
1.2262 - return lineObj = getLine(doc, l);
1.2263 - }
1.2264 - function moveOnce(boundToLine) {
1.2265 - var next = (visually ? moveVisually : moveLogically)(lineObj, ch, dir, true);
1.2266 - if (next == null) {
1.2267 - if (!boundToLine && findNextLine()) {
1.2268 - if (visually) ch = (dir < 0 ? lineRight : lineLeft)(lineObj);
1.2269 - else ch = dir < 0 ? lineObj.text.length : 0;
1.2270 - } else return false;
1.2271 - } else ch = next;
1.2272 - return true;
1.2273 - }
1.2274 - if (unit == "char") moveOnce();
1.2275 - else if (unit == "column") moveOnce(true);
1.2276 - else if (unit == "word") {
1.2277 - var sawWord = false;
1.2278 - for (;;) {
1.2279 - if (dir < 0) if (!moveOnce()) break;
1.2280 - if (isWordChar(lineObj.text.charAt(ch))) sawWord = true;
1.2281 - else if (sawWord) {if (dir < 0) {dir = 1; moveOnce();} break;}
1.2282 - if (dir > 0) if (!moveOnce()) break;
1.2283 - }
1.2284 - }
1.2285 - return skipAtomic(cm, {line: line, ch: ch}, dir, true);
1.2286 - }
1.2287 -
1.2288 - function findWordAt(line, pos) {
1.2289 - var start = pos.ch, end = pos.ch;
1.2290 - if (line) {
1.2291 - if (pos.after === false || end == line.length) --start; else ++end;
1.2292 - var startChar = line.charAt(start);
1.2293 - var check = isWordChar(startChar) ? isWordChar :
1.2294 - /\s/.test(startChar) ? function(ch) {return /\s/.test(ch);} :
1.2295 - function(ch) {return !/\s/.test(ch) && !isWordChar(ch);};
1.2296 - while (start > 0 && check(line.charAt(start - 1))) --start;
1.2297 - while (end < line.length && check(line.charAt(end))) ++end;
1.2298 - }
1.2299 - return {from: {line: pos.line, ch: start}, to: {line: pos.line, ch: end}};
1.2300 - }
1.2301 -
1.2302 - function selectLine(cm, line) {
1.2303 - extendSelection(cm, {line: line, ch: 0}, clipPos(cm.view.doc, {line: line + 1, ch: 0}));
1.2304 - }
1.2305 -
1.2306 - // PROTOTYPE
1.2307 -
1.2308 - // The publicly visible API. Note that operation(null, f) means
1.2309 - // 'wrap f in an operation, performed on its `this` parameter'
1.2310 -
1.2311 - CodeMirror.prototype = {
1.2312 - getValue: function(lineSep) {
1.2313 - var text = [], doc = this.view.doc;
1.2314 - doc.iter(0, doc.size, function(line) { text.push(line.text); });
1.2315 - return text.join(lineSep || "\n");
1.2316 - },
1.2317 -
1.2318 - setValue: operation(null, function(code) {
1.2319 - var doc = this.view.doc, top = {line: 0, ch: 0}, lastLen = getLine(doc, doc.size-1).text.length;
1.2320 - updateDocInner(this, top, {line: doc.size - 1, ch: lastLen}, splitLines(code), top, top, "setValue");
1.2321 - }),
1.2322 -
1.2323 - getSelection: function(lineSep) { return this.getRange(this.view.sel.from, this.view.sel.to, lineSep); },
1.2324 -
1.2325 - replaceSelection: operation(null, function(code, collapse, origin) {
1.2326 - var sel = this.view.sel;
1.2327 - updateDoc(this, sel.from, sel.to, splitLines(code), collapse || "around", origin);
1.2328 - }),
1.2329 -
1.2330 - focus: function(){window.focus(); focusInput(this); onFocus(this); fastPoll(this);},
1.2331 -
1.2332 - setOption: function(option, value) {
1.2333 - var options = this.options, old = options[option];
1.2334 - if (options[option] == value && option != "mode") return;
1.2335 - options[option] = value;
1.2336 - if (optionHandlers.hasOwnProperty(option))
1.2337 - operation(this, optionHandlers[option])(this, value, old);
1.2338 - },
1.2339 -
1.2340 - getOption: function(option) {return this.options[option];},
1.2341 -
1.2342 - getMode: function() {return this.view.mode;},
1.2343 -
1.2344 - addKeyMap: function(map) {
1.2345 - this.view.keyMaps.push(map);
1.2346 - },
1.2347 -
1.2348 - removeKeyMap: function(map) {
1.2349 - var maps = this.view.keyMaps;
1.2350 - for (var i = 0; i < maps.length; ++i)
1.2351 - if ((typeof map == "string" ? maps[i].name : maps[i]) == map) {
1.2352 - maps.splice(i, 1);
1.2353 - return true;
1.2354 - }
1.2355 - },
1.2356 -
1.2357 - undo: operation(null, function() {unredoHelper(this, "undo");}),
1.2358 - redo: operation(null, function() {unredoHelper(this, "redo");}),
1.2359 -
1.2360 - indentLine: operation(null, function(n, dir, aggressive) {
1.2361 - if (typeof dir != "string") {
1.2362 - if (dir == null) dir = this.options.smartIndent ? "smart" : "prev";
1.2363 - else dir = dir ? "add" : "subtract";
1.2364 - }
1.2365 - if (isLine(this.view.doc, n)) indentLine(this, n, dir, aggressive);
1.2366 - }),
1.2367 -
1.2368 - indentSelection: operation(null, function(how) {
1.2369 - var sel = this.view.sel;
1.2370 - if (posEq(sel.from, sel.to)) return indentLine(this, sel.from.line, how);
1.2371 - var e = sel.to.line - (sel.to.ch ? 0 : 1);
1.2372 - for (var i = sel.from.line; i <= e; ++i) indentLine(this, i, how);
1.2373 - }),
1.2374 -
1.2375 - historySize: function() {
1.2376 - var hist = this.view.history;
1.2377 - return {undo: hist.done.length, redo: hist.undone.length};
1.2378 - },
1.2379 -
1.2380 - clearHistory: function() {this.view.history = makeHistory();},
1.2381 -
1.2382 - markClean: function() {
1.2383 - this.view.history.dirtyCounter = 0;
1.2384 - this.view.history.lastOp = this.view.history.lastOrigin = null;
1.2385 - },
1.2386 -
1.2387 - isClean: function () {return this.view.history.dirtyCounter == 0;},
1.2388 -
1.2389 - getHistory: function() {
1.2390 - var hist = this.view.history;
1.2391 - function cp(arr) {
1.2392 - for (var i = 0, nw = [], nwelt; i < arr.length; ++i) {
1.2393 - var set = arr[i];
1.2394 - nw.push({events: nwelt = [], fromBefore: set.fromBefore, toBefore: set.toBefore,
1.2395 - fromAfter: set.fromAfter, toAfter: set.toAfter});
1.2396 - for (var j = 0, elt = set.events; j < elt.length; ++j) {
1.2397 - var old = [], cur = elt[j];
1.2398 - nwelt.push({start: cur.start, added: cur.added, old: old});
1.2399 - for (var k = 0; k < cur.old.length; ++k) old.push(hlText(cur.old[k]));
1.2400 - }
1.2401 - }
1.2402 - return nw;
1.2403 - }
1.2404 - return {done: cp(hist.done), undone: cp(hist.undone)};
1.2405 - },
1.2406 -
1.2407 - setHistory: function(histData) {
1.2408 - var hist = this.view.history = makeHistory();
1.2409 - hist.done = histData.done;
1.2410 - hist.undone = histData.undone;
1.2411 - },
1.2412 -
1.2413 - // Fetch the parser token for a given character. Useful for hacks
1.2414 - // that want to inspect the mode state (say, for completion).
1.2415 - getTokenAt: function(pos) {
1.2416 - var doc = this.view.doc;
1.2417 - pos = clipPos(doc, pos);
1.2418 - var state = getStateBefore(this, pos.line), mode = this.view.mode;
1.2419 - var line = getLine(doc, pos.line);
1.2420 - var stream = new StringStream(line.text, this.options.tabSize);
1.2421 - while (stream.pos < pos.ch && !stream.eol()) {
1.2422 - stream.start = stream.pos;
1.2423 - var style = mode.token(stream, state);
1.2424 - }
1.2425 - return {start: stream.start,
1.2426 - end: stream.pos,
1.2427 - string: stream.current(),
1.2428 - className: style || null, // Deprecated, use 'type' instead
1.2429 - type: style || null,
1.2430 - state: state};
1.2431 - },
1.2432 -
1.2433 - getStateAfter: function(line) {
1.2434 - var doc = this.view.doc;
1.2435 - line = clipLine(doc, line == null ? doc.size - 1: line);
1.2436 - return getStateBefore(this, line + 1);
1.2437 - },
1.2438 -
1.2439 - cursorCoords: function(start, mode) {
1.2440 - var pos, sel = this.view.sel;
1.2441 - if (start == null) pos = sel.head;
1.2442 - else if (typeof start == "object") pos = clipPos(this.view.doc, start);
1.2443 - else pos = start ? sel.from : sel.to;
1.2444 - return cursorCoords(this, pos, mode || "page");
1.2445 - },
1.2446 -
1.2447 - charCoords: function(pos, mode) {
1.2448 - return charCoords(this, clipPos(this.view.doc, pos), mode || "page");
1.2449 - },
1.2450 -
1.2451 - coordsChar: function(coords) {
1.2452 - var off = this.display.lineSpace.getBoundingClientRect();
1.2453 - return coordsChar(this, coords.left - off.left, coords.top - off.top);
1.2454 - },
1.2455 -
1.2456 - defaultTextHeight: function() { return textHeight(this.display); },
1.2457 -
1.2458 - markText: operation(null, function(from, to, options) {
1.2459 - return markText(this, clipPos(this.view.doc, from), clipPos(this.view.doc, to),
1.2460 - options, "range");
1.2461 - }),
1.2462 -
1.2463 - setBookmark: operation(null, function(pos, widget) {
1.2464 - pos = clipPos(this.view.doc, pos);
1.2465 - return markText(this, pos, pos, widget ? {replacedWith: widget} : {}, "bookmark");
1.2466 - }),
1.2467 -
1.2468 - findMarksAt: function(pos) {
1.2469 - var doc = this.view.doc;
1.2470 - pos = clipPos(doc, pos);
1.2471 - var markers = [], spans = getLine(doc, pos.line).markedSpans;
1.2472 - if (spans) for (var i = 0; i < spans.length; ++i) {
1.2473 - var span = spans[i];
1.2474 - if ((span.from == null || span.from <= pos.ch) &&
1.2475 - (span.to == null || span.to >= pos.ch))
1.2476 - markers.push(span.marker);
1.2477 - }
1.2478 - return markers;
1.2479 - },
1.2480 -
1.2481 - setGutterMarker: operation(null, function(line, gutterID, value) {
1.2482 - return changeLine(this, line, function(line) {
1.2483 - var markers = line.gutterMarkers || (line.gutterMarkers = {});
1.2484 - markers[gutterID] = value;
1.2485 - if (!value && isEmpty(markers)) line.gutterMarkers = null;
1.2486 - return true;
1.2487 - });
1.2488 - }),
1.2489 -
1.2490 - clearGutter: operation(null, function(gutterID) {
1.2491 - var i = 0, cm = this, doc = cm.view.doc;
1.2492 - doc.iter(0, doc.size, function(line) {
1.2493 - if (line.gutterMarkers && line.gutterMarkers[gutterID]) {
1.2494 - line.gutterMarkers[gutterID] = null;
1.2495 - regChange(cm, i, i + 1);
1.2496 - if (isEmpty(line.gutterMarkers)) line.gutterMarkers = null;
1.2497 - }
1.2498 - ++i;
1.2499 - });
1.2500 - }),
1.2501 -
1.2502 - addLineClass: operation(null, function(handle, where, cls) {
1.2503 - return changeLine(this, handle, function(line) {
1.2504 - var prop = where == "text" ? "textClass" : where == "background" ? "bgClass" : "wrapClass";
1.2505 - if (!line[prop]) line[prop] = cls;
1.2506 - else if (new RegExp("\\b" + cls + "\\b").test(line[prop])) return false;
1.2507 - else line[prop] += " " + cls;
1.2508 - return true;
1.2509 - });
1.2510 - }),
1.2511 -
1.2512 - removeLineClass: operation(null, function(handle, where, cls) {
1.2513 - return changeLine(this, handle, function(line) {
1.2514 - var prop = where == "text" ? "textClass" : where == "background" ? "bgClass" : "wrapClass";
1.2515 - var cur = line[prop];
1.2516 - if (!cur) return false;
1.2517 - else if (cls == null) line[prop] = null;
1.2518 - else {
1.2519 - var upd = cur.replace(new RegExp("^" + cls + "\\b\\s*|\\s*\\b" + cls + "\\b"), "");
1.2520 - if (upd == cur) return false;
1.2521 - line[prop] = upd || null;
1.2522 - }
1.2523 - return true;
1.2524 - });
1.2525 - }),
1.2526 -
1.2527 - addLineWidget: operation(null, function(handle, node, options) {
1.2528 - var widget = options || {};
1.2529 - widget.node = node;
1.2530 - if (widget.noHScroll) this.display.alignWidgets = true;
1.2531 - changeLine(this, handle, function(line) {
1.2532 - (line.widgets || (line.widgets = [])).push(widget);
1.2533 - widget.line = line;
1.2534 - return true;
1.2535 - });
1.2536 - return widget;
1.2537 - }),
1.2538 -
1.2539 - removeLineWidget: operation(null, function(widget) {
1.2540 - var ws = widget.line.widgets, no = lineNo(widget.line);
1.2541 - if (no == null) return;
1.2542 - for (var i = 0; i < ws.length; ++i) if (ws[i] == widget) ws.splice(i--, 1);
1.2543 - regChange(this, no, no + 1);
1.2544 - }),
1.2545 -
1.2546 - lineInfo: function(line) {
1.2547 - if (typeof line == "number") {
1.2548 - if (!isLine(this.view.doc, line)) return null;
1.2549 - var n = line;
1.2550 - line = getLine(this.view.doc, line);
1.2551 - if (!line) return null;
1.2552 - } else {
1.2553 - var n = lineNo(line);
1.2554 - if (n == null) return null;
1.2555 - }
1.2556 - return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers,
1.2557 - textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass,
1.2558 - widgets: line.widgets};
1.2559 - },
1.2560 -
1.2561 - getViewport: function() { return {from: this.display.showingFrom, to: this.display.showingTo};},
1.2562 -
1.2563 - addWidget: function(pos, node, scroll, vert, horiz) {
1.2564 - var display = this.display;
1.2565 - pos = cursorCoords(this, clipPos(this.view.doc, pos));
1.2566 - var top = pos.top, left = pos.left;
1.2567 - node.style.position = "absolute";
1.2568 - display.sizer.appendChild(node);
1.2569 - if (vert == "over") top = pos.top;
1.2570 - else if (vert == "near") {
1.2571 - var vspace = Math.max(display.wrapper.clientHeight, this.view.doc.height),
1.2572 - hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth);
1.2573 - if (pos.bottom + node.offsetHeight > vspace && pos.top > node.offsetHeight)
1.2574 - top = pos.top - node.offsetHeight;
1.2575 - if (left + node.offsetWidth > hspace)
1.2576 - left = hspace - node.offsetWidth;
1.2577 - }
1.2578 - node.style.top = (top + paddingTop(display)) + "px";
1.2579 - node.style.left = node.style.right = "";
1.2580 - if (horiz == "right") {
1.2581 - left = display.sizer.clientWidth - node.offsetWidth;
1.2582 - node.style.right = "0px";
1.2583 - } else {
1.2584 - if (horiz == "left") left = 0;
1.2585 - else if (horiz == "middle") left = (display.sizer.clientWidth - node.offsetWidth) / 2;
1.2586 - node.style.left = left + "px";
1.2587 - }
1.2588 - if (scroll)
1.2589 - scrollIntoView(this, left, top, left + node.offsetWidth, top + node.offsetHeight);
1.2590 - },
1.2591 -
1.2592 - lineCount: function() {return this.view.doc.size;},
1.2593 -
1.2594 - clipPos: function(pos) {return clipPos(this.view.doc, pos);},
1.2595 -
1.2596 - getCursor: function(start) {
1.2597 - var sel = this.view.sel, pos;
1.2598 - if (start == null || start == "head") pos = sel.head;
1.2599 - else if (start == "anchor") pos = sel.anchor;
1.2600 - else if (start == "end" || start === false) pos = sel.to;
1.2601 - else pos = sel.from;
1.2602 - return copyPos(pos);
1.2603 - },
1.2604 -
1.2605 - somethingSelected: function() {return !posEq(this.view.sel.from, this.view.sel.to);},
1.2606 -
1.2607 - setCursor: operation(null, function(line, ch, extend) {
1.2608 - var pos = clipPos(this.view.doc, typeof line == "number" ? {line: line, ch: ch || 0} : line);
1.2609 - if (extend) extendSelection(this, pos);
1.2610 - else setSelection(this, pos, pos);
1.2611 - }),
1.2612 -
1.2613 - setSelection: operation(null, function(anchor, head) {
1.2614 - var doc = this.view.doc;
1.2615 - setSelection(this, clipPos(doc, anchor), clipPos(doc, head || anchor));
1.2616 - }),
1.2617 -
1.2618 - extendSelection: operation(null, function(from, to) {
1.2619 - var doc = this.view.doc;
1.2620 - extendSelection(this, clipPos(doc, from), to && clipPos(doc, to));
1.2621 - }),
1.2622 -
1.2623 - setExtending: function(val) {this.view.sel.extend = val;},
1.2624 -
1.2625 - getLine: function(line) {var l = this.getLineHandle(line); return l && l.text;},
1.2626 -
1.2627 - getLineHandle: function(line) {
1.2628 - var doc = this.view.doc;
1.2629 - if (isLine(doc, line)) return getLine(doc, line);
1.2630 - },
1.2631 -
1.2632 - getLineNumber: function(line) {return lineNo(line);},
1.2633 -
1.2634 - setLine: operation(null, function(line, text) {
1.2635 - if (isLine(this.view.doc, line))
1.2636 - replaceRange(this, text, {line: line, ch: 0}, {line: line, ch: getLine(this.view.doc, line).text.length});
1.2637 - }),
1.2638 -
1.2639 - removeLine: operation(null, function(line) {
1.2640 - if (isLine(this.view.doc, line))
1.2641 - replaceRange(this, "", {line: line, ch: 0}, clipPos(this.view.doc, {line: line+1, ch: 0}));
1.2642 - }),
1.2643 -
1.2644 - replaceRange: operation(null, function(code, from, to) {
1.2645 - var doc = this.view.doc;
1.2646 - from = clipPos(doc, from);
1.2647 - to = to ? clipPos(doc, to) : from;
1.2648 - return replaceRange(this, code, from, to);
1.2649 - }),
1.2650 -
1.2651 - getRange: function(from, to, lineSep) {
1.2652 - var doc = this.view.doc;
1.2653 - from = clipPos(doc, from); to = clipPos(doc, to);
1.2654 - var l1 = from.line, l2 = to.line;
1.2655 - if (l1 == l2) return getLine(doc, l1).text.slice(from.ch, to.ch);
1.2656 - var code = [getLine(doc, l1).text.slice(from.ch)];
1.2657 - doc.iter(l1 + 1, l2, function(line) { code.push(line.text); });
1.2658 - code.push(getLine(doc, l2).text.slice(0, to.ch));
1.2659 - return code.join(lineSep || "\n");
1.2660 - },
1.2661 -
1.2662 - triggerOnKeyDown: operation(null, onKeyDown),
1.2663 -
1.2664 - execCommand: function(cmd) {return commands[cmd](this);},
1.2665 -
1.2666 - // Stuff used by commands, probably not much use to outside code.
1.2667 - moveH: operation(null, function(dir, unit) {
1.2668 - var sel = this.view.sel, pos = dir < 0 ? sel.from : sel.to;
1.2669 - if (sel.shift || sel.extend || posEq(sel.from, sel.to)) pos = findPosH(this, dir, unit, true);
1.2670 - extendSelection(this, pos, pos, dir);
1.2671 - }),
1.2672 -
1.2673 - deleteH: operation(null, function(dir, unit) {
1.2674 - var sel = this.view.sel;
1.2675 - if (!posEq(sel.from, sel.to)) replaceRange(this, "", sel.from, sel.to, "delete");
1.2676 - else replaceRange(this, "", sel.from, findPosH(this, dir, unit, false), "delete");
1.2677 - this.curOp.userSelChange = true;
1.2678 - }),
1.2679 -
1.2680 - moveV: operation(null, function(dir, unit) {
1.2681 - var view = this.view, doc = view.doc, display = this.display;
1.2682 - var cur = view.sel.head, pos = cursorCoords(this, cur, "div");
1.2683 - var x = pos.left, y;
1.2684 - if (view.goalColumn != null) x = view.goalColumn;
1.2685 - if (unit == "page") {
1.2686 - var pageSize = Math.min(display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight);
1.2687 - y = pos.top + dir * pageSize;
1.2688 - } else if (unit == "line") {
1.2689 - y = dir > 0 ? pos.bottom + 3 : pos.top - 3;
1.2690 - }
1.2691 - do {
1.2692 - var target = coordsChar(this, x, y);
1.2693 - y += dir * 5;
1.2694 - } while (target.outside && (dir < 0 ? y > 0 : y < doc.height));
1.2695 -
1.2696 - if (unit == "page") display.scrollbarV.scrollTop += charCoords(this, target, "div").top - pos.top;
1.2697 - extendSelection(this, target, target, dir);
1.2698 - view.goalColumn = x;
1.2699 - }),
1.2700 -
1.2701 - toggleOverwrite: function() {
1.2702 - if (this.view.overwrite = !this.view.overwrite)
1.2703 - this.display.cursor.className += " CodeMirror-overwrite";
1.2704 - else
1.2705 - this.display.cursor.className = this.display.cursor.className.replace(" CodeMirror-overwrite", "");
1.2706 - },
1.2707 -
1.2708 - posFromIndex: function(off) {
1.2709 - var lineNo = 0, ch, doc = this.view.doc;
1.2710 - doc.iter(0, doc.size, function(line) {
1.2711 - var sz = line.text.length + 1;
1.2712 - if (sz > off) { ch = off; return true; }
1.2713 - off -= sz;
1.2714 - ++lineNo;
1.2715 - });
1.2716 - return clipPos(doc, {line: lineNo, ch: ch});
1.2717 - },
1.2718 - indexFromPos: function (coords) {
1.2719 - if (coords.line < 0 || coords.ch < 0) return 0;
1.2720 - var index = coords.ch;
1.2721 - this.view.doc.iter(0, coords.line, function (line) {
1.2722 - index += line.text.length + 1;
1.2723 - });
1.2724 - return index;
1.2725 - },
1.2726 -
1.2727 - scrollTo: function(x, y) {
1.2728 - if (x != null) this.display.scrollbarH.scrollLeft = this.display.scroller.scrollLeft = x;
1.2729 - if (y != null) this.display.scrollbarV.scrollTop = this.display.scroller.scrollTop = y;
1.2730 - updateDisplay(this, []);
1.2731 - },
1.2732 - getScrollInfo: function() {
1.2733 - var scroller = this.display.scroller, co = scrollerCutOff;
1.2734 - return {left: scroller.scrollLeft, top: scroller.scrollTop,
1.2735 - height: scroller.scrollHeight - co, width: scroller.scrollWidth - co,
1.2736 - clientHeight: scroller.clientHeight - co, clientWidth: scroller.clientWidth - co};
1.2737 - },
1.2738 -
1.2739 - scrollIntoView: function(pos) {
1.2740 - if (typeof pos == "number") pos = {line: pos, ch: 0};
1.2741 - pos = pos ? clipPos(this.view.doc, pos) : this.view.sel.head;
1.2742 - scrollPosIntoView(this, pos);
1.2743 - },
1.2744 -
1.2745 - setSize: function(width, height) {
1.2746 - function interpret(val) {
1.2747 - return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val;
1.2748 - }
1.2749 - if (width != null) this.display.wrapper.style.width = interpret(width);
1.2750 - if (height != null) this.display.wrapper.style.height = interpret(height);
1.2751 - this.refresh();
1.2752 - },
1.2753 -
1.2754 - on: function(type, f) {on(this, type, f);},
1.2755 - off: function(type, f) {off(this, type, f);},
1.2756 -
1.2757 - operation: function(f){return operation(this, f)();},
1.2758 -
1.2759 - refresh: function() {
1.2760 - clearCaches(this);
1.2761 - if (this.display.scroller.scrollHeight > this.view.scrollTop)
1.2762 - this.display.scrollbarV.scrollTop = this.display.scroller.scrollTop = this.view.scrollTop;
1.2763 - updateDisplay(this, true);
1.2764 - },
1.2765 -
1.2766 - getInputField: function(){return this.display.input;},
1.2767 - getWrapperElement: function(){return this.display.wrapper;},
1.2768 - getScrollerElement: function(){return this.display.scroller;},
1.2769 - getGutterElement: function(){return this.display.gutters;}
1.2770 - };
1.2771 -
1.2772 - // OPTION DEFAULTS
1.2773 -
1.2774 - var optionHandlers = CodeMirror.optionHandlers = {};
1.2775 -
1.2776 - // The default configuration options.
1.2777 - var defaults = CodeMirror.defaults = {};
1.2778 -
1.2779 - function option(name, deflt, handle, notOnInit) {
1.2780 - CodeMirror.defaults[name] = deflt;
1.2781 - if (handle) optionHandlers[name] =
1.2782 - notOnInit ? function(cm, val, old) {if (old != Init) handle(cm, val, old);} : handle;
1.2783 - }
1.2784 -
1.2785 - var Init = CodeMirror.Init = {toString: function(){return "CodeMirror.Init";}};
1.2786 -
1.2787 - // These two are, on init, called from the constructor because they
1.2788 - // have to be initialized before the editor can start at all.
1.2789 - option("value", "", function(cm, val) {cm.setValue(val);}, true);
1.2790 - option("mode", null, loadMode, true);
1.2791 -
1.2792 - option("indentUnit", 2, loadMode, true);
1.2793 - option("indentWithTabs", false);
1.2794 - option("smartIndent", true);
1.2795 - option("tabSize", 4, function(cm) {
1.2796 - loadMode(cm);
1.2797 - clearCaches(cm);
1.2798 - updateDisplay(cm, true);
1.2799 - }, true);
1.2800 - option("electricChars", true);
1.2801 -
1.2802 - option("theme", "default", function(cm) {
1.2803 - themeChanged(cm);
1.2804 - guttersChanged(cm);
1.2805 - }, true);
1.2806 - option("keyMap", "default", keyMapChanged);
1.2807 - option("extraKeys", null);
1.2808 -
1.2809 - option("onKeyEvent", null);
1.2810 - option("onDragEvent", null);
1.2811 -
1.2812 - option("lineWrapping", false, wrappingChanged, true);
1.2813 - option("gutters", [], function(cm) {
1.2814 - setGuttersForLineNumbers(cm.options);
1.2815 - guttersChanged(cm);
1.2816 - }, true);
1.2817 - option("lineNumbers", false, function(cm) {
1.2818 - setGuttersForLineNumbers(cm.options);
1.2819 - guttersChanged(cm);
1.2820 - }, true);
1.2821 - option("firstLineNumber", 1, guttersChanged, true);
1.2822 - option("lineNumberFormatter", function(integer) {return integer;}, guttersChanged, true);
1.2823 - option("showCursorWhenSelecting", false, updateSelection, true);
1.2824 -
1.2825 - option("readOnly", false, function(cm, val) {
1.2826 - if (val == "nocursor") {onBlur(cm); cm.display.input.blur();}
1.2827 - else if (!val) resetInput(cm, true);
1.2828 - });
1.2829 - option("dragDrop", true);
1.2830 -
1.2831 - option("cursorBlinkRate", 530);
1.2832 - option("cursorHeight", 1);
1.2833 - option("workTime", 100);
1.2834 - option("workDelay", 100);
1.2835 - option("flattenSpans", true);
1.2836 - option("pollInterval", 100);
1.2837 - option("undoDepth", 40);
1.2838 - option("viewportMargin", 10, function(cm){cm.refresh();}, true);
1.2839 -
1.2840 - option("tabindex", null, function(cm, val) {
1.2841 - cm.display.input.tabIndex = val || "";
1.2842 - });
1.2843 - option("autofocus", null);
1.2844 -
1.2845 - // MODE DEFINITION AND QUERYING
1.2846 -
1.2847 - // Known modes, by name and by MIME
1.2848 - var modes = CodeMirror.modes = {}, mimeModes = CodeMirror.mimeModes = {};
1.2849 -
1.2850 - CodeMirror.defineMode = function(name, mode) {
1.2851 - if (!CodeMirror.defaults.mode && name != "null") CodeMirror.defaults.mode = name;
1.2852 - if (arguments.length > 2) {
1.2853 - mode.dependencies = [];
1.2854 - for (var i = 2; i < arguments.length; ++i) mode.dependencies.push(arguments[i]);
1.2855 - }
1.2856 - modes[name] = mode;
1.2857 - };
1.2858 -
1.2859 - CodeMirror.defineMIME = function(mime, spec) {
1.2860 - mimeModes[mime] = spec;
1.2861 - };
1.2862 -
1.2863 - CodeMirror.resolveMode = function(spec) {
1.2864 - if (typeof spec == "string" && mimeModes.hasOwnProperty(spec))
1.2865 - spec = mimeModes[spec];
1.2866 - else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec))
1.2867 - return CodeMirror.resolveMode("application/xml");
1.2868 - if (typeof spec == "string") return {name: spec};
1.2869 - else return spec || {name: "null"};
1.2870 - };
1.2871 -
1.2872 - CodeMirror.getMode = function(options, spec) {
1.2873 - var spec = CodeMirror.resolveMode(spec);
1.2874 - var mfactory = modes[spec.name];
1.2875 - if (!mfactory) return CodeMirror.getMode(options, "text/plain");
1.2876 - var modeObj = mfactory(options, spec);
1.2877 - if (modeExtensions.hasOwnProperty(spec.name)) {
1.2878 - var exts = modeExtensions[spec.name];
1.2879 - for (var prop in exts) {
1.2880 - if (!exts.hasOwnProperty(prop)) continue;
1.2881 - if (modeObj.hasOwnProperty(prop)) modeObj["_" + prop] = modeObj[prop];
1.2882 - modeObj[prop] = exts[prop];
1.2883 - }
1.2884 - }
1.2885 - modeObj.name = spec.name;
1.2886 - return modeObj;
1.2887 - };
1.2888 -
1.2889 - CodeMirror.defineMode("null", function() {
1.2890 - return {token: function(stream) {stream.skipToEnd();}};
1.2891 - });
1.2892 - CodeMirror.defineMIME("text/plain", "null");
1.2893 -
1.2894 - var modeExtensions = CodeMirror.modeExtensions = {};
1.2895 - CodeMirror.extendMode = function(mode, properties) {
1.2896 - var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});
1.2897 - for (var prop in properties) if (properties.hasOwnProperty(prop))
1.2898 - exts[prop] = properties[prop];
1.2899 - };
1.2900 -
1.2901 - // EXTENSIONS
1.2902 -
1.2903 - CodeMirror.defineExtension = function(name, func) {
1.2904 - CodeMirror.prototype[name] = func;
1.2905 - };
1.2906 -
1.2907 - CodeMirror.defineOption = option;
1.2908 -
1.2909 - var initHooks = [];
1.2910 - CodeMirror.defineInitHook = function(f) {initHooks.push(f);};
1.2911 -
1.2912 - // MODE STATE HANDLING
1.2913 -
1.2914 - // Utility functions for working with state. Exported because modes
1.2915 - // sometimes need to do this.
1.2916 - function copyState(mode, state) {
1.2917 - if (state === true) return state;
1.2918 - if (mode.copyState) return mode.copyState(state);
1.2919 - var nstate = {};
1.2920 - for (var n in state) {
1.2921 - var val = state[n];
1.2922 - if (val instanceof Array) val = val.concat([]);
1.2923 - nstate[n] = val;
1.2924 - }
1.2925 - return nstate;
1.2926 - }
1.2927 - CodeMirror.copyState = copyState;
1.2928 -
1.2929 - function startState(mode, a1, a2) {
1.2930 - return mode.startState ? mode.startState(a1, a2) : true;
1.2931 - }
1.2932 - CodeMirror.startState = startState;
1.2933 -
1.2934 - CodeMirror.innerMode = function(mode, state) {
1.2935 - while (mode.innerMode) {
1.2936 - var info = mode.innerMode(state);
1.2937 - state = info.state;
1.2938 - mode = info.mode;
1.2939 - }
1.2940 - return info || {mode: mode, state: state};
1.2941 - };
1.2942 -
1.2943 - // STANDARD COMMANDS
1.2944 -
1.2945 - var commands = CodeMirror.commands = {
1.2946 - selectAll: function(cm) {cm.setSelection({line: 0, ch: 0}, {line: cm.lineCount() - 1});},
1.2947 - killLine: function(cm) {
1.2948 - var from = cm.getCursor(true), to = cm.getCursor(false), sel = !posEq(from, to);
1.2949 - if (!sel && cm.getLine(from.line).length == from.ch)
1.2950 - cm.replaceRange("", from, {line: from.line + 1, ch: 0}, "delete");
1.2951 - else cm.replaceRange("", from, sel ? to : {line: from.line}, "delete");
1.2952 - },
1.2953 - deleteLine: function(cm) {
1.2954 - var l = cm.getCursor().line;
1.2955 - cm.replaceRange("", {line: l, ch: 0}, {line: l}, "delete");
1.2956 - },
1.2957 - undo: function(cm) {cm.undo();},
1.2958 - redo: function(cm) {cm.redo();},
1.2959 - goDocStart: function(cm) {cm.extendSelection({line: 0, ch: 0});},
1.2960 - goDocEnd: function(cm) {cm.extendSelection({line: cm.lineCount() - 1});},
1.2961 - goLineStart: function(cm) {
1.2962 - cm.extendSelection(lineStart(cm, cm.getCursor().line));
1.2963 - },
1.2964 - goLineStartSmart: function(cm) {
1.2965 - var cur = cm.getCursor(), start = lineStart(cm, cur.line);
1.2966 - var line = cm.getLineHandle(start.line);
1.2967 - var order = getOrder(line);
1.2968 - if (!order || order[0].level == 0) {
1.2969 - var firstNonWS = Math.max(0, line.text.search(/\S/));
1.2970 - var inWS = cur.line == start.line && cur.ch <= firstNonWS && cur.ch;
1.2971 - cm.extendSelection({line: start.line, ch: inWS ? 0 : firstNonWS});
1.2972 - } else cm.extendSelection(start);
1.2973 - },
1.2974 - goLineEnd: function(cm) {
1.2975 - cm.extendSelection(lineEnd(cm, cm.getCursor().line));
1.2976 - },
1.2977 - goLineUp: function(cm) {cm.moveV(-1, "line");},
1.2978 - goLineDown: function(cm) {cm.moveV(1, "line");},
1.2979 - goPageUp: function(cm) {cm.moveV(-1, "page");},
1.2980 - goPageDown: function(cm) {cm.moveV(1, "page");},
1.2981 - goCharLeft: function(cm) {cm.moveH(-1, "char");},
1.2982 - goCharRight: function(cm) {cm.moveH(1, "char");},
1.2983 - goColumnLeft: function(cm) {cm.moveH(-1, "column");},
1.2984 - goColumnRight: function(cm) {cm.moveH(1, "column");},
1.2985 - goWordLeft: function(cm) {cm.moveH(-1, "word");},
1.2986 - goWordRight: function(cm) {cm.moveH(1, "word");},
1.2987 - delCharBefore: function(cm) {cm.deleteH(-1, "char");},
1.2988 - delCharAfter: function(cm) {cm.deleteH(1, "char");},
1.2989 - delWordBefore: function(cm) {cm.deleteH(-1, "word");},
1.2990 - delWordAfter: function(cm) {cm.deleteH(1, "word");},
1.2991 - indentAuto: function(cm) {cm.indentSelection("smart");},
1.2992 - indentMore: function(cm) {cm.indentSelection("add");},
1.2993 - indentLess: function(cm) {cm.indentSelection("subtract");},
1.2994 - insertTab: function(cm) {cm.replaceSelection("\t", "end", "input");},
1.2995 - defaultTab: function(cm) {
1.2996 - if (cm.somethingSelected()) cm.indentSelection("add");
1.2997 - else cm.replaceSelection("\t", "end", "input");
1.2998 - },
1.2999 - transposeChars: function(cm) {
1.3000 - var cur = cm.getCursor(), line = cm.getLine(cur.line);
1.3001 - if (cur.ch > 0 && cur.ch < line.length - 1)
1.3002 - cm.replaceRange(line.charAt(cur.ch) + line.charAt(cur.ch - 1),
1.3003 - {line: cur.line, ch: cur.ch - 1}, {line: cur.line, ch: cur.ch + 1});
1.3004 - },
1.3005 - newlineAndIndent: function(cm) {
1.3006 - operation(cm, function() {
1.3007 - cm.replaceSelection("\n", "end", "input");
1.3008 - cm.indentLine(cm.getCursor().line, null, true);
1.3009 - })();
1.3010 - },
1.3011 - toggleOverwrite: function(cm) {cm.toggleOverwrite();}
1.3012 - };
1.3013 -
1.3014 - // STANDARD KEYMAPS
1.3015 -
1.3016 - var keyMap = CodeMirror.keyMap = {};
1.3017 - keyMap.basic = {
1.3018 - "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
1.3019 - "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
1.3020 - "Delete": "delCharAfter", "Backspace": "delCharBefore", "Tab": "defaultTab", "Shift-Tab": "indentAuto",
1.3021 - "Enter": "newlineAndIndent", "Insert": "toggleOverwrite"
1.3022 - };
1.3023 - // Note that the save and find-related commands aren't defined by
1.3024 - // default. Unknown commands are simply ignored.
1.3025 - keyMap.pcDefault = {
1.3026 - "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
1.3027 - "Ctrl-Home": "goDocStart", "Alt-Up": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Down": "goDocEnd",
1.3028 - "Ctrl-Left": "goWordLeft", "Ctrl-Right": "goWordRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
1.3029 - "Ctrl-Backspace": "delWordBefore", "Ctrl-Delete": "delWordAfter", "Ctrl-S": "save", "Ctrl-F": "find",
1.3030 - "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
1.3031 - "Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
1.3032 - fallthrough: "basic"
1.3033 - };
1.3034 - keyMap.macDefault = {
1.3035 - "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
1.3036 - "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goWordLeft",
1.3037 - "Alt-Right": "goWordRight", "Cmd-Left": "goLineStart", "Cmd-Right": "goLineEnd", "Alt-Backspace": "delWordBefore",
1.3038 - "Ctrl-Alt-Backspace": "delWordAfter", "Alt-Delete": "delWordAfter", "Cmd-S": "save", "Cmd-F": "find",
1.3039 - "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
1.3040 - "Cmd-[": "indentLess", "Cmd-]": "indentMore",
1.3041 - fallthrough: ["basic", "emacsy"]
1.3042 - };
1.3043 - keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault;
1.3044 - keyMap.emacsy = {
1.3045 - "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
1.3046 - "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
1.3047 - "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore",
1.3048 - "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars"
1.3049 - };
1.3050 -
1.3051 - // KEYMAP DISPATCH
1.3052 -
1.3053 - function getKeyMap(val) {
1.3054 - if (typeof val == "string") return keyMap[val];
1.3055 - else return val;
1.3056 - }
1.3057 -
1.3058 - function lookupKey(name, maps, handle, stop) {
1.3059 - function lookup(map) {
1.3060 - map = getKeyMap(map);
1.3061 - var found = map[name];
1.3062 - if (found === false) {
1.3063 - if (stop) stop();
1.3064 - return true;
1.3065 - }
1.3066 - if (found != null && handle(found)) return true;
1.3067 - if (map.nofallthrough) {
1.3068 - if (stop) stop();
1.3069 - return true;
1.3070 - }
1.3071 - var fallthrough = map.fallthrough;
1.3072 - if (fallthrough == null) return false;
1.3073 - if (Object.prototype.toString.call(fallthrough) != "[object Array]")
1.3074 - return lookup(fallthrough);
1.3075 - for (var i = 0, e = fallthrough.length; i < e; ++i) {
1.3076 - if (lookup(fallthrough[i])) return true;
1.3077 - }
1.3078 - return false;
1.3079 - }
1.3080 -
1.3081 - for (var i = 0; i < maps.length; ++i)
1.3082 - if (lookup(maps[i])) return true;
1.3083 - }
1.3084 - function isModifierKey(event) {
1.3085 - var name = keyNames[e_prop(event, "keyCode")];
1.3086 - return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod";
1.3087 - }
1.3088 - CodeMirror.isModifierKey = isModifierKey;
1.3089 -
1.3090 - // FROMTEXTAREA
1.3091 -
1.3092 - CodeMirror.fromTextArea = function(textarea, options) {
1.3093 - if (!options) options = {};
1.3094 - options.value = textarea.value;
1.3095 - if (!options.tabindex && textarea.tabindex)
1.3096 - options.tabindex = textarea.tabindex;
1.3097 - // Set autofocus to true if this textarea is focused, or if it has
1.3098 - // autofocus and no other element is focused.
1.3099 - if (options.autofocus == null) {
1.3100 - var hasFocus = document.body;
1.3101 - // doc.activeElement occasionally throws on IE
1.3102 - try { hasFocus = document.activeElement; } catch(e) {}
1.3103 - options.autofocus = hasFocus == textarea ||
1.3104 - textarea.getAttribute("autofocus") != null && hasFocus == document.body;
1.3105 - }
1.3106 -
1.3107 - function save() {textarea.value = cm.getValue();}
1.3108 - if (textarea.form) {
1.3109 - // Deplorable hack to make the submit method do the right thing.
1.3110 - on(textarea.form, "submit", save);
1.3111 - var form = textarea.form, realSubmit = form.submit;
1.3112 - try {
1.3113 - form.submit = function wrappedSubmit() {
1.3114 - save();
1.3115 - form.submit = realSubmit;
1.3116 - form.submit();
1.3117 - form.submit = wrappedSubmit;
1.3118 - };
1.3119 - } catch(e) {}
1.3120 - }
1.3121 -
1.3122 - textarea.style.display = "none";
1.3123 - var cm = CodeMirror(function(node) {
1.3124 - textarea.parentNode.insertBefore(node, textarea.nextSibling);
1.3125 - }, options);
1.3126 - cm.save = save;
1.3127 - cm.getTextArea = function() { return textarea; };
1.3128 - cm.toTextArea = function() {
1.3129 - save();
1.3130 - textarea.parentNode.removeChild(cm.getWrapperElement());
1.3131 - textarea.style.display = "";
1.3132 - if (textarea.form) {
1.3133 - off(textarea.form, "submit", save);
1.3134 - if (typeof textarea.form.submit == "function")
1.3135 - textarea.form.submit = realSubmit;
1.3136 - }
1.3137 - };
1.3138 - return cm;
1.3139 - };
1.3140 -
1.3141 - // STRING STREAM
1.3142 -
1.3143 - // Fed to the mode parsers, provides helper functions to make
1.3144 - // parsers more succinct.
1.3145 -
1.3146 - // The character stream used by a mode's parser.
1.3147 - function StringStream(string, tabSize) {
1.3148 - this.pos = this.start = 0;
1.3149 - this.string = string;
1.3150 - this.tabSize = tabSize || 8;
1.3151 - }
1.3152 -
1.3153 - StringStream.prototype = {
1.3154 - eol: function() {return this.pos >= this.string.length;},
1.3155 - sol: function() {return this.pos == 0;},
1.3156 - peek: function() {return this.string.charAt(this.pos) || undefined;},
1.3157 - next: function() {
1.3158 - if (this.pos < this.string.length)
1.3159 - return this.string.charAt(this.pos++);
1.3160 - },
1.3161 - eat: function(match) {
1.3162 - var ch = this.string.charAt(this.pos);
1.3163 - if (typeof match == "string") var ok = ch == match;
1.3164 - else var ok = ch && (match.test ? match.test(ch) : match(ch));
1.3165 - if (ok) {++this.pos; return ch;}
1.3166 - },
1.3167 - eatWhile: function(match) {
1.3168 - var start = this.pos;
1.3169 - while (this.eat(match)){}
1.3170 - return this.pos > start;
1.3171 - },
1.3172 - eatSpace: function() {
1.3173 - var start = this.pos;
1.3174 - while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) ++this.pos;
1.3175 - return this.pos > start;
1.3176 - },
1.3177 - skipToEnd: function() {this.pos = this.string.length;},
1.3178 - skipTo: function(ch) {
1.3179 - var found = this.string.indexOf(ch, this.pos);
1.3180 - if (found > -1) {this.pos = found; return true;}
1.3181 - },
1.3182 - backUp: function(n) {this.pos -= n;},
1.3183 - column: function() {return countColumn(this.string, this.start, this.tabSize);},
1.3184 - indentation: function() {return countColumn(this.string, null, this.tabSize);},
1.3185 - match: function(pattern, consume, caseInsensitive) {
1.3186 - if (typeof pattern == "string") {
1.3187 - var cased = function(str) {return caseInsensitive ? str.toLowerCase() : str;};
1.3188 - if (cased(this.string).indexOf(cased(pattern), this.pos) == this.pos) {
1.3189 - if (consume !== false) this.pos += pattern.length;
1.3190 - return true;
1.3191 - }
1.3192 - } else {
1.3193 - var match = this.string.slice(this.pos).match(pattern);
1.3194 - if (match && match.index > 0) return null;
1.3195 - if (match && consume !== false) this.pos += match[0].length;
1.3196 - return match;
1.3197 - }
1.3198 - },
1.3199 - current: function(){return this.string.slice(this.start, this.pos);}
1.3200 - };
1.3201 - CodeMirror.StringStream = StringStream;
1.3202 -
1.3203 - // TEXTMARKERS
1.3204 -
1.3205 - function TextMarker(cm, type) {
1.3206 - this.lines = [];
1.3207 - this.type = type;
1.3208 - this.cm = cm;
1.3209 - }
1.3210 -
1.3211 - TextMarker.prototype.clear = function() {
1.3212 - if (this.explicitlyCleared) return;
1.3213 - startOperation(this.cm);
1.3214 - var min = null, max = null;
1.3215 - for (var i = 0; i < this.lines.length; ++i) {
1.3216 - var line = this.lines[i];
1.3217 - var span = getMarkedSpanFor(line.markedSpans, this);
1.3218 - if (span.to != null) max = lineNo(line);
1.3219 - line.markedSpans = removeMarkedSpan(line.markedSpans, span);
1.3220 - if (span.from != null)
1.3221 - min = lineNo(line);
1.3222 - else if (this.collapsed && !lineIsHidden(line))
1.3223 - updateLineHeight(line, textHeight(this.cm.display));
1.3224 - }
1.3225 - if (min != null) regChange(this.cm, min, max + 1);
1.3226 - this.lines.length = 0;
1.3227 - this.explicitlyCleared = true;
1.3228 - if (this.collapsed && this.cm.view.cantEdit) {
1.3229 - this.cm.view.cantEdit = false;
1.3230 - reCheckSelection(this.cm);
1.3231 - }
1.3232 - endOperation(this.cm);
1.3233 - signalLater(this.cm, this, "clear");
1.3234 - };
1.3235 -
1.3236 - TextMarker.prototype.find = function() {
1.3237 - var from, to;
1.3238 - for (var i = 0; i < this.lines.length; ++i) {
1.3239 - var line = this.lines[i];
1.3240 - var span = getMarkedSpanFor(line.markedSpans, this);
1.3241 - if (span.from != null || span.to != null) {
1.3242 - var found = lineNo(line);
1.3243 - if (span.from != null) from = {line: found, ch: span.from};
1.3244 - if (span.to != null) to = {line: found, ch: span.to};
1.3245 - }
1.3246 - }
1.3247 - if (this.type == "bookmark") return from;
1.3248 - return from && {from: from, to: to};
1.3249 - };
1.3250 -
1.3251 - function markText(cm, from, to, options, type) {
1.3252 - var doc = cm.view.doc;
1.3253 - var marker = new TextMarker(cm, type);
1.3254 - if (type == "range" && !posLess(from, to)) return marker;
1.3255 - if (options) for (var opt in options) if (options.hasOwnProperty(opt))
1.3256 - marker[opt] = options[opt];
1.3257 - if (marker.replacedWith) {
1.3258 - marker.collapsed = true;
1.3259 - marker.replacedWith = elt("span", [marker.replacedWith], "CodeMirror-widget");
1.3260 - }
1.3261 - if (marker.collapsed) sawCollapsedSpans = true;
1.3262 -
1.3263 - var curLine = from.line, size = 0, collapsedAtStart, collapsedAtEnd;
1.3264 - doc.iter(curLine, to.line + 1, function(line) {
1.3265 - var span = {from: null, to: null, marker: marker};
1.3266 - size += line.text.length;
1.3267 - if (curLine == from.line) {span.from = from.ch; size -= from.ch;}
1.3268 - if (curLine == to.line) {span.to = to.ch; size -= line.text.length - to.ch;}
1.3269 - if (marker.collapsed) {
1.3270 - if (curLine == to.line) collapsedAtEnd = collapsedSpanAt(line, to.ch);
1.3271 - if (curLine == from.line) collapsedAtStart = collapsedSpanAt(line, from.ch);
1.3272 - else updateLineHeight(line, 0);
1.3273 - }
1.3274 - addMarkedSpan(line, span);
1.3275 - if (marker.collapsed && curLine == from.line && lineIsHidden(line))
1.3276 - updateLineHeight(line, 0);
1.3277 - ++curLine;
1.3278 - });
1.3279 -
1.3280 - if (marker.readOnly) {
1.3281 - sawReadOnlySpans = true;
1.3282 - if (cm.view.history.done.length || cm.view.history.undone.length)
1.3283 - cm.clearHistory();
1.3284 - }
1.3285 - if (marker.collapsed) {
1.3286 - if (collapsedAtStart != collapsedAtEnd)
1.3287 - throw new Error("Inserting collapsed marker overlapping an existing one");
1.3288 - marker.size = size;
1.3289 - marker.atomic = true;
1.3290 - }
1.3291 - if (marker.className || marker.startStyle || marker.endStyle || marker.collapsed)
1.3292 - regChange(cm, from.line, to.line + 1);
1.3293 - if (marker.atomic) reCheckSelection(cm);
1.3294 - return marker;
1.3295 - }
1.3296 -
1.3297 - // TEXTMARKER SPANS
1.3298 -
1.3299 - function getMarkedSpanFor(spans, marker) {
1.3300 - if (spans) for (var i = 0; i < spans.length; ++i) {
1.3301 - var span = spans[i];
1.3302 - if (span.marker == marker) return span;
1.3303 - }
1.3304 - }
1.3305 - function removeMarkedSpan(spans, span) {
1.3306 - for (var r, i = 0; i < spans.length; ++i)
1.3307 - if (spans[i] != span) (r || (r = [])).push(spans[i]);
1.3308 - return r;
1.3309 - }
1.3310 - function addMarkedSpan(line, span) {
1.3311 - line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span];
1.3312 - span.marker.lines.push(line);
1.3313 - }
1.3314 -
1.3315 - function markedSpansBefore(old, startCh) {
1.3316 - if (old) for (var i = 0, nw; i < old.length; ++i) {
1.3317 - var span = old[i], marker = span.marker;
1.3318 - var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh);
1.3319 - if (startsBefore || marker.type == "bookmark" && span.from == startCh) {
1.3320 - var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh);
1.3321 - (nw || (nw = [])).push({from: span.from,
1.3322 - to: endsAfter ? null : span.to,
1.3323 - marker: marker});
1.3324 - }
1.3325 - }
1.3326 - return nw;
1.3327 - }
1.3328 -
1.3329 - function markedSpansAfter(old, startCh, endCh) {
1.3330 - if (old) for (var i = 0, nw; i < old.length; ++i) {
1.3331 - var span = old[i], marker = span.marker;
1.3332 - var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh);
1.3333 - if (endsAfter || marker.type == "bookmark" && span.from == endCh && span.from != startCh) {
1.3334 - var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh);
1.3335 - (nw || (nw = [])).push({from: startsBefore ? null : span.from - endCh,
1.3336 - to: span.to == null ? null : span.to - endCh,
1.3337 - marker: marker});
1.3338 - }
1.3339 - }
1.3340 - return nw;
1.3341 - }
1.3342 -
1.3343 - function updateMarkedSpans(oldFirst, oldLast, startCh, endCh, newText) {
1.3344 - if (!oldFirst && !oldLast) return newText;
1.3345 - // Get the spans that 'stick out' on both sides
1.3346 - var first = markedSpansBefore(oldFirst, startCh);
1.3347 - var last = markedSpansAfter(oldLast, startCh, endCh);
1.3348 -
1.3349 - // Next, merge those two ends
1.3350 - var sameLine = newText.length == 1, offset = lst(newText).length + (sameLine ? startCh : 0);
1.3351 - if (first) {
1.3352 - // Fix up .to properties of first
1.3353 - for (var i = 0; i < first.length; ++i) {
1.3354 - var span = first[i];
1.3355 - if (span.to == null) {
1.3356 - var found = getMarkedSpanFor(last, span.marker);
1.3357 - if (!found) span.to = startCh;
1.3358 - else if (sameLine) span.to = found.to == null ? null : found.to + offset;
1.3359 - }
1.3360 - }
1.3361 - }
1.3362 - if (last) {
1.3363 - // Fix up .from in last (or move them into first in case of sameLine)
1.3364 - for (var i = 0; i < last.length; ++i) {
1.3365 - var span = last[i];
1.3366 - if (span.to != null) span.to += offset;
1.3367 - if (span.from == null) {
1.3368 - var found = getMarkedSpanFor(first, span.marker);
1.3369 - if (!found) {
1.3370 - span.from = offset;
1.3371 - if (sameLine) (first || (first = [])).push(span);
1.3372 - }
1.3373 - } else {
1.3374 - span.from += offset;
1.3375 - if (sameLine) (first || (first = [])).push(span);
1.3376 - }
1.3377 - }
1.3378 - }
1.3379 -
1.3380 - var newMarkers = [newHL(newText[0], first)];
1.3381 - if (!sameLine) {
1.3382 - // Fill gap with whole-line-spans
1.3383 - var gap = newText.length - 2, gapMarkers;
1.3384 - if (gap > 0 && first)
1.3385 - for (var i = 0; i < first.length; ++i)
1.3386 - if (first[i].to == null)
1.3387 - (gapMarkers || (gapMarkers = [])).push({from: null, to: null, marker: first[i].marker});
1.3388 - for (var i = 0; i < gap; ++i)
1.3389 - newMarkers.push(newHL(newText[i+1], gapMarkers));
1.3390 - newMarkers.push(newHL(lst(newText), last));
1.3391 - }
1.3392 - return newMarkers;
1.3393 - }
1.3394 -
1.3395 - function removeReadOnlyRanges(doc, from, to) {
1.3396 - var markers = null;
1.3397 - doc.iter(from.line, to.line + 1, function(line) {
1.3398 - if (line.markedSpans) for (var i = 0; i < line.markedSpans.length; ++i) {
1.3399 - var mark = line.markedSpans[i].marker;
1.3400 - if (mark.readOnly && (!markers || indexOf(markers, mark) == -1))
1.3401 - (markers || (markers = [])).push(mark);
1.3402 - }
1.3403 - });
1.3404 - if (!markers) return null;
1.3405 - var parts = [{from: from, to: to}];
1.3406 - for (var i = 0; i < markers.length; ++i) {
1.3407 - var m = markers[i].find();
1.3408 - for (var j = 0; j < parts.length; ++j) {
1.3409 - var p = parts[j];
1.3410 - if (!posLess(m.from, p.to) || posLess(m.to, p.from)) continue;
1.3411 - var newParts = [j, 1];
1.3412 - if (posLess(p.from, m.from)) newParts.push({from: p.from, to: m.from});
1.3413 - if (posLess(m.to, p.to)) newParts.push({from: m.to, to: p.to});
1.3414 - parts.splice.apply(parts, newParts);
1.3415 - j += newParts.length - 1;
1.3416 - }
1.3417 - }
1.3418 - return parts;
1.3419 - }
1.3420 -
1.3421 - function collapsedSpanAt(line, ch) {
1.3422 - var sps = sawCollapsedSpans && line.markedSpans, found;
1.3423 - if (sps) for (var sp, i = 0; i < sps.length; ++i) {
1.3424 - sp = sps[i];
1.3425 - if (!sp.marker.collapsed) continue;
1.3426 - if ((sp.from == null || sp.from < ch) &&
1.3427 - (sp.to == null || sp.to > ch) &&
1.3428 - (!found || found.width < sp.marker.width))
1.3429 - found = sp.marker;
1.3430 - }
1.3431 - return found;
1.3432 - }
1.3433 - function collapsedSpanAtStart(line) { return collapsedSpanAt(line, -1); }
1.3434 - function collapsedSpanAtEnd(line) { return collapsedSpanAt(line, line.text.length + 1); }
1.3435 -
1.3436 - function visualLine(doc, line) {
1.3437 - var merged;
1.3438 - while (merged = collapsedSpanAtStart(line))
1.3439 - line = getLine(doc, merged.find().from.line);
1.3440 - return line;
1.3441 - }
1.3442 -
1.3443 - function lineIsHidden(line) {
1.3444 - var sps = sawCollapsedSpans && line.markedSpans;
1.3445 - if (sps) for (var sp, i = 0; i < sps.length; ++i) {
1.3446 - sp = sps[i];
1.3447 - if (!sp.marker.collapsed) continue;
1.3448 - if (sp.from == null) return true;
1.3449 - if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(line, sp))
1.3450 - return true;
1.3451 - }
1.3452 - }
1.3453 - window.lineIsHidden = lineIsHidden;
1.3454 - function lineIsHiddenInner(line, span) {
1.3455 - if (span.to == null || span.marker.inclusiveRight && span.to == line.text.length)
1.3456 - return true;
1.3457 - for (var sp, i = 0; i < line.markedSpans.length; ++i) {
1.3458 - sp = line.markedSpans[i];
1.3459 - if (sp.marker.collapsed && sp.from == span.to &&
1.3460 - (sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
1.3461 - lineIsHiddenInner(line, sp)) return true;
1.3462 - }
1.3463 - }
1.3464 -
1.3465 - // hl stands for history-line, a data structure that can be either a
1.3466 - // string (line without markers) or a {text, markedSpans} object.
1.3467 - function hlText(val) { return typeof val == "string" ? val : val.text; }
1.3468 - function hlSpans(val) {
1.3469 - if (typeof val == "string") return null;
1.3470 - var spans = val.markedSpans, out = null;
1.3471 - for (var i = 0; i < spans.length; ++i) {
1.3472 - if (spans[i].marker.explicitlyCleared) { if (!out) out = spans.slice(0, i); }
1.3473 - else if (out) out.push(spans[i]);
1.3474 - }
1.3475 - return !out ? spans : out.length ? out : null;
1.3476 - }
1.3477 - function newHL(text, spans) { return spans ? {text: text, markedSpans: spans} : text; }
1.3478 -
1.3479 - function detachMarkedSpans(line) {
1.3480 - var spans = line.markedSpans;
1.3481 - if (!spans) return;
1.3482 - for (var i = 0; i < spans.length; ++i) {
1.3483 - var lines = spans[i].marker.lines;
1.3484 - var ix = indexOf(lines, line);
1.3485 - lines.splice(ix, 1);
1.3486 - }
1.3487 - line.markedSpans = null;
1.3488 - }
1.3489 -
1.3490 - function attachMarkedSpans(line, spans) {
1.3491 - if (!spans) return;
1.3492 - for (var i = 0; i < spans.length; ++i)
1.3493 - spans[i].marker.lines.push(line);
1.3494 - line.markedSpans = spans;
1.3495 - }
1.3496 -
1.3497 - // LINE DATA STRUCTURE
1.3498 -
1.3499 - // Line objects. These hold state related to a line, including
1.3500 - // highlighting info (the styles array).
1.3501 - function makeLine(text, markedSpans, height) {
1.3502 - var line = {text: text, height: height};
1.3503 - attachMarkedSpans(line, markedSpans);
1.3504 - if (lineIsHidden(line)) line.height = 0;
1.3505 - return line;
1.3506 - }
1.3507 -
1.3508 - function updateLine(cm, line, text, markedSpans) {
1.3509 - line.text = text;
1.3510 - line.stateAfter = line.styles = null;
1.3511 - if (line.order != null) line.order = null;
1.3512 - detachMarkedSpans(line);
1.3513 - attachMarkedSpans(line, markedSpans);
1.3514 - if (lineIsHidden(line)) line.height = 0;
1.3515 - else if (!line.height) line.height = textHeight(cm.display);
1.3516 - signalLater(cm, line, "change");
1.3517 - }
1.3518 -
1.3519 - function cleanUpLine(line) {
1.3520 - line.parent = null;
1.3521 - detachMarkedSpans(line);
1.3522 - }
1.3523 -
1.3524 - // Run the given mode's parser over a line, update the styles
1.3525 - // array, which contains alternating fragments of text and CSS
1.3526 - // classes.
1.3527 - function highlightLine(cm, line, state) {
1.3528 - var mode = cm.view.mode, flattenSpans = cm.options.flattenSpans;
1.3529 - var changed = !line.styles, pos = 0, curText = "", curStyle = null;
1.3530 - var stream = new StringStream(line.text, cm.options.tabSize), st = line.styles || (line.styles = []);
1.3531 - if (line.text == "" && mode.blankLine) mode.blankLine(state);
1.3532 - while (!stream.eol()) {
1.3533 - var style = mode.token(stream, state), substr = stream.current();
1.3534 - stream.start = stream.pos;
1.3535 - if (!flattenSpans || curStyle != style) {
1.3536 - if (curText) {
1.3537 - changed = changed || pos >= st.length || curText != st[pos] || curStyle != st[pos+1];
1.3538 - st[pos++] = curText; st[pos++] = curStyle;
1.3539 - }
1.3540 - curText = substr; curStyle = style;
1.3541 - } else curText = curText + substr;
1.3542 - // Give up when line is ridiculously long
1.3543 - if (stream.pos > 5000) break;
1.3544 - }
1.3545 - if (curText) {
1.3546 - changed = changed || pos >= st.length || curText != st[pos] || curStyle != st[pos+1];
1.3547 - st[pos++] = curText; st[pos++] = curStyle;
1.3548 - }
1.3549 - if (stream.pos > 5000) { st[pos++] = line.text.slice(stream.pos); st[pos++] = null; }
1.3550 - if (pos != st.length) { st.length = pos; changed = true; }
1.3551 - return changed;
1.3552 - }
1.3553 -
1.3554 - // Lightweight form of highlight -- proceed over this line and
1.3555 - // update state, but don't save a style array.
1.3556 - function processLine(cm, line, state) {
1.3557 - var mode = cm.view.mode;
1.3558 - var stream = new StringStream(line.text, cm.options.tabSize);
1.3559 - if (line.text == "" && mode.blankLine) mode.blankLine(state);
1.3560 - while (!stream.eol() && stream.pos <= 5000) {
1.3561 - mode.token(stream, state);
1.3562 - stream.start = stream.pos;
1.3563 - }
1.3564 - }
1.3565 -
1.3566 - var styleToClassCache = {};
1.3567 - function styleToClass(style) {
1.3568 - if (!style) return null;
1.3569 - return styleToClassCache[style] ||
1.3570 - (styleToClassCache[style] = "cm-" + style.replace(/ +/g, " cm-"));
1.3571 - }
1.3572 -
1.3573 - function lineContent(cm, realLine, measure) {
1.3574 - var merged, line = realLine, lineBefore, sawBefore, simple = true;
1.3575 - while (merged = collapsedSpanAtStart(line)) {
1.3576 - simple = false;
1.3577 - line = getLine(cm.view.doc, merged.find().from.line);
1.3578 - if (!lineBefore) lineBefore = line;
1.3579 - }
1.3580 -
1.3581 - var builder = {pre: elt("pre"), col: 0, pos: 0, display: !measure,
1.3582 - measure: null, addedOne: false, cm: cm};
1.3583 - if (line.textClass) builder.pre.className = line.textClass;
1.3584 -
1.3585 - do {
1.3586 - if (!line.styles)
1.3587 - highlightLine(cm, line, line.stateAfter = getStateBefore(cm, lineNo(line)));
1.3588 - builder.measure = line == realLine && measure;
1.3589 - builder.pos = 0;
1.3590 - builder.addToken = builder.measure ? buildTokenMeasure : buildToken;
1.3591 - if (measure && sawBefore && line != realLine && !builder.addedOne) {
1.3592 - measure[0] = builder.pre.appendChild(zeroWidthElement(cm.display.measure));
1.3593 - builder.addedOne = true;
1.3594 - }
1.3595 - var next = insertLineContent(line, builder);
1.3596 - sawBefore = line == lineBefore;
1.3597 - if (next) {
1.3598 - line = getLine(cm.view.doc, next.to.line);
1.3599 - simple = false;
1.3600 - }
1.3601 - } while (next);
1.3602 -
1.3603 - if (measure && !builder.addedOne)
1.3604 - measure[0] = builder.pre.appendChild(simple ? elt("span", "\u00a0") : zeroWidthElement(cm.display.measure));
1.3605 - if (!builder.pre.firstChild && !lineIsHidden(realLine))
1.3606 - builder.pre.appendChild(document.createTextNode("\u00a0"));
1.3607 -
1.3608 - return builder.pre;
1.3609 - }
1.3610 -
1.3611 - var tokenSpecialChars = /[\t\u0000-\u0019\u200b\u2028\u2029\uFEFF]/g;
1.3612 - function buildToken(builder, text, style, startStyle, endStyle) {
1.3613 - if (!text) return;
1.3614 - if (!tokenSpecialChars.test(text)) {
1.3615 - builder.col += text.length;
1.3616 - var content = document.createTextNode(text);
1.3617 - } else {
1.3618 - var content = document.createDocumentFragment(), pos = 0;
1.3619 - while (true) {
1.3620 - tokenSpecialChars.lastIndex = pos;
1.3621 - var m = tokenSpecialChars.exec(text);
1.3622 - var skipped = m ? m.index - pos : text.length - pos;
1.3623 - if (skipped) {
1.3624 - content.appendChild(document.createTextNode(text.slice(pos, pos + skipped)));
1.3625 - builder.col += skipped;
1.3626 - }
1.3627 - if (!m) break;
1.3628 - pos += skipped + 1;
1.3629 - if (m[0] == "\t") {
1.3630 - var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize;
1.3631 - content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab"));
1.3632 - builder.col += tabWidth;
1.3633 - } else {
1.3634 - var token = elt("span", "\u2022", "cm-invalidchar");
1.3635 - token.title = "\\u" + m[0].charCodeAt(0).toString(16);
1.3636 - content.appendChild(token);
1.3637 - builder.col += 1;
1.3638 - }
1.3639 - }
1.3640 - }
1.3641 - if (style || startStyle || endStyle || builder.measure) {
1.3642 - var fullStyle = style || "";
1.3643 - if (startStyle) fullStyle += startStyle;
1.3644 - if (endStyle) fullStyle += endStyle;
1.3645 - return builder.pre.appendChild(elt("span", [content], fullStyle));
1.3646 - }
1.3647 - builder.pre.appendChild(content);
1.3648 - }
1.3649 -
1.3650 - function buildTokenMeasure(builder, text, style, startStyle, endStyle) {
1.3651 - for (var i = 0; i < text.length; ++i) {
1.3652 - if (i && i < text.length - 1 &&
1.3653 - builder.cm.options.lineWrapping &&
1.3654 - spanAffectsWrapping.test(text.slice(i - 1, i + 1)))
1.3655 - builder.pre.appendChild(elt("wbr"));
1.3656 - builder.measure[builder.pos++] =
1.3657 - buildToken(builder, text.charAt(i), style,
1.3658 - i == 0 && startStyle, i == text.length - 1 && endStyle);
1.3659 - }
1.3660 - if (text.length) builder.addedOne = true;
1.3661 - }
1.3662 -
1.3663 - function buildCollapsedSpan(builder, size, widget) {
1.3664 - if (widget) {
1.3665 - if (!builder.display) widget = widget.cloneNode(true);
1.3666 - builder.pre.appendChild(widget);
1.3667 - if (builder.measure && size) {
1.3668 - builder.measure[builder.pos] = widget;
1.3669 - builder.addedOne = true;
1.3670 - }
1.3671 - }
1.3672 - builder.pos += size;
1.3673 - }
1.3674 -
1.3675 - // Outputs a number of spans to make up a line, taking highlighting
1.3676 - // and marked text into account.
1.3677 - function insertLineContent(line, builder) {
1.3678 - var st = line.styles, spans = line.markedSpans;
1.3679 - if (!spans) {
1.3680 - for (var i = 0; i < st.length; i+=2)
1.3681 - builder.addToken(builder, st[i], styleToClass(st[i+1]));
1.3682 - return;
1.3683 - }
1.3684 -
1.3685 - var allText = line.text, len = allText.length;
1.3686 - var pos = 0, i = 0, text = "", style;
1.3687 - var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, collapsed;
1.3688 - for (;;) {
1.3689 - if (nextChange == pos) { // Update current marker set
1.3690 - spanStyle = spanEndStyle = spanStartStyle = "";
1.3691 - collapsed = null; nextChange = Infinity;
1.3692 - var foundBookmark = null;
1.3693 - for (var j = 0; j < spans.length; ++j) {
1.3694 - var sp = spans[j], m = sp.marker;
1.3695 - if (sp.from <= pos && (sp.to == null || sp.to > pos)) {
1.3696 - if (sp.to != null && nextChange > sp.to) { nextChange = sp.to; spanEndStyle = ""; }
1.3697 - if (m.className) spanStyle += " " + m.className;
1.3698 - if (m.startStyle && sp.from == pos) spanStartStyle += " " + m.startStyle;
1.3699 - if (m.endStyle && sp.to == nextChange) spanEndStyle += " " + m.endStyle;
1.3700 - if (m.collapsed && (!collapsed || collapsed.marker.width < m.width))
1.3701 - collapsed = sp;
1.3702 - } else if (sp.from > pos && nextChange > sp.from) {
1.3703 - nextChange = sp.from;
1.3704 - }
1.3705 - if (m.type == "bookmark" && sp.from == pos && m.replacedWith)
1.3706 - foundBookmark = m.replacedWith;
1.3707 - }
1.3708 - if (collapsed && (collapsed.from || 0) == pos) {
1.3709 - buildCollapsedSpan(builder, (collapsed.to == null ? len : collapsed.to) - pos,
1.3710 - collapsed.from != null && collapsed.marker.replacedWith);
1.3711 - if (collapsed.to == null) return collapsed.marker.find();
1.3712 - }
1.3713 - if (foundBookmark && !collapsed) buildCollapsedSpan(builder, 0, foundBookmark);
1.3714 - }
1.3715 - if (pos >= len) break;
1.3716 -
1.3717 - var upto = Math.min(len, nextChange);
1.3718 - while (true) {
1.3719 - if (text) {
1.3720 - var end = pos + text.length;
1.3721 - if (!collapsed) {
1.3722 - var tokenText = end > upto ? text.slice(0, upto - pos) : text;
1.3723 - builder.addToken(builder, tokenText, style + spanStyle,
1.3724 - spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "");
1.3725 - }
1.3726 - if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;}
1.3727 - pos = end;
1.3728 - spanStartStyle = "";
1.3729 - }
1.3730 - text = st[i++]; style = styleToClass(st[i++]);
1.3731 - }
1.3732 - }
1.3733 - }
1.3734 -
1.3735 - // DOCUMENT DATA STRUCTURE
1.3736 -
1.3737 - function LeafChunk(lines) {
1.3738 - this.lines = lines;
1.3739 - this.parent = null;
1.3740 - for (var i = 0, e = lines.length, height = 0; i < e; ++i) {
1.3741 - lines[i].parent = this;
1.3742 - height += lines[i].height;
1.3743 - }
1.3744 - this.height = height;
1.3745 - }
1.3746 -
1.3747 - LeafChunk.prototype = {
1.3748 - chunkSize: function() { return this.lines.length; },
1.3749 - remove: function(at, n, cm) {
1.3750 - for (var i = at, e = at + n; i < e; ++i) {
1.3751 - var line = this.lines[i];
1.3752 - this.height -= line.height;
1.3753 - cleanUpLine(line);
1.3754 - signalLater(cm, line, "delete");
1.3755 - }
1.3756 - this.lines.splice(at, n);
1.3757 - },
1.3758 - collapse: function(lines) {
1.3759 - lines.splice.apply(lines, [lines.length, 0].concat(this.lines));
1.3760 - },
1.3761 - insertHeight: function(at, lines, height) {
1.3762 - this.height += height;
1.3763 - this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at));
1.3764 - for (var i = 0, e = lines.length; i < e; ++i) lines[i].parent = this;
1.3765 - },
1.3766 - iterN: function(at, n, op) {
1.3767 - for (var e = at + n; at < e; ++at)
1.3768 - if (op(this.lines[at])) return true;
1.3769 - }
1.3770 - };
1.3771 -
1.3772 - function BranchChunk(children) {
1.3773 - this.children = children;
1.3774 - var size = 0, height = 0;
1.3775 - for (var i = 0, e = children.length; i < e; ++i) {
1.3776 - var ch = children[i];
1.3777 - size += ch.chunkSize(); height += ch.height;
1.3778 - ch.parent = this;
1.3779 - }
1.3780 - this.size = size;
1.3781 - this.height = height;
1.3782 - this.parent = null;
1.3783 - }
1.3784 -
1.3785 - BranchChunk.prototype = {
1.3786 - chunkSize: function() { return this.size; },
1.3787 - remove: function(at, n, callbacks) {
1.3788 - this.size -= n;
1.3789 - for (var i = 0; i < this.children.length; ++i) {
1.3790 - var child = this.children[i], sz = child.chunkSize();
1.3791 - if (at < sz) {
1.3792 - var rm = Math.min(n, sz - at), oldHeight = child.height;
1.3793 - child.remove(at, rm, callbacks);
1.3794 - this.height -= oldHeight - child.height;
1.3795 - if (sz == rm) { this.children.splice(i--, 1); child.parent = null; }
1.3796 - if ((n -= rm) == 0) break;
1.3797 - at = 0;
1.3798 - } else at -= sz;
1.3799 - }
1.3800 - if (this.size - n < 25) {
1.3801 - var lines = [];
1.3802 - this.collapse(lines);
1.3803 - this.children = [new LeafChunk(lines)];
1.3804 - this.children[0].parent = this;
1.3805 - }
1.3806 - },
1.3807 - collapse: function(lines) {
1.3808 - for (var i = 0, e = this.children.length; i < e; ++i) this.children[i].collapse(lines);
1.3809 - },
1.3810 - insert: function(at, lines) {
1.3811 - var height = 0;
1.3812 - for (var i = 0, e = lines.length; i < e; ++i) height += lines[i].height;
1.3813 - this.insertHeight(at, lines, height);
1.3814 - },
1.3815 - insertHeight: function(at, lines, height) {
1.3816 - this.size += lines.length;
1.3817 - this.height += height;
1.3818 - for (var i = 0, e = this.children.length; i < e; ++i) {
1.3819 - var child = this.children[i], sz = child.chunkSize();
1.3820 - if (at <= sz) {
1.3821 - child.insertHeight(at, lines, height);
1.3822 - if (child.lines && child.lines.length > 50) {
1.3823 - while (child.lines.length > 50) {
1.3824 - var spilled = child.lines.splice(child.lines.length - 25, 25);
1.3825 - var newleaf = new LeafChunk(spilled);
1.3826 - child.height -= newleaf.height;
1.3827 - this.children.splice(i + 1, 0, newleaf);
1.3828 - newleaf.parent = this;
1.3829 - }
1.3830 - this.maybeSpill();
1.3831 - }
1.3832 - break;
1.3833 - }
1.3834 - at -= sz;
1.3835 - }
1.3836 - },
1.3837 - maybeSpill: function() {
1.3838 - if (this.children.length <= 10) return;
1.3839 - var me = this;
1.3840 - do {
1.3841 - var spilled = me.children.splice(me.children.length - 5, 5);
1.3842 - var sibling = new BranchChunk(spilled);
1.3843 - if (!me.parent) { // Become the parent node
1.3844 - var copy = new BranchChunk(me.children);
1.3845 - copy.parent = me;
1.3846 - me.children = [copy, sibling];
1.3847 - me = copy;
1.3848 - } else {
1.3849 - me.size -= sibling.size;
1.3850 - me.height -= sibling.height;
1.3851 - var myIndex = indexOf(me.parent.children, me);
1.3852 - me.parent.children.splice(myIndex + 1, 0, sibling);
1.3853 - }
1.3854 - sibling.parent = me.parent;
1.3855 - } while (me.children.length > 10);
1.3856 - me.parent.maybeSpill();
1.3857 - },
1.3858 - iter: function(from, to, op) { this.iterN(from, to - from, op); },
1.3859 - iterN: function(at, n, op) {
1.3860 - for (var i = 0, e = this.children.length; i < e; ++i) {
1.3861 - var child = this.children[i], sz = child.chunkSize();
1.3862 - if (at < sz) {
1.3863 - var used = Math.min(n, sz - at);
1.3864 - if (child.iterN(at, used, op)) return true;
1.3865 - if ((n -= used) == 0) break;
1.3866 - at = 0;
1.3867 - } else at -= sz;
1.3868 - }
1.3869 - }
1.3870 - };
1.3871 -
1.3872 - // LINE UTILITIES
1.3873 -
1.3874 - function getLine(chunk, n) {
1.3875 - while (!chunk.lines) {
1.3876 - for (var i = 0;; ++i) {
1.3877 - var child = chunk.children[i], sz = child.chunkSize();
1.3878 - if (n < sz) { chunk = child; break; }
1.3879 - n -= sz;
1.3880 - }
1.3881 - }
1.3882 - return chunk.lines[n];
1.3883 - }
1.3884 -
1.3885 - function updateLineHeight(line, height) {
1.3886 - var diff = height - line.height;
1.3887 - for (var n = line; n; n = n.parent) n.height += diff;
1.3888 - }
1.3889 -
1.3890 - function lineNo(line) {
1.3891 - if (line.parent == null) return null;
1.3892 - var cur = line.parent, no = indexOf(cur.lines, line);
1.3893 - for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {
1.3894 - for (var i = 0;; ++i) {
1.3895 - if (chunk.children[i] == cur) break;
1.3896 - no += chunk.children[i].chunkSize();
1.3897 - }
1.3898 - }
1.3899 - return no;
1.3900 - }
1.3901 -
1.3902 - function lineAtHeight(chunk, h) {
1.3903 - var n = 0;
1.3904 - outer: do {
1.3905 - for (var i = 0, e = chunk.children.length; i < e; ++i) {
1.3906 - var child = chunk.children[i], ch = child.height;
1.3907 - if (h < ch) { chunk = child; continue outer; }
1.3908 - h -= ch;
1.3909 - n += child.chunkSize();
1.3910 - }
1.3911 - return n;
1.3912 - } while (!chunk.lines);
1.3913 - for (var i = 0, e = chunk.lines.length; i < e; ++i) {
1.3914 - var line = chunk.lines[i], lh = line.height;
1.3915 - if (h < lh) break;
1.3916 - h -= lh;
1.3917 - }
1.3918 - return n + i;
1.3919 - }
1.3920 -
1.3921 - function heightAtLine(cm, lineObj) {
1.3922 - lineObj = visualLine(cm.view.doc, lineObj);
1.3923 -
1.3924 - var h = 0, chunk = lineObj.parent;
1.3925 - for (var i = 0; i < chunk.lines.length; ++i) {
1.3926 - var line = chunk.lines[i];
1.3927 - if (line == lineObj) break;
1.3928 - else h += line.height;
1.3929 - }
1.3930 - for (var p = chunk.parent; p; chunk = p, p = chunk.parent) {
1.3931 - for (var i = 0; i < p.children.length; ++i) {
1.3932 - var cur = p.children[i];
1.3933 - if (cur == chunk) break;
1.3934 - else h += cur.height;
1.3935 - }
1.3936 - }
1.3937 - return h;
1.3938 - }
1.3939 -
1.3940 - function getOrder(line) {
1.3941 - var order = line.order;
1.3942 - if (order == null) order = line.order = bidiOrdering(line.text);
1.3943 - return order;
1.3944 - }
1.3945 -
1.3946 - // HISTORY
1.3947 -
1.3948 - function makeHistory() {
1.3949 - return {
1.3950 - // Arrays of history events. Doing something adds an event to
1.3951 - // done and clears undo. Undoing moves events from done to
1.3952 - // undone, redoing moves them in the other direction.
1.3953 - done: [], undone: [],
1.3954 - // Used to track when changes can be merged into a single undo
1.3955 - // event
1.3956 - lastTime: 0, lastOp: null, lastOrigin: null,
1.3957 - // Used by the isClean() method
1.3958 - dirtyCounter: 0
1.3959 - };
1.3960 - }
1.3961 -
1.3962 - function addChange(cm, start, added, old, origin, fromBefore, toBefore, fromAfter, toAfter) {
1.3963 - var history = cm.view.history;
1.3964 - history.undone.length = 0;
1.3965 - var time = +new Date, cur = lst(history.done);
1.3966 -
1.3967 - if (cur &&
1.3968 - (history.lastOp == cm.curOp.id ||
1.3969 - history.lastOrigin == origin && (origin == "input" || origin == "delete") &&
1.3970 - history.lastTime > time - 600)) {
1.3971 - // Merge this change into the last event
1.3972 - var last = lst(cur.events);
1.3973 - if (last.start > start + old.length || last.start + last.added < start) {
1.3974 - // Doesn't intersect with last sub-event, add new sub-event
1.3975 - cur.events.push({start: start, added: added, old: old});
1.3976 - } else {
1.3977 - // Patch up the last sub-event
1.3978 - var startBefore = Math.max(0, last.start - start),
1.3979 - endAfter = Math.max(0, (start + old.length) - (last.start + last.added));
1.3980 - for (var i = startBefore; i > 0; --i) last.old.unshift(old[i - 1]);
1.3981 - for (var i = endAfter; i > 0; --i) last.old.push(old[old.length - i]);
1.3982 - if (startBefore) last.start = start;
1.3983 - last.added += added - (old.length - startBefore - endAfter);
1.3984 - }
1.3985 - cur.fromAfter = fromAfter; cur.toAfter = toAfter;
1.3986 - } else {
1.3987 - // Can not be merged, start a new event.
1.3988 - cur = {events: [{start: start, added: added, old: old}],
1.3989 - fromBefore: fromBefore, toBefore: toBefore, fromAfter: fromAfter, toAfter: toAfter};
1.3990 - history.done.push(cur);
1.3991 - while (history.done.length > cm.options.undoDepth)
1.3992 - history.done.shift();
1.3993 - if (history.dirtyCounter < 0)
1.3994 - // The user has made a change after undoing past the last clean state.
1.3995 - // We can never get back to a clean state now until markClean() is called.
1.3996 - history.dirtyCounter = NaN;
1.3997 - else
1.3998 - history.dirtyCounter++;
1.3999 - }
1.4000 - history.lastTime = time;
1.4001 - history.lastOp = cm.curOp.id;
1.4002 - history.lastOrigin = origin;
1.4003 - }
1.4004 -
1.4005 - // EVENT OPERATORS
1.4006 -
1.4007 - function stopMethod() {e_stop(this);}
1.4008 - // Ensure an event has a stop method.
1.4009 - function addStop(event) {
1.4010 - if (!event.stop) event.stop = stopMethod;
1.4011 - return event;
1.4012 - }
1.4013 -
1.4014 - function e_preventDefault(e) {
1.4015 - if (e.preventDefault) e.preventDefault();
1.4016 - else e.returnValue = false;
1.4017 - }
1.4018 - function e_stopPropagation(e) {
1.4019 - if (e.stopPropagation) e.stopPropagation();
1.4020 - else e.cancelBubble = true;
1.4021 - }
1.4022 - function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);}
1.4023 - CodeMirror.e_stop = e_stop;
1.4024 - CodeMirror.e_preventDefault = e_preventDefault;
1.4025 - CodeMirror.e_stopPropagation = e_stopPropagation;
1.4026 -
1.4027 - function e_target(e) {return e.target || e.srcElement;}
1.4028 - function e_button(e) {
1.4029 - var b = e.which;
1.4030 - if (b == null) {
1.4031 - if (e.button & 1) b = 1;
1.4032 - else if (e.button & 2) b = 3;
1.4033 - else if (e.button & 4) b = 2;
1.4034 - }
1.4035 - if (mac && e.ctrlKey && b == 1) b = 3;
1.4036 - return b;
1.4037 - }
1.4038 -
1.4039 - // Allow 3rd-party code to override event properties by adding an override
1.4040 - // object to an event object.
1.4041 - function e_prop(e, prop) {
1.4042 - var overridden = e.override && e.override.hasOwnProperty(prop);
1.4043 - return overridden ? e.override[prop] : e[prop];
1.4044 - }
1.4045 -
1.4046 - // EVENT HANDLING
1.4047 -
1.4048 - function on(emitter, type, f) {
1.4049 - if (emitter.addEventListener)
1.4050 - emitter.addEventListener(type, f, false);
1.4051 - else if (emitter.attachEvent)
1.4052 - emitter.attachEvent("on" + type, f);
1.4053 - else {
1.4054 - var map = emitter._handlers || (emitter._handlers = {});
1.4055 - var arr = map[type] || (map[type] = []);
1.4056 - arr.push(f);
1.4057 - }
1.4058 - }
1.4059 -
1.4060 - function off(emitter, type, f) {
1.4061 - if (emitter.removeEventListener)
1.4062 - emitter.removeEventListener(type, f, false);
1.4063 - else if (emitter.detachEvent)
1.4064 - emitter.detachEvent("on" + type, f);
1.4065 - else {
1.4066 - var arr = emitter._handlers && emitter._handlers[type];
1.4067 - if (!arr) return;
1.4068 - for (var i = 0; i < arr.length; ++i)
1.4069 - if (arr[i] == f) { arr.splice(i, 1); break; }
1.4070 - }
1.4071 - }
1.4072 -
1.4073 - function signal(emitter, type /*, values...*/) {
1.4074 - var arr = emitter._handlers && emitter._handlers[type];
1.4075 - if (!arr) return;
1.4076 - var args = Array.prototype.slice.call(arguments, 2);
1.4077 - for (var i = 0; i < arr.length; ++i) arr[i].apply(null, args);
1.4078 - }
1.4079 -
1.4080 - function signalLater(cm, emitter, type /*, values...*/) {
1.4081 - var arr = emitter._handlers && emitter._handlers[type];
1.4082 - if (!arr) return;
1.4083 - var args = Array.prototype.slice.call(arguments, 3), flist = cm.curOp && cm.curOp.delayedCallbacks;
1.4084 - function bnd(f) {return function(){f.apply(null, args);};};
1.4085 - for (var i = 0; i < arr.length; ++i)
1.4086 - if (flist) flist.push(bnd(arr[i]));
1.4087 - else arr[i].apply(null, args);
1.4088 - }
1.4089 -
1.4090 - function hasHandler(emitter, type) {
1.4091 - var arr = emitter._handlers && emitter._handlers[type];
1.4092 - return arr && arr.length > 0;
1.4093 - }
1.4094 -
1.4095 - CodeMirror.on = on; CodeMirror.off = off; CodeMirror.signal = signal;
1.4096 -
1.4097 - // MISC UTILITIES
1.4098 -
1.4099 - // Number of pixels added to scroller and sizer to hide scrollbar
1.4100 - var scrollerCutOff = 30;
1.4101 -
1.4102 - // Returned or thrown by various protocols to signal 'I'm not
1.4103 - // handling this'.
1.4104 - var Pass = CodeMirror.Pass = {toString: function(){return "CodeMirror.Pass";}};
1.4105 -
1.4106 - function Delayed() {this.id = null;}
1.4107 - Delayed.prototype = {set: function(ms, f) {clearTimeout(this.id); this.id = setTimeout(f, ms);}};
1.4108 -
1.4109 - // Counts the column offset in a string, taking tabs into account.
1.4110 - // Used mostly to find indentation.
1.4111 - function countColumn(string, end, tabSize) {
1.4112 - if (end == null) {
1.4113 - end = string.search(/[^\s\u00a0]/);
1.4114 - if (end == -1) end = string.length;
1.4115 - }
1.4116 - for (var i = 0, n = 0; i < end; ++i) {
1.4117 - if (string.charAt(i) == "\t") n += tabSize - (n % tabSize);
1.4118 - else ++n;
1.4119 - }
1.4120 - return n;
1.4121 - }
1.4122 - CodeMirror.countColumn = countColumn;
1.4123 -
1.4124 - var spaceStrs = [""];
1.4125 - function spaceStr(n) {
1.4126 - while (spaceStrs.length <= n)
1.4127 - spaceStrs.push(lst(spaceStrs) + " ");
1.4128 - return spaceStrs[n];
1.4129 - }
1.4130 -
1.4131 - function lst(arr) { return arr[arr.length-1]; }
1.4132 -
1.4133 - function selectInput(node) {
1.4134 - if (ios) { // Mobile Safari apparently has a bug where select() is broken.
1.4135 - node.selectionStart = 0;
1.4136 - node.selectionEnd = node.value.length;
1.4137 - } else node.select();
1.4138 - }
1.4139 -
1.4140 - function indexOf(collection, elt) {
1.4141 - if (collection.indexOf) return collection.indexOf(elt);
1.4142 - for (var i = 0, e = collection.length; i < e; ++i)
1.4143 - if (collection[i] == elt) return i;
1.4144 - return -1;
1.4145 - }
1.4146 -
1.4147 - function emptyArray(size) {
1.4148 - for (var a = [], i = 0; i < size; ++i) a.push(undefined);
1.4149 - return a;
1.4150 - }
1.4151 -
1.4152 - function bind(f) {
1.4153 - var args = Array.prototype.slice.call(arguments, 1);
1.4154 - return function(){return f.apply(null, args);};
1.4155 - }
1.4156 -
1.4157 - var nonASCIISingleCaseWordChar = /[\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc]/;
1.4158 - function isWordChar(ch) {
1.4159 - return /\w/.test(ch) || ch > "\x80" &&
1.4160 - (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch));
1.4161 - }
1.4162 -
1.4163 - function isEmpty(obj) {
1.4164 - var c = 0;
1.4165 - for (var n in obj) if (obj.hasOwnProperty(n) && obj[n]) ++c;
1.4166 - return !c;
1.4167 - }
1.4168 -
1.4169 - var isExtendingChar = /[\u0300-\u036F\u0483-\u0487\u0488-\u0489\u0591-\u05BD\u05BF\u05C1-\u05C2\u05C4-\u05C5\u05C7\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7-\u06E8\u06EA-\u06ED\uA66F\uA670-\uA672\uA674-\uA67D\uA69F]/;
1.4170 -
1.4171 - // DOM UTILITIES
1.4172 -
1.4173 - function elt(tag, content, className, style) {
1.4174 - var e = document.createElement(tag);
1.4175 - if (className) e.className = className;
1.4176 - if (style) e.style.cssText = style;
1.4177 - if (typeof content == "string") setTextContent(e, content);
1.4178 - else if (content) for (var i = 0; i < content.length; ++i) e.appendChild(content[i]);
1.4179 - return e;
1.4180 - }
1.4181 -
1.4182 - function removeChildren(e) {
1.4183 - e.innerHTML = "";
1.4184 - return e;
1.4185 - }
1.4186 -
1.4187 - function removeChildrenAndAdd(parent, e) {
1.4188 - return removeChildren(parent).appendChild(e);
1.4189 - }
1.4190 -
1.4191 - function setTextContent(e, str) {
1.4192 - if (ie_lt9) {
1.4193 - e.innerHTML = "";
1.4194 - e.appendChild(document.createTextNode(str));
1.4195 - } else e.textContent = str;
1.4196 - }
1.4197 -
1.4198 - // FEATURE DETECTION
1.4199 -
1.4200 - // Detect drag-and-drop
1.4201 - var dragAndDrop = function() {
1.4202 - // There is *some* kind of drag-and-drop support in IE6-8, but I
1.4203 - // couldn't get it to work yet.
1.4204 - if (ie_lt9) return false;
1.4205 - var div = elt('div');
1.4206 - return "draggable" in div || "dragDrop" in div;
1.4207 - }();
1.4208 -
1.4209 - // For a reason I have yet to figure out, some browsers disallow
1.4210 - // word wrapping between certain characters *only* if a new inline
1.4211 - // element is started between them. This makes it hard to reliably
1.4212 - // measure the position of things, since that requires inserting an
1.4213 - // extra span. This terribly fragile set of regexps matches the
1.4214 - // character combinations that suffer from this phenomenon on the
1.4215 - // various browsers.
1.4216 - var spanAffectsWrapping = /^$/; // Won't match any two-character string
1.4217 - if (gecko) spanAffectsWrapping = /$'/;
1.4218 - else if (safari) spanAffectsWrapping = /\-[^ \-?]|\?[^ !'\"\),.\-\/:;\?\]\}]/;
1.4219 - else if (chrome) spanAffectsWrapping = /\-[^ \-\.?]|\?[^ \-\.?\]\}:;!'\"\),\/]|[\.!\"#&%\)*+,:;=>\]|\}~][\(\{\[<]|\$'/;
1.4220 -
1.4221 - var knownScrollbarWidth;
1.4222 - function scrollbarWidth(measure) {
1.4223 - if (knownScrollbarWidth != null) return knownScrollbarWidth;
1.4224 - var test = elt("div", null, null, "width: 50px; height: 50px; overflow-x: scroll");
1.4225 - removeChildrenAndAdd(measure, test);
1.4226 - if (test.offsetWidth)
1.4227 - knownScrollbarWidth = test.offsetHeight - test.clientHeight;
1.4228 - return knownScrollbarWidth || 0;
1.4229 - }
1.4230 -
1.4231 - var zwspSupported;
1.4232 - function zeroWidthElement(measure) {
1.4233 - if (zwspSupported == null) {
1.4234 - var test = elt("span", "\u200b");
1.4235 - removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")]));
1.4236 - if (measure.firstChild.offsetHeight != 0)
1.4237 - zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !ie_lt8;
1.4238 - }
1.4239 - if (zwspSupported) return elt("span", "\u200b");
1.4240 - else return elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px");
1.4241 - }
1.4242 -
1.4243 - // See if "".split is the broken IE version, if so, provide an
1.4244 - // alternative way to split lines.
1.4245 - var splitLines = "\n\nb".split(/\n/).length != 3 ? function(string) {
1.4246 - var pos = 0, result = [], l = string.length;
1.4247 - while (pos <= l) {
1.4248 - var nl = string.indexOf("\n", pos);
1.4249 - if (nl == -1) nl = string.length;
1.4250 - var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl);
1.4251 - var rt = line.indexOf("\r");
1.4252 - if (rt != -1) {
1.4253 - result.push(line.slice(0, rt));
1.4254 - pos += rt + 1;
1.4255 - } else {
1.4256 - result.push(line);
1.4257 - pos = nl + 1;
1.4258 - }
1.4259 - }
1.4260 - return result;
1.4261 - } : function(string){return string.split(/\r\n?|\n/);};
1.4262 - CodeMirror.splitLines = splitLines;
1.4263 -
1.4264 - var hasSelection = window.getSelection ? function(te) {
1.4265 - try { return te.selectionStart != te.selectionEnd; }
1.4266 - catch(e) { return false; }
1.4267 - } : function(te) {
1.4268 - try {var range = te.ownerDocument.selection.createRange();}
1.4269 - catch(e) {}
1.4270 - if (!range || range.parentElement() != te) return false;
1.4271 - return range.compareEndPoints("StartToEnd", range) != 0;
1.4272 - };
1.4273 -
1.4274 - var hasCopyEvent = (function() {
1.4275 - var e = elt("div");
1.4276 - if ("oncopy" in e) return true;
1.4277 - e.setAttribute("oncopy", "return;");
1.4278 - return typeof e.oncopy == 'function';
1.4279 - })();
1.4280 -
1.4281 - // KEY NAMING
1.4282 -
1.4283 - var keyNames = {3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
1.4284 - 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
1.4285 - 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
1.4286 - 46: "Delete", 59: ";", 91: "Mod", 92: "Mod", 93: "Mod", 109: "-", 107: "=", 127: "Delete",
1.4287 - 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\",
1.4288 - 221: "]", 222: "'", 63276: "PageUp", 63277: "PageDown", 63275: "End", 63273: "Home",
1.4289 - 63234: "Left", 63232: "Up", 63235: "Right", 63233: "Down", 63302: "Insert", 63272: "Delete"};
1.4290 - CodeMirror.keyNames = keyNames;
1.4291 - (function() {
1.4292 - // Number keys
1.4293 - for (var i = 0; i < 10; i++) keyNames[i + 48] = String(i);
1.4294 - // Alphabetic keys
1.4295 - for (var i = 65; i <= 90; i++) keyNames[i] = String.fromCharCode(i);
1.4296 - // Function keys
1.4297 - for (var i = 1; i <= 12; i++) keyNames[i + 111] = keyNames[i + 63235] = "F" + i;
1.4298 - })();
1.4299 -
1.4300 - // BIDI HELPERS
1.4301 -
1.4302 - function iterateBidiSections(order, from, to, f) {
1.4303 - if (!order) return f(from, to, "ltr");
1.4304 - for (var i = 0; i < order.length; ++i) {
1.4305 - var part = order[i];
1.4306 - if (part.from < to && part.to > from)
1.4307 - f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr");
1.4308 - }
1.4309 - }
1.4310 -
1.4311 - function bidiLeft(part) { return part.level % 2 ? part.to : part.from; }
1.4312 - function bidiRight(part) { return part.level % 2 ? part.from : part.to; }
1.4313 -
1.4314 - function lineLeft(line) { var order = getOrder(line); return order ? bidiLeft(order[0]) : 0; }
1.4315 - function lineRight(line) {
1.4316 - var order = getOrder(line);
1.4317 - if (!order) return line.text.length;
1.4318 - return bidiRight(lst(order));
1.4319 - }
1.4320 -
1.4321 - function lineStart(cm, lineN) {
1.4322 - var line = getLine(cm.view.doc, lineN);
1.4323 - var visual = visualLine(cm.view.doc, line);
1.4324 - if (visual != line) lineN = lineNo(visual);
1.4325 - var order = getOrder(visual);
1.4326 - var ch = !order ? 0 : order[0].level % 2 ? lineRight(visual) : lineLeft(visual);
1.4327 - return {line: lineN, ch: ch};
1.4328 - }
1.4329 - function lineEnd(cm, lineNo) {
1.4330 - var merged, line;
1.4331 - while (merged = collapsedSpanAtEnd(line = getLine(cm.view.doc, lineNo)))
1.4332 - lineNo = merged.find().to.line;
1.4333 - var order = getOrder(line);
1.4334 - var ch = !order ? line.text.length : order[0].level % 2 ? lineLeft(line) : lineRight(line);
1.4335 - return {line: lineNo, ch: ch};
1.4336 - }
1.4337 -
1.4338 - // This is somewhat involved. It is needed in order to move
1.4339 - // 'visually' through bi-directional text -- i.e., pressing left
1.4340 - // should make the cursor go left, even when in RTL text. The
1.4341 - // tricky part is the 'jumps', where RTL and LTR text touch each
1.4342 - // other. This often requires the cursor offset to move more than
1.4343 - // one unit, in order to visually move one unit.
1.4344 - function moveVisually(line, start, dir, byUnit) {
1.4345 - var bidi = getOrder(line);
1.4346 - if (!bidi) return moveLogically(line, start, dir, byUnit);
1.4347 - var moveOneUnit = byUnit ? function(pos, dir) {
1.4348 - do pos += dir;
1.4349 - while (pos > 0 && isExtendingChar.test(line.text.charAt(pos)));
1.4350 - return pos;
1.4351 - } : function(pos, dir) { return pos + dir; };
1.4352 - var linedir = bidi[0].level;
1.4353 - for (var i = 0; i < bidi.length; ++i) {
1.4354 - var part = bidi[i], sticky = part.level % 2 == linedir;
1.4355 - if ((part.from < start && part.to > start) ||
1.4356 - (sticky && (part.from == start || part.to == start))) break;
1.4357 - }
1.4358 - var target = moveOneUnit(start, part.level % 2 ? -dir : dir);
1.4359 -
1.4360 - while (target != null) {
1.4361 - if (part.level % 2 == linedir) {
1.4362 - if (target < part.from || target > part.to) {
1.4363 - part = bidi[i += dir];
1.4364 - target = part && (dir > 0 == part.level % 2 ? moveOneUnit(part.to, -1) : moveOneUnit(part.from, 1));
1.4365 - } else break;
1.4366 - } else {
1.4367 - if (target == bidiLeft(part)) {
1.4368 - part = bidi[--i];
1.4369 - target = part && bidiRight(part);
1.4370 - } else if (target == bidiRight(part)) {
1.4371 - part = bidi[++i];
1.4372 - target = part && bidiLeft(part);
1.4373 - } else break;
1.4374 - }
1.4375 - }
1.4376 -
1.4377 - return target < 0 || target > line.text.length ? null : target;
1.4378 - }
1.4379 -
1.4380 - function moveLogically(line, start, dir, byUnit) {
1.4381 - var target = start + dir;
1.4382 - if (byUnit) while (target > 0 && isExtendingChar.test(line.text.charAt(target))) target += dir;
1.4383 - return target < 0 || target > line.text.length ? null : target;
1.4384 - }
1.4385 -
1.4386 - // Bidirectional ordering algorithm
1.4387 - // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm
1.4388 - // that this (partially) implements.
1.4389 -
1.4390 - // One-char codes used for character types:
1.4391 - // L (L): Left-to-Right
1.4392 - // R (R): Right-to-Left
1.4393 - // r (AL): Right-to-Left Arabic
1.4394 - // 1 (EN): European Number
1.4395 - // + (ES): European Number Separator
1.4396 - // % (ET): European Number Terminator
1.4397 - // n (AN): Arabic Number
1.4398 - // , (CS): Common Number Separator
1.4399 - // m (NSM): Non-Spacing Mark
1.4400 - // b (BN): Boundary Neutral
1.4401 - // s (B): Paragraph Separator
1.4402 - // t (S): Segment Separator
1.4403 - // w (WS): Whitespace
1.4404 - // N (ON): Other Neutrals
1.4405 -
1.4406 - // Returns null if characters are ordered as they appear
1.4407 - // (left-to-right), or an array of sections ({from, to, level}
1.4408 - // objects) in the order in which they occur visually.
1.4409 - var bidiOrdering = (function() {
1.4410 - // Character types for codepoints 0 to 0xff
1.4411 - var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLL";
1.4412 - // Character types for codepoints 0x600 to 0x6ff
1.4413 - var arabicTypes = "rrrrrrrrrrrr,rNNmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmrrrrrrrnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmNmmmmrrrrrrrrrrrrrrrrrr";
1.4414 - function charType(code) {
1.4415 - if (code <= 0xff) return lowTypes.charAt(code);
1.4416 - else if (0x590 <= code && code <= 0x5f4) return "R";
1.4417 - else if (0x600 <= code && code <= 0x6ff) return arabicTypes.charAt(code - 0x600);
1.4418 - else if (0x700 <= code && code <= 0x8ac) return "r";
1.4419 - else return "L";
1.4420 - }
1.4421 -
1.4422 - var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;
1.4423 - var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/;
1.4424 -
1.4425 - return function charOrdering(str) {
1.4426 - if (!bidiRE.test(str)) return false;
1.4427 - var len = str.length, types = [], startType = null;
1.4428 - for (var i = 0, type; i < len; ++i) {
1.4429 - types.push(type = charType(str.charCodeAt(i)));
1.4430 - if (startType == null) {
1.4431 - if (type == "L") startType = "L";
1.4432 - else if (type == "R" || type == "r") startType = "R";
1.4433 - }
1.4434 - }
1.4435 - if (startType == null) startType = "L";
1.4436 -
1.4437 - // W1. Examine each non-spacing mark (NSM) in the level run, and
1.4438 - // change the type of the NSM to the type of the previous
1.4439 - // character. If the NSM is at the start of the level run, it will
1.4440 - // get the type of sor.
1.4441 - for (var i = 0, prev = startType; i < len; ++i) {
1.4442 - var type = types[i];
1.4443 - if (type == "m") types[i] = prev;
1.4444 - else prev = type;
1.4445 - }
1.4446 -
1.4447 - // W2. Search backwards from each instance of a European number
1.4448 - // until the first strong type (R, L, AL, or sor) is found. If an
1.4449 - // AL is found, change the type of the European number to Arabic
1.4450 - // number.
1.4451 - // W3. Change all ALs to R.
1.4452 - for (var i = 0, cur = startType; i < len; ++i) {
1.4453 - var type = types[i];
1.4454 - if (type == "1" && cur == "r") types[i] = "n";
1.4455 - else if (isStrong.test(type)) { cur = type; if (type == "r") types[i] = "R"; }
1.4456 - }
1.4457 -
1.4458 - // W4. A single European separator between two European numbers
1.4459 - // changes to a European number. A single common separator between
1.4460 - // two numbers of the same type changes to that type.
1.4461 - for (var i = 1, prev = types[0]; i < len - 1; ++i) {
1.4462 - var type = types[i];
1.4463 - if (type == "+" && prev == "1" && types[i+1] == "1") types[i] = "1";
1.4464 - else if (type == "," && prev == types[i+1] &&
1.4465 - (prev == "1" || prev == "n")) types[i] = prev;
1.4466 - prev = type;
1.4467 - }
1.4468 -
1.4469 - // W5. A sequence of European terminators adjacent to European
1.4470 - // numbers changes to all European numbers.
1.4471 - // W6. Otherwise, separators and terminators change to Other
1.4472 - // Neutral.
1.4473 - for (var i = 0; i < len; ++i) {
1.4474 - var type = types[i];
1.4475 - if (type == ",") types[i] = "N";
1.4476 - else if (type == "%") {
1.4477 - for (var end = i + 1; end < len && types[end] == "%"; ++end) {}
1.4478 - var replace = (i && types[i-1] == "!") || (end < len - 1 && types[end] == "1") ? "1" : "N";
1.4479 - for (var j = i; j < end; ++j) types[j] = replace;
1.4480 - i = end - 1;
1.4481 - }
1.4482 - }
1.4483 -
1.4484 - // W7. Search backwards from each instance of a European number
1.4485 - // until the first strong type (R, L, or sor) is found. If an L is
1.4486 - // found, then change the type of the European number to L.
1.4487 - for (var i = 0, cur = startType; i < len; ++i) {
1.4488 - var type = types[i];
1.4489 - if (cur == "L" && type == "1") types[i] = "L";
1.4490 - else if (isStrong.test(type)) cur = type;
1.4491 - }
1.4492 -
1.4493 - // N1. A sequence of neutrals takes the direction of the
1.4494 - // surrounding strong text if the text on both sides has the same
1.4495 - // direction. European and Arabic numbers act as if they were R in
1.4496 - // terms of their influence on neutrals. Start-of-level-run (sor)
1.4497 - // and end-of-level-run (eor) are used at level run boundaries.
1.4498 - // N2. Any remaining neutrals take the embedding direction.
1.4499 - for (var i = 0; i < len; ++i) {
1.4500 - if (isNeutral.test(types[i])) {
1.4501 - for (var end = i + 1; end < len && isNeutral.test(types[end]); ++end) {}
1.4502 - var before = (i ? types[i-1] : startType) == "L";
1.4503 - var after = (end < len - 1 ? types[end] : startType) == "L";
1.4504 - var replace = before || after ? "L" : "R";
1.4505 - for (var j = i; j < end; ++j) types[j] = replace;
1.4506 - i = end - 1;
1.4507 - }
1.4508 - }
1.4509 -
1.4510 - // Here we depart from the documented algorithm, in order to avoid
1.4511 - // building up an actual levels array. Since there are only three
1.4512 - // levels (0, 1, 2) in an implementation that doesn't take
1.4513 - // explicit embedding into account, we can build up the order on
1.4514 - // the fly, without following the level-based algorithm.
1.4515 - var order = [], m;
1.4516 - for (var i = 0; i < len;) {
1.4517 - if (countsAsLeft.test(types[i])) {
1.4518 - var start = i;
1.4519 - for (++i; i < len && countsAsLeft.test(types[i]); ++i) {}
1.4520 - order.push({from: start, to: i, level: 0});
1.4521 - } else {
1.4522 - var pos = i, at = order.length;
1.4523 - for (++i; i < len && types[i] != "L"; ++i) {}
1.4524 - for (var j = pos; j < i;) {
1.4525 - if (countsAsNum.test(types[j])) {
1.4526 - if (pos < j) order.splice(at, 0, {from: pos, to: j, level: 1});
1.4527 - var nstart = j;
1.4528 - for (++j; j < i && countsAsNum.test(types[j]); ++j) {}
1.4529 - order.splice(at, 0, {from: nstart, to: j, level: 2});
1.4530 - pos = j;
1.4531 - } else ++j;
1.4532 - }
1.4533 - if (pos < i) order.splice(at, 0, {from: pos, to: i, level: 1});
1.4534 - }
1.4535 - }
1.4536 - if (order[0].level == 1 && (m = str.match(/^\s+/))) {
1.4537 - order[0].from = m[0].length;
1.4538 - order.unshift({from: 0, to: m[0].length, level: 0});
1.4539 - }
1.4540 - if (lst(order).level == 1 && (m = str.match(/\s+$/))) {
1.4541 - lst(order).to -= m[0].length;
1.4542 - order.push({from: len - m[0].length, to: len, level: 0});
1.4543 - }
1.4544 - if (order[0].level != lst(order).level)
1.4545 - order.push({from: len, to: len, level: order[0].level});
1.4546 -
1.4547 - return order;
1.4548 - };
1.4549 - })();
1.4550 -
1.4551 - // THE END
1.4552 -
1.4553 - CodeMirror.version = "3.0";
1.4554 -
1.4555 - return CodeMirror;
1.4556 -})();