~ubuntu-branches/ubuntu/trusty/qiime/trusty

« back to all changes in this revision

Viewing changes to web/home_static/nih-cloud-apr2012/NIHCloudDemo_Fast_files/codemirror.js

  • Committer: Package Import Robot
  • Author(s): Andreas Tille
  • Date: 2013-06-17 18:28:26 UTC
  • mfrom: (9.1.2 sid)
  • Revision ID: package-import@ubuntu.com-20130617182826-376az5ad080a0sfe
Tags: 1.7.0+dfsg-1
Upload preparations done for BioLinux to Debian

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
// All functions that need access to the editor's state live inside
2
 
// the CodeMirror function. Below that, at the bottom of the file,
3
 
// some utilities are defined.
4
 
 
5
 
// CodeMirror is the only global var we claim
6
 
var CodeMirror = (function() {
7
 
  // This is the function that produces an editor instance. It's
8
 
  // closure is used to store the editor state.
9
 
  function CodeMirror(place, givenOptions) {
10
 
    // Determine effective options based on given values and defaults.
11
 
    var options = {}, defaults = CodeMirror.defaults;
12
 
    for (var opt in defaults)
13
 
      if (defaults.hasOwnProperty(opt))
14
 
        options[opt] = (givenOptions && givenOptions.hasOwnProperty(opt) ? givenOptions : defaults)[opt];
15
 
 
16
 
    var targetDocument = options["document"];
17
 
    // The element in which the editor lives.
18
 
    var wrapper = targetDocument.createElement("div");
19
 
    wrapper.className = "CodeMirror" + (options.lineWrapping ? " CodeMirror-wrap" : "");
20
 
    // This mess creates the base DOM structure for the editor.
21
 
    wrapper.innerHTML =
22
 
      '<div style="overflow: hidden; position: relative; width: 3px; height: 0px;">' + // Wraps and hides input textarea
23
 
        '<textarea style="position: absolute; padding: 0; width: 1px; height: 1em" wrap="off" ' +
24
 
          'autocorrect="off" autocapitalize="off"></textarea></div>' +
25
 
      '<div class="CodeMirror-scroll" tabindex="-1">' +
26
 
        '<div style="position: relative">' + // Set to the height of the text, causes scrolling
27
 
          '<div style="position: relative">' + // Moved around its parent to cover visible view
28
 
            '<div class="CodeMirror-gutter"><div class="CodeMirror-gutter-text"></div></div>' +
29
 
            // Provides positioning relative to (visible) text origin
30
 
            '<div class="CodeMirror-lines"><div style="position: relative; z-index: 0">' +
31
 
              '<div style="position: absolute; width: 100%; height: 0; overflow: hidden; visibility: hidden; outline: 5px auto none"></div>' +
32
 
              '<pre class="CodeMirror-cursor">&#160;</pre>' + // Absolutely positioned blinky cursor
33
 
              '<div style="position: relative; z-index: -1"></div><div></div>' + // DIVs containing the selection and the actual code
34
 
            '</div></div></div></div></div>';
35
 
    if (place.appendChild) place.appendChild(wrapper); else place(wrapper);
36
 
    // I've never seen more elegant code in my life.
37
 
    var inputDiv = wrapper.firstChild, input = inputDiv.firstChild,
38
 
        scroller = wrapper.lastChild, code = scroller.firstChild,
39
 
        mover = code.firstChild, gutter = mover.firstChild, gutterText = gutter.firstChild,
40
 
        lineSpace = gutter.nextSibling.firstChild, measure = lineSpace.firstChild,
41
 
        cursor = measure.nextSibling, selectionDiv = cursor.nextSibling,
42
 
        lineDiv = selectionDiv.nextSibling;
43
 
    themeChanged();
44
 
    // Needed to hide big blue blinking cursor on Mobile Safari
45
 
    if (ios) input.style.width = "0px";
46
 
    if (!webkit) lineSpace.draggable = true;
47
 
    lineSpace.style.outline = "none";
48
 
    if (options.tabindex != null) input.tabIndex = options.tabindex;
49
 
    if (!options.gutter && !options.lineNumbers) gutter.style.display = "none";
50
 
 
51
 
    // Check for problem with IE innerHTML not working when we have a
52
 
    // P (or similar) parent node.
53
 
    try { stringWidth("x"); }
54
 
    catch (e) {
55
 
      if (e.message.match(/runtime/i))
56
 
        e = new Error("A CodeMirror inside a P-style element does not work in Internet Explorer. (innerHTML bug)");
57
 
      throw e;
58
 
    }
59
 
 
60
 
    // Delayed object wrap timeouts, making sure only one is active. blinker holds an interval.
61
 
    var poll = new Delayed(), highlight = new Delayed(), blinker;
62
 
 
63
 
    // mode holds a mode API object. doc is the tree of Line objects,
64
 
    // work an array of lines that should be parsed, and history the
65
 
    // undo history (instance of History constructor).
66
 
    var mode, doc = new BranchChunk([new LeafChunk([new Line("")])]), work, focused;
67
 
    loadMode();
68
 
    // The selection. These are always maintained to point at valid
69
 
    // positions. Inverted is used to remember that the user is
70
 
    // selecting bottom-to-top.
71
 
    var sel = {from: {line: 0, ch: 0}, to: {line: 0, ch: 0}, inverted: false};
72
 
    // Selection-related flags. shiftSelecting obviously tracks
73
 
    // whether the user is holding shift.
74
 
    var shiftSelecting, lastClick, lastDoubleClick, lastScrollPos = 0, draggingText,
75
 
        overwrite = false, suppressEdits = false;
76
 
    // Variables used by startOperation/endOperation to track what
77
 
    // happened during the operation.
78
 
    var updateInput, userSelChange, changes, textChanged, selectionChanged, leaveInputAlone,
79
 
        gutterDirty, callbacks;
80
 
    // Current visible range (may be bigger than the view window).
81
 
    var displayOffset = 0, showingFrom = 0, showingTo = 0, lastSizeC = 0;
82
 
    // bracketHighlighted is used to remember that a backet has been
83
 
    // marked.
84
 
    var bracketHighlighted;
85
 
    // Tracks the maximum line length so that the horizontal scrollbar
86
 
    // can be kept static when scrolling.
87
 
    var maxLine = "", maxWidth, tabText = computeTabText();
88
 
 
89
 
    // Initialize the content.
90
 
    operation(function(){setValue(options.value || ""); updateInput = false;})();
91
 
    var history = new History();
92
 
 
93
 
    // Register our event handlers.
94
 
    connect(scroller, "mousedown", operation(onMouseDown));
95
 
    connect(scroller, "dblclick", operation(onDoubleClick));
96
 
    connect(lineSpace, "dragstart", onDragStart);
97
 
    connect(lineSpace, "selectstart", e_preventDefault);
98
 
    // Gecko browsers fire contextmenu *after* opening the menu, at
99
 
    // which point we can't mess with it anymore. Context menu is
100
 
    // handled in onMouseDown for Gecko.
101
 
    if (!gecko) connect(scroller, "contextmenu", onContextMenu);
102
 
    connect(scroller, "scroll", function() {
103
 
      lastScrollPos = scroller.scrollTop;
104
 
      updateDisplay([]);
105
 
      if (options.fixedGutter) gutter.style.left = scroller.scrollLeft + "px";
106
 
      if (options.onScroll) options.onScroll(instance);
107
 
    });
108
 
    connect(window, "resize", function() {updateDisplay(true);});
109
 
    connect(input, "keyup", operation(onKeyUp));
110
 
    connect(input, "input", fastPoll);
111
 
    connect(input, "keydown", operation(onKeyDown));
112
 
    connect(input, "keypress", operation(onKeyPress));
113
 
    connect(input, "focus", onFocus);
114
 
    connect(input, "blur", onBlur);
115
 
 
116
 
    connect(scroller, "dragenter", e_stop);
117
 
    connect(scroller, "dragover", e_stop);
118
 
    connect(scroller, "drop", operation(onDrop));
119
 
    connect(scroller, "paste", function(){focusInput(); fastPoll();});
120
 
    connect(input, "paste", fastPoll);
121
 
    connect(input, "cut", operation(function(){
122
 
      if (!options.readOnly) replaceSelection("");
123
 
    }));
124
 
 
125
 
    // IE throws unspecified error in certain cases, when
126
 
    // trying to access activeElement before onload
127
 
    var hasFocus; try { hasFocus = (targetDocument.activeElement == input); } catch(e) { }
128
 
    if (hasFocus) setTimeout(onFocus, 20);
129
 
    else onBlur();
130
 
 
131
 
    function isLine(l) {return l >= 0 && l < doc.size;}
132
 
    // The instance object that we'll return. Mostly calls out to
133
 
    // local functions in the CodeMirror function. Some do some extra
134
 
    // range checking and/or clipping. operation is used to wrap the
135
 
    // call so that changes it makes are tracked, and the display is
136
 
    // updated afterwards.
137
 
    var instance = wrapper.CodeMirror = {
138
 
      getValue: getValue,
139
 
      setValue: operation(setValue),
140
 
      getSelection: getSelection,
141
 
      replaceSelection: operation(replaceSelection),
142
 
      focus: function(){focusInput(); onFocus(); fastPoll();},
143
 
      setOption: function(option, value) {
144
 
        var oldVal = options[option];
145
 
        options[option] = value;
146
 
        if (option == "mode" || option == "indentUnit") loadMode();
147
 
        else if (option == "readOnly" && value == "nocursor") {onBlur(); input.blur();}
148
 
        else if (option == "readOnly" && !value) {resetInput(true);}
149
 
        else if (option == "theme") themeChanged();
150
 
        else if (option == "lineWrapping" && oldVal != value) operation(wrappingChanged)();
151
 
        else if (option == "tabSize") operation(tabsChanged)();
152
 
        if (option == "lineNumbers" || option == "gutter" || option == "firstLineNumber" || option == "theme") {
153
 
          gutterChanged();
154
 
          updateDisplay(true);
155
 
        }
156
 
      },
157
 
      getOption: function(option) {return options[option];},
158
 
      undo: operation(undo),
159
 
      redo: operation(redo),
160
 
      indentLine: operation(function(n, dir) {
161
 
        if (typeof dir != "string") {
162
 
          if (dir == null) dir = options.smartIndent ? "smart" : "prev";
163
 
          else dir = dir ? "add" : "subtract";
164
 
        }
165
 
        if (isLine(n)) indentLine(n, dir);
166
 
      }),
167
 
      indentSelection: operation(indentSelected),
168
 
      historySize: function() {return {undo: history.done.length, redo: history.undone.length};},
169
 
      clearHistory: function() {history = new History();},
170
 
      matchBrackets: operation(function(){matchBrackets(true);}),
171
 
      getTokenAt: operation(function(pos) {
172
 
        pos = clipPos(pos);
173
 
        return getLine(pos.line).getTokenAt(mode, getStateBefore(pos.line), pos.ch);
174
 
      }),
175
 
      getStateAfter: function(line) {
176
 
        line = clipLine(line == null ? doc.size - 1: line);
177
 
        return getStateBefore(line + 1);
178
 
      },
179
 
      cursorCoords: function(start){
180
 
        if (start == null) start = sel.inverted;
181
 
        return pageCoords(start ? sel.from : sel.to);
182
 
      },
183
 
      charCoords: function(pos){return pageCoords(clipPos(pos));},
184
 
      coordsChar: function(coords) {
185
 
        var off = eltOffset(lineSpace);
186
 
        return coordsChar(coords.x - off.left, coords.y - off.top);
187
 
      },
188
 
      markText: operation(markText),
189
 
      setBookmark: setBookmark,
190
 
      setMarker: operation(addGutterMarker),
191
 
      clearMarker: operation(removeGutterMarker),
192
 
      setLineClass: operation(setLineClass),
193
 
      hideLine: operation(function(h) {return setLineHidden(h, true);}),
194
 
      showLine: operation(function(h) {return setLineHidden(h, false);}),
195
 
      onDeleteLine: function(line, f) {
196
 
        if (typeof line == "number") {
197
 
          if (!isLine(line)) return null;
198
 
          line = getLine(line);
199
 
        }
200
 
        (line.handlers || (line.handlers = [])).push(f);
201
 
        return line;
202
 
      },
203
 
      lineInfo: lineInfo,
204
 
      addWidget: function(pos, node, scroll, vert, horiz) {
205
 
        pos = localCoords(clipPos(pos));
206
 
        var top = pos.yBot, left = pos.x;
207
 
        node.style.position = "absolute";
208
 
        code.appendChild(node);
209
 
        if (vert == "over") top = pos.y;
210
 
        else if (vert == "near") {
211
 
          var vspace = Math.max(scroller.offsetHeight, doc.height * textHeight()),
212
 
              hspace = Math.max(code.clientWidth, lineSpace.clientWidth) - paddingLeft();
213
 
          if (pos.yBot + node.offsetHeight > vspace && pos.y > node.offsetHeight)
214
 
            top = pos.y - node.offsetHeight;
215
 
          if (left + node.offsetWidth > hspace)
216
 
            left = hspace - node.offsetWidth;
217
 
        }
218
 
        node.style.top = (top + paddingTop()) + "px";
219
 
        node.style.left = node.style.right = "";
220
 
        if (horiz == "right") {
221
 
          left = code.clientWidth - node.offsetWidth;
222
 
          node.style.right = "0px";
223
 
        } else {
224
 
          if (horiz == "left") left = 0;
225
 
          else if (horiz == "middle") left = (code.clientWidth - node.offsetWidth) / 2;
226
 
          node.style.left = (left + paddingLeft()) + "px";
227
 
        }
228
 
        if (scroll)
229
 
          scrollIntoView(left, top, left + node.offsetWidth, top + node.offsetHeight);
230
 
      },
231
 
 
232
 
      lineCount: function() {return doc.size;},
233
 
      clipPos: clipPos,
234
 
      getCursor: function(start) {
235
 
        if (start == null) start = sel.inverted;
236
 
        return copyPos(start ? sel.from : sel.to);
237
 
      },
238
 
      somethingSelected: function() {return !posEq(sel.from, sel.to);},
239
 
      setCursor: operation(function(line, ch, user) {
240
 
        if (ch == null && typeof line.line == "number") setCursor(line.line, line.ch, user);
241
 
        else setCursor(line, ch, user);
242
 
      }),
243
 
      setSelection: operation(function(from, to, user) {
244
 
        (user ? setSelectionUser : setSelection)(clipPos(from), clipPos(to || from));
245
 
      }),
246
 
      getLine: function(line) {if (isLine(line)) return getLine(line).text;},
247
 
      getLineHandle: function(line) {if (isLine(line)) return getLine(line);},
248
 
      setLine: operation(function(line, text) {
249
 
        if (isLine(line)) replaceRange(text, {line: line, ch: 0}, {line: line, ch: getLine(line).text.length});
250
 
      }),
251
 
      removeLine: operation(function(line) {
252
 
        if (isLine(line)) replaceRange("", {line: line, ch: 0}, clipPos({line: line+1, ch: 0}));
253
 
      }),
254
 
      replaceRange: operation(replaceRange),
255
 
      getRange: function(from, to) {return getRange(clipPos(from), clipPos(to));},
256
 
 
257
 
      execCommand: function(cmd) {return commands[cmd](instance);},
258
 
      // Stuff used by commands, probably not much use to outside code.
259
 
      moveH: operation(moveH),
260
 
      deleteH: operation(deleteH),
261
 
      moveV: operation(moveV),
262
 
      toggleOverwrite: function() {overwrite = !overwrite;},
263
 
 
264
 
      posFromIndex: function(off) {
265
 
        var lineNo = 0, ch;
266
 
        doc.iter(0, doc.size, function(line) {
267
 
          var sz = line.text.length + 1;
268
 
          if (sz > off) { ch = off; return true; }
269
 
          off -= sz;
270
 
          ++lineNo;
271
 
        });
272
 
        return clipPos({line: lineNo, ch: ch});
273
 
      },
274
 
      indexFromPos: function (coords) {
275
 
        if (coords.line < 0 || coords.ch < 0) return 0;
276
 
        var index = coords.ch;
277
 
        doc.iter(0, coords.line, function (line) {
278
 
          index += line.text.length + 1;
279
 
        });
280
 
        return index;
281
 
      },
282
 
      scrollTo: function(x, y) {
283
 
        if (x != null) scroller.scrollLeft = x;
284
 
        if (y != null) scroller.scrollTop = y;
285
 
        updateDisplay([]);
286
 
      },
287
 
 
288
 
      operation: function(f){return operation(f)();},
289
 
      refresh: function(){
290
 
        updateDisplay(true);
291
 
        if (scroller.scrollHeight > lastScrollPos)
292
 
          scroller.scrollTop = lastScrollPos;
293
 
      },
294
 
      getInputField: function(){return input;},
295
 
      getWrapperElement: function(){return wrapper;},
296
 
      getScrollerElement: function(){return scroller;},
297
 
      getGutterElement: function(){return gutter;}
298
 
    };
299
 
 
300
 
    function getLine(n) { return getLineAt(doc, n); }
301
 
    function updateLineHeight(line, height) {
302
 
      gutterDirty = true;
303
 
      var diff = height - line.height;
304
 
      for (var n = line; n; n = n.parent) n.height += diff;
305
 
    }
306
 
 
307
 
    function setValue(code) {
308
 
      var top = {line: 0, ch: 0};
309
 
      updateLines(top, {line: doc.size - 1, ch: getLine(doc.size-1).text.length},
310
 
                  splitLines(code), top, top);
311
 
      updateInput = true;
312
 
    }
313
 
    function getValue(code) {
314
 
      var text = [];
315
 
      doc.iter(0, doc.size, function(line) { text.push(line.text); });
316
 
      return text.join("\n");
317
 
    }
318
 
 
319
 
    function onMouseDown(e) {
320
 
      setShift(e_prop(e, "shiftKey"));
321
 
      // Check whether this is a click in a widget
322
 
      for (var n = e_target(e); n != wrapper; n = n.parentNode)
323
 
        if (n.parentNode == code && n != mover) return;
324
 
 
325
 
      // See if this is a click in the gutter
326
 
      for (var n = e_target(e); n != wrapper; n = n.parentNode)
327
 
        if (n.parentNode == gutterText) {
328
 
          if (options.onGutterClick)
329
 
            options.onGutterClick(instance, indexOf(gutterText.childNodes, n) + showingFrom, e);
330
 
          return e_preventDefault(e);
331
 
        }
332
 
 
333
 
      var start = posFromMouse(e);
334
 
 
335
 
      switch (e_button(e)) {
336
 
      case 3:
337
 
        if (gecko && !mac) onContextMenu(e);
338
 
        return;
339
 
      case 2:
340
 
        if (start) setCursor(start.line, start.ch, true);
341
 
        return;
342
 
      }
343
 
      // For button 1, if it was clicked inside the editor
344
 
      // (posFromMouse returning non-null), we have to adjust the
345
 
      // selection.
346
 
      if (!start) {if (e_target(e) == scroller) e_preventDefault(e); return;}
347
 
 
348
 
      if (!focused) onFocus();
349
 
 
350
 
      var now = +new Date;
351
 
      if (lastDoubleClick && lastDoubleClick.time > now - 400 && posEq(lastDoubleClick.pos, start)) {
352
 
        e_preventDefault(e);
353
 
        setTimeout(focusInput, 20);
354
 
        return selectLine(start.line);
355
 
      } else if (lastClick && lastClick.time > now - 400 && posEq(lastClick.pos, start)) {
356
 
        lastDoubleClick = {time: now, pos: start};
357
 
        e_preventDefault(e);
358
 
        return selectWordAt(start);
359
 
      } else { lastClick = {time: now, pos: start}; }
360
 
 
361
 
      var last = start, going;
362
 
      if (dragAndDrop && !options.readOnly && !posEq(sel.from, sel.to) &&
363
 
          !posLess(start, sel.from) && !posLess(sel.to, start)) {
364
 
        // Let the drag handler handle this.
365
 
        if (webkit) lineSpace.draggable = true;
366
 
        var up = connect(targetDocument, "mouseup", operation(function(e2) {
367
 
          if (webkit) lineSpace.draggable = false;
368
 
          draggingText = false;
369
 
          up();
370
 
          if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) {
371
 
            e_preventDefault(e2);
372
 
            setCursor(start.line, start.ch, true);
373
 
            focusInput();
374
 
          }
375
 
        }), true);
376
 
        draggingText = true;
377
 
        return;
378
 
      }
379
 
      e_preventDefault(e);
380
 
      setCursor(start.line, start.ch, true);
381
 
 
382
 
      function extend(e) {
383
 
        var cur = posFromMouse(e, true);
384
 
        if (cur && !posEq(cur, last)) {
385
 
          if (!focused) onFocus();
386
 
          last = cur;
387
 
          setSelectionUser(start, cur);
388
 
          updateInput = false;
389
 
          var visible = visibleLines();
390
 
          if (cur.line >= visible.to || cur.line < visible.from)
391
 
            going = setTimeout(operation(function(){extend(e);}), 150);
392
 
        }
393
 
      }
394
 
 
395
 
      var move = connect(targetDocument, "mousemove", operation(function(e) {
396
 
        clearTimeout(going);
397
 
        e_preventDefault(e);
398
 
        extend(e);
399
 
      }), true);
400
 
      var up = connect(targetDocument, "mouseup", operation(function(e) {
401
 
        clearTimeout(going);
402
 
        var cur = posFromMouse(e);
403
 
        if (cur) setSelectionUser(start, cur);
404
 
        e_preventDefault(e);
405
 
        focusInput();
406
 
        updateInput = true;
407
 
        move(); up();
408
 
      }), true);
409
 
    }
410
 
    function onDoubleClick(e) {
411
 
      for (var n = e_target(e); n != wrapper; n = n.parentNode)
412
 
        if (n.parentNode == gutterText) return e_preventDefault(e);
413
 
      var start = posFromMouse(e);
414
 
      if (!start) return;
415
 
      lastDoubleClick = {time: +new Date, pos: start};
416
 
      e_preventDefault(e);
417
 
      selectWordAt(start);
418
 
    }
419
 
    function onDrop(e) {
420
 
      e.preventDefault();
421
 
      var pos = posFromMouse(e, true), files = e.dataTransfer.files;
422
 
      if (!pos || options.readOnly) return;
423
 
      if (files && files.length && window.FileReader && window.File) {
424
 
        function loadFile(file, i) {
425
 
          var reader = new FileReader;
426
 
          reader.onload = function() {
427
 
            text[i] = reader.result;
428
 
            if (++read == n) {
429
 
              pos = clipPos(pos);
430
 
              operation(function() {
431
 
                var end = replaceRange(text.join(""), pos, pos);
432
 
                setSelectionUser(pos, end);
433
 
              })();
434
 
            }
435
 
          };
436
 
          reader.readAsText(file);
437
 
        }
438
 
        var n = files.length, text = Array(n), read = 0;
439
 
        for (var i = 0; i < n; ++i) loadFile(files[i], i);
440
 
      }
441
 
      else {
442
 
        try {
443
 
          var text = e.dataTransfer.getData("Text");
444
 
          if (text) {
445
 
            var curFrom = sel.from, curTo = sel.to;
446
 
            setSelectionUser(pos, pos);
447
 
            if (draggingText) replaceRange("", curFrom, curTo);
448
 
            replaceSelection(text);
449
 
            focusInput();
450
 
          }
451
 
        }
452
 
        catch(e){}
453
 
      }
454
 
    }
455
 
    function onDragStart(e) {
456
 
      var txt = getSelection();
457
 
      // This will reset escapeElement
458
 
      htmlEscape(txt);
459
 
      e.dataTransfer.setDragImage(escapeElement, 0, 0);
460
 
      e.dataTransfer.setData("Text", txt);
461
 
    }
462
 
    function handleKeyBinding(e) {
463
 
      var name = keyNames[e_prop(e, "keyCode")], next = keyMap[options.keyMap].auto, bound, dropShift;
464
 
      function handleNext() {
465
 
        return next.call ? next.call(null, instance) : next;
466
 
      }
467
 
      if (name == null || e.altGraphKey) {
468
 
        if (next) options.keyMap = handleNext();
469
 
        return null;
470
 
      }
471
 
      if (e_prop(e, "altKey")) name = "Alt-" + name;
472
 
      if (e_prop(e, "ctrlKey")) name = "Ctrl-" + name;
473
 
      if (e_prop(e, "metaKey")) name = "Cmd-" + name;
474
 
      if (e_prop(e, "shiftKey") &&
475
 
          (bound = lookupKey("Shift-" + name, options.extraKeys, options.keyMap))) {
476
 
        dropShift = true;
477
 
      } else {
478
 
        bound = lookupKey(name, options.extraKeys, options.keyMap);
479
 
      }
480
 
      if (typeof bound == "string") {
481
 
        if (commands.propertyIsEnumerable(bound)) bound = commands[bound];
482
 
        else bound = null;
483
 
      }
484
 
      if (next && (bound || !isModifierKey(e))) options.keyMap = handleNext();
485
 
      if (!bound) return false;
486
 
      var prevShift = shiftSelecting;
487
 
      try {
488
 
        if (options.readOnly) suppressEdits = true;
489
 
        if (dropShift) shiftSelecting = null;
490
 
        bound(instance);
491
 
      } finally {
492
 
        shiftSelecting = prevShift;
493
 
        suppressEdits = false;
494
 
      }
495
 
      e_preventDefault(e);
496
 
      return true;
497
 
    }
498
 
    var lastStoppedKey = null;
499
 
    function onKeyDown(e) {
500
 
      if (!focused) onFocus();
501
 
      if (ie && e.keyCode == 27) { e.returnValue = false; }
502
 
      if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return;
503
 
      var code = e_prop(e, "keyCode");
504
 
      // IE does strange things with escape.
505
 
      setShift(code == 16 || e_prop(e, "shiftKey"));
506
 
      // First give onKeyEvent option a chance to handle this.
507
 
      var handled = handleKeyBinding(e);
508
 
      if (window.opera) {
509
 
        lastStoppedKey = handled ? code : null;
510
 
        // Opera has no cut event... we try to at least catch the key combo
511
 
        if (!handled && code == 88 && e_prop(e, mac ? "metaKey" : "ctrlKey"))
512
 
          replaceSelection("");
513
 
      }
514
 
    }
515
 
    function onKeyPress(e) {
516
 
      if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return;
517
 
      var keyCode = e_prop(e, "keyCode"), charCode = e_prop(e, "charCode");
518
 
      if (window.opera && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;}
519
 
      if (window.opera && !e.which && handleKeyBinding(e)) return;
520
 
      if (options.electricChars && mode.electricChars && options.smartIndent && !options.readOnly) {
521
 
        var ch = String.fromCharCode(charCode == null ? keyCode : charCode);
522
 
        if (mode.electricChars.indexOf(ch) > -1)
523
 
          setTimeout(operation(function() {indentLine(sel.to.line, "smart");}), 75);
524
 
      }
525
 
      fastPoll();
526
 
    }
527
 
    function onKeyUp(e) {
528
 
      if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return;
529
 
      if (e_prop(e, "keyCode") == 16) shiftSelecting = null;
530
 
    }
531
 
 
532
 
    function onFocus() {
533
 
      if (options.readOnly == "nocursor") return;
534
 
      if (!focused) {
535
 
        if (options.onFocus) options.onFocus(instance);
536
 
        focused = true;
537
 
        if (wrapper.className.search(/\bCodeMirror-focused\b/) == -1)
538
 
          wrapper.className += " CodeMirror-focused";
539
 
        if (!leaveInputAlone) resetInput(true);
540
 
      }
541
 
      slowPoll();
542
 
      restartBlink();
543
 
    }
544
 
    function onBlur() {
545
 
      if (focused) {
546
 
        if (options.onBlur) options.onBlur(instance);
547
 
        focused = false;
548
 
        if (bracketHighlighted)
549
 
          operation(function(){
550
 
            if (bracketHighlighted) { bracketHighlighted(); bracketHighlighted = null; }
551
 
          })();
552
 
        wrapper.className = wrapper.className.replace(" CodeMirror-focused", "");
553
 
      }
554
 
      clearInterval(blinker);
555
 
      setTimeout(function() {if (!focused) shiftSelecting = null;}, 150);
556
 
    }
557
 
 
558
 
    // Replace the range from from to to by the strings in newText.
559
 
    // Afterwards, set the selection to selFrom, selTo.
560
 
    function updateLines(from, to, newText, selFrom, selTo) {
561
 
      if (suppressEdits) return;
562
 
      if (history) {
563
 
        var old = [];
564
 
        doc.iter(from.line, to.line + 1, function(line) { old.push(line.text); });
565
 
        history.addChange(from.line, newText.length, old);
566
 
        while (history.done.length > options.undoDepth) history.done.shift();
567
 
      }
568
 
      updateLinesNoUndo(from, to, newText, selFrom, selTo);
569
 
    }
570
 
    function unredoHelper(from, to, dir) {
571
 
      var set = from.pop(), len = set ? set.length : 0, out = [];
572
 
      for (var i = dir > 0 ? 0 : len - 1, e = dir > 0 ? len : -1; i != e; i += dir) {
573
 
        var change = set[i];
574
 
        var replaced = [], end = change.start + change.added;
575
 
        doc.iter(change.start, end, function(line) { replaced.push(line.text); });
576
 
        out.push({start: change.start, added: change.old.length, old: replaced});
577
 
        var pos = clipPos({line: change.start + change.old.length - 1,
578
 
                           ch: editEnd(replaced[replaced.length-1], change.old[change.old.length-1])});
579
 
        updateLinesNoUndo({line: change.start, ch: 0}, {line: end - 1, ch: getLine(end-1).text.length}, change.old, pos, pos);
580
 
      }
581
 
      updateInput = true;
582
 
      to.push(out);
583
 
    }
584
 
    function undo() {unredoHelper(history.done, history.undone, -1);}
585
 
    function redo() {unredoHelper(history.undone, history.done, 1);}
586
 
 
587
 
    function updateLinesNoUndo(from, to, newText, selFrom, selTo) {
588
 
      if (suppressEdits) return;
589
 
      var recomputeMaxLength = false, maxLineLength = maxLine.length;
590
 
      if (!options.lineWrapping)
591
 
        doc.iter(from.line, to.line, function(line) {
592
 
          if (line.text.length == maxLineLength) {recomputeMaxLength = true; return true;}
593
 
        });
594
 
      if (from.line != to.line || newText.length > 1) gutterDirty = true;
595
 
 
596
 
      var nlines = to.line - from.line, firstLine = getLine(from.line), lastLine = getLine(to.line);
597
 
      // First adjust the line structure, taking some care to leave highlighting intact.
598
 
      if (from.ch == 0 && to.ch == 0 && newText[newText.length - 1] == "") {
599
 
        // This is a whole-line replace. Treated specially to make
600
 
        // sure line objects move the way they are supposed to.
601
 
        var added = [], prevLine = null;
602
 
        if (from.line) {
603
 
          prevLine = getLine(from.line - 1);
604
 
          prevLine.fixMarkEnds(lastLine);
605
 
        } else lastLine.fixMarkStarts();
606
 
        for (var i = 0, e = newText.length - 1; i < e; ++i)
607
 
          added.push(Line.inheritMarks(newText[i], prevLine));
608
 
        if (nlines) doc.remove(from.line, nlines, callbacks);
609
 
        if (added.length) doc.insert(from.line, added);
610
 
      } else if (firstLine == lastLine) {
611
 
        if (newText.length == 1)
612
 
          firstLine.replace(from.ch, to.ch, newText[0]);
613
 
        else {
614
 
          lastLine = firstLine.split(to.ch, newText[newText.length-1]);
615
 
          firstLine.replace(from.ch, null, newText[0]);
616
 
          firstLine.fixMarkEnds(lastLine);
617
 
          var added = [];
618
 
          for (var i = 1, e = newText.length - 1; i < e; ++i)
619
 
            added.push(Line.inheritMarks(newText[i], firstLine));
620
 
          added.push(lastLine);
621
 
          doc.insert(from.line + 1, added);
622
 
        }
623
 
      } else if (newText.length == 1) {
624
 
        firstLine.replace(from.ch, null, newText[0]);
625
 
        lastLine.replace(null, to.ch, "");
626
 
        firstLine.append(lastLine);
627
 
        doc.remove(from.line + 1, nlines, callbacks);
628
 
      } else {
629
 
        var added = [];
630
 
        firstLine.replace(from.ch, null, newText[0]);
631
 
        lastLine.replace(null, to.ch, newText[newText.length-1]);
632
 
        firstLine.fixMarkEnds(lastLine);
633
 
        for (var i = 1, e = newText.length - 1; i < e; ++i)
634
 
          added.push(Line.inheritMarks(newText[i], firstLine));
635
 
        if (nlines > 1) doc.remove(from.line + 1, nlines - 1, callbacks);
636
 
        doc.insert(from.line + 1, added);
637
 
      }
638
 
      if (options.lineWrapping) {
639
 
        var perLine = scroller.clientWidth / charWidth() - 3;
640
 
        doc.iter(from.line, from.line + newText.length, function(line) {
641
 
          if (line.hidden) return;
642
 
          var guess = Math.ceil(line.text.length / perLine) || 1;
643
 
          if (guess != line.height) updateLineHeight(line, guess);
644
 
        });
645
 
      } else {
646
 
        doc.iter(from.line, i + newText.length, function(line) {
647
 
          var l = line.text;
648
 
          if (l.length > maxLineLength) {
649
 
            maxLine = l; maxLineLength = l.length; maxWidth = null;
650
 
            recomputeMaxLength = false;
651
 
          }
652
 
        });
653
 
        if (recomputeMaxLength) {
654
 
          maxLineLength = 0; maxLine = ""; maxWidth = null;
655
 
          doc.iter(0, doc.size, function(line) {
656
 
            var l = line.text;
657
 
            if (l.length > maxLineLength) {
658
 
              maxLineLength = l.length; maxLine = l;
659
 
            }
660
 
          });
661
 
        }
662
 
      }
663
 
 
664
 
      // Add these lines to the work array, so that they will be
665
 
      // highlighted. Adjust work lines if lines were added/removed.
666
 
      var newWork = [], lendiff = newText.length - nlines - 1;
667
 
      for (var i = 0, l = work.length; i < l; ++i) {
668
 
        var task = work[i];
669
 
        if (task < from.line) newWork.push(task);
670
 
        else if (task > to.line) newWork.push(task + lendiff);
671
 
      }
672
 
      var hlEnd = from.line + Math.min(newText.length, 500);
673
 
      highlightLines(from.line, hlEnd);
674
 
      newWork.push(hlEnd);
675
 
      work = newWork;
676
 
      startWorker(100);
677
 
      // Remember that these lines changed, for updating the display
678
 
      changes.push({from: from.line, to: to.line + 1, diff: lendiff});
679
 
      var changeObj = {from: from, to: to, text: newText};
680
 
      if (textChanged) {
681
 
        for (var cur = textChanged; cur.next; cur = cur.next) {}
682
 
        cur.next = changeObj;
683
 
      } else textChanged = changeObj;
684
 
 
685
 
      // Update the selection
686
 
      function updateLine(n) {return n <= Math.min(to.line, to.line + lendiff) ? n : n + lendiff;}
687
 
      setSelection(selFrom, selTo, updateLine(sel.from.line), updateLine(sel.to.line));
688
 
 
689
 
      // Make sure the scroll-size div has the correct height.
690
 
      if (scroller.clientHeight)
691
 
        code.style.height = (doc.height * textHeight() + 2 * paddingTop()) + "px";
692
 
    }
693
 
 
694
 
    function replaceRange(code, from, to) {
695
 
      from = clipPos(from);
696
 
      if (!to) to = from; else to = clipPos(to);
697
 
      code = splitLines(code);
698
 
      function adjustPos(pos) {
699
 
        if (posLess(pos, from)) return pos;
700
 
        if (!posLess(to, pos)) return end;
701
 
        var line = pos.line + code.length - (to.line - from.line) - 1;
702
 
        var ch = pos.ch;
703
 
        if (pos.line == to.line)
704
 
          ch += code[code.length-1].length - (to.ch - (to.line == from.line ? from.ch : 0));
705
 
        return {line: line, ch: ch};
706
 
      }
707
 
      var end;
708
 
      replaceRange1(code, from, to, function(end1) {
709
 
        end = end1;
710
 
        return {from: adjustPos(sel.from), to: adjustPos(sel.to)};
711
 
      });
712
 
      return end;
713
 
    }
714
 
    function replaceSelection(code, collapse) {
715
 
      replaceRange1(splitLines(code), sel.from, sel.to, function(end) {
716
 
        if (collapse == "end") return {from: end, to: end};
717
 
        else if (collapse == "start") return {from: sel.from, to: sel.from};
718
 
        else return {from: sel.from, to: end};
719
 
      });
720
 
    }
721
 
    function replaceRange1(code, from, to, computeSel) {
722
 
      var endch = code.length == 1 ? code[0].length + from.ch : code[code.length-1].length;
723
 
      var newSel = computeSel({line: from.line + code.length - 1, ch: endch});
724
 
      updateLines(from, to, code, newSel.from, newSel.to);
725
 
    }
726
 
 
727
 
    function getRange(from, to) {
728
 
      var l1 = from.line, l2 = to.line;
729
 
      if (l1 == l2) return getLine(l1).text.slice(from.ch, to.ch);
730
 
      var code = [getLine(l1).text.slice(from.ch)];
731
 
      doc.iter(l1 + 1, l2, function(line) { code.push(line.text); });
732
 
      code.push(getLine(l2).text.slice(0, to.ch));
733
 
      return code.join("\n");
734
 
    }
735
 
    function getSelection() {
736
 
      return getRange(sel.from, sel.to);
737
 
    }
738
 
 
739
 
    var pollingFast = false; // Ensures slowPoll doesn't cancel fastPoll
740
 
    function slowPoll() {
741
 
      if (pollingFast) return;
742
 
      poll.set(options.pollInterval, function() {
743
 
        startOperation();
744
 
        readInput();
745
 
        if (focused) slowPoll();
746
 
        endOperation();
747
 
      });
748
 
    }
749
 
    function fastPoll() {
750
 
      var missed = false;
751
 
      pollingFast = true;
752
 
      function p() {
753
 
        startOperation();
754
 
        var changed = readInput();
755
 
        if (!changed && !missed) {missed = true; poll.set(60, p);}
756
 
        else {pollingFast = false; slowPoll();}
757
 
        endOperation();
758
 
      }
759
 
      poll.set(20, p);
760
 
    }
761
 
 
762
 
    // Previnput is a hack to work with IME. If we reset the textarea
763
 
    // on every change, that breaks IME. So we look for changes
764
 
    // compared to the previous content instead. (Modern browsers have
765
 
    // events that indicate IME taking place, but these are not widely
766
 
    // supported or compatible enough yet to rely on.)
767
 
    var prevInput = "";
768
 
    function readInput() {
769
 
      if (leaveInputAlone || !focused || hasSelection(input) || options.readOnly) return false;
770
 
      var text = input.value;
771
 
      if (text == prevInput) return false;
772
 
      shiftSelecting = null;
773
 
      var same = 0, l = Math.min(prevInput.length, text.length);
774
 
      while (same < l && prevInput[same] == text[same]) ++same;
775
 
      if (same < prevInput.length)
776
 
        sel.from = {line: sel.from.line, ch: sel.from.ch - (prevInput.length - same)};
777
 
      else if (overwrite && posEq(sel.from, sel.to))
778
 
        sel.to = {line: sel.to.line, ch: Math.min(getLine(sel.to.line).text.length, sel.to.ch + (text.length - same))};
779
 
      replaceSelection(text.slice(same), "end");
780
 
      prevInput = text;
781
 
      return true;
782
 
    }
783
 
    function resetInput(user) {
784
 
      if (!posEq(sel.from, sel.to)) {
785
 
        prevInput = "";
786
 
        input.value = getSelection();
787
 
        selectInput(input);
788
 
      } else if (user) prevInput = input.value = "";
789
 
    }
790
 
 
791
 
    function focusInput() {
792
 
      if (options.readOnly != "nocursor") input.focus();
793
 
    }
794
 
 
795
 
    function scrollEditorIntoView() {
796
 
      if (!cursor.getBoundingClientRect) return;
797
 
      var rect = cursor.getBoundingClientRect();
798
 
      // IE returns bogus coordinates when the instance sits inside of an iframe and the cursor is hidden
799
 
      if (ie && rect.top == rect.bottom) return;
800
 
      var winH = window.innerHeight || Math.max(document.body.offsetHeight, document.documentElement.offsetHeight);
801
 
      if (rect.top < 0 || rect.bottom > winH) cursor.scrollIntoView();
802
 
    }
803
 
    function scrollCursorIntoView() {
804
 
      var cursor = localCoords(sel.inverted ? sel.from : sel.to);
805
 
      var x = options.lineWrapping ? Math.min(cursor.x, lineSpace.offsetWidth) : cursor.x;
806
 
      return scrollIntoView(x, cursor.y, x, cursor.yBot);
807
 
    }
808
 
    function scrollIntoView(x1, y1, x2, y2) {
809
 
      var pl = paddingLeft(), pt = paddingTop(), lh = textHeight();
810
 
      y1 += pt; y2 += pt; x1 += pl; x2 += pl;
811
 
      var screen = scroller.clientHeight, screentop = scroller.scrollTop, scrolled = false, result = true;
812
 
      if (y1 < screentop) {scroller.scrollTop = Math.max(0, y1 - 2*lh); scrolled = true;}
813
 
      else if (y2 > screentop + screen) {scroller.scrollTop = y2 + lh - screen; scrolled = true;}
814
 
 
815
 
      var screenw = scroller.clientWidth, screenleft = scroller.scrollLeft;
816
 
      var gutterw = options.fixedGutter ? gutter.clientWidth : 0;
817
 
      if (x1 < screenleft + gutterw) {
818
 
        if (x1 < 50) x1 = 0;
819
 
        scroller.scrollLeft = Math.max(0, x1 - 10 - gutterw);
820
 
        scrolled = true;
821
 
      }
822
 
      else if (x2 > screenw + screenleft - 3) {
823
 
        scroller.scrollLeft = x2 + 10 - screenw;
824
 
        scrolled = true;
825
 
        if (x2 > code.clientWidth) result = false;
826
 
      }
827
 
      if (scrolled && options.onScroll) options.onScroll(instance);
828
 
      return result;
829
 
    }
830
 
 
831
 
    function visibleLines() {
832
 
      var lh = textHeight(), top = scroller.scrollTop - paddingTop();
833
 
      var from_height = Math.max(0, Math.floor(top / lh));
834
 
      var to_height = Math.ceil((top + scroller.clientHeight) / lh);
835
 
      return {from: lineAtHeight(doc, from_height),
836
 
              to: lineAtHeight(doc, to_height)};
837
 
    }
838
 
    // Uses a set of changes plus the current scroll position to
839
 
    // determine which DOM updates have to be made, and makes the
840
 
    // updates.
841
 
    function updateDisplay(changes, suppressCallback) {
842
 
      if (!scroller.clientWidth) {
843
 
        showingFrom = showingTo = displayOffset = 0;
844
 
        return;
845
 
      }
846
 
      // Compute the new visible window
847
 
      var visible = visibleLines();
848
 
      // Bail out if the visible area is already rendered and nothing changed.
849
 
      if (changes !== true && changes.length == 0 && visible.from > showingFrom && visible.to < showingTo) return;
850
 
      var from = Math.max(visible.from - 100, 0), to = Math.min(doc.size, visible.to + 100);
851
 
      if (showingFrom < from && from - showingFrom < 20) from = showingFrom;
852
 
      if (showingTo > to && showingTo - to < 20) to = Math.min(doc.size, showingTo);
853
 
 
854
 
      // Create a range of theoretically intact lines, and punch holes
855
 
      // in that using the change info.
856
 
      var intact = changes === true ? [] :
857
 
        computeIntact([{from: showingFrom, to: showingTo, domStart: 0}], changes);
858
 
      // Clip off the parts that won't be visible
859
 
      var intactLines = 0;
860
 
      for (var i = 0; i < intact.length; ++i) {
861
 
        var range = intact[i];
862
 
        if (range.from < from) {range.domStart += (from - range.from); range.from = from;}
863
 
        if (range.to > to) range.to = to;
864
 
        if (range.from >= range.to) intact.splice(i--, 1);
865
 
        else intactLines += range.to - range.from;
866
 
      }
867
 
      if (intactLines == to - from) return;
868
 
      intact.sort(function(a, b) {return a.domStart - b.domStart;});
869
 
 
870
 
      var th = textHeight(), gutterDisplay = gutter.style.display;
871
 
      lineDiv.style.display = "none";
872
 
      patchDisplay(from, to, intact);
873
 
      lineDiv.style.display = gutter.style.display = "";
874
 
 
875
 
      // Position the mover div to align with the lines it's supposed
876
 
      // to be showing (which will cover the visible display)
877
 
      var different = from != showingFrom || to != showingTo || lastSizeC != scroller.clientHeight + th;
878
 
      // This is just a bogus formula that detects when the editor is
879
 
      // resized or the font size changes.
880
 
      if (different) lastSizeC = scroller.clientHeight + th;
881
 
      showingFrom = from; showingTo = to;
882
 
      displayOffset = heightAtLine(doc, from);
883
 
      mover.style.top = (displayOffset * th) + "px";
884
 
      if (scroller.clientHeight)
885
 
        code.style.height = (doc.height * th + 2 * paddingTop()) + "px";
886
 
 
887
 
      // Since this is all rather error prone, it is honoured with the
888
 
      // only assertion in the whole file.
889
 
      if (lineDiv.childNodes.length != showingTo - showingFrom)
890
 
        throw new Error("BAD PATCH! " + JSON.stringify(intact) + " size=" + (showingTo - showingFrom) +
891
 
                        " nodes=" + lineDiv.childNodes.length);
892
 
 
893
 
      if (options.lineWrapping) {
894
 
        maxWidth = scroller.clientWidth;
895
 
        var curNode = lineDiv.firstChild, heightChanged = false;
896
 
        doc.iter(showingFrom, showingTo, function(line) {
897
 
          if (!line.hidden) {
898
 
            var height = Math.round(curNode.offsetHeight / th) || 1;
899
 
            if (line.height != height) {
900
 
              updateLineHeight(line, height);
901
 
              gutterDirty = heightChanged = true;
902
 
            }
903
 
          }
904
 
          curNode = curNode.nextSibling;
905
 
        });
906
 
        if (heightChanged)
907
 
          code.style.height = (doc.height * th + 2 * paddingTop()) + "px";
908
 
      } else {
909
 
        if (maxWidth == null) maxWidth = stringWidth(maxLine);
910
 
        if (maxWidth > scroller.clientWidth) {
911
 
          lineSpace.style.width = maxWidth + "px";
912
 
          // Needed to prevent odd wrapping/hiding of widgets placed in here.
913
 
          code.style.width = "";
914
 
          code.style.width = scroller.scrollWidth + "px";
915
 
        } else {
916
 
          lineSpace.style.width = code.style.width = "";
917
 
        }
918
 
      }
919
 
      gutter.style.display = gutterDisplay;
920
 
      if (different || gutterDirty) updateGutter();
921
 
      updateSelection();
922
 
      if (!suppressCallback && options.onUpdate) options.onUpdate(instance);
923
 
      return true;
924
 
    }
925
 
 
926
 
    function computeIntact(intact, changes) {
927
 
      for (var i = 0, l = changes.length || 0; i < l; ++i) {
928
 
        var change = changes[i], intact2 = [], diff = change.diff || 0;
929
 
        for (var j = 0, l2 = intact.length; j < l2; ++j) {
930
 
          var range = intact[j];
931
 
          if (change.to <= range.from && change.diff)
932
 
            intact2.push({from: range.from + diff, to: range.to + diff,
933
 
                          domStart: range.domStart});
934
 
          else if (change.to <= range.from || change.from >= range.to)
935
 
            intact2.push(range);
936
 
          else {
937
 
            if (change.from > range.from)
938
 
              intact2.push({from: range.from, to: change.from, domStart: range.domStart});
939
 
            if (change.to < range.to)
940
 
              intact2.push({from: change.to + diff, to: range.to + diff,
941
 
                            domStart: range.domStart + (change.to - range.from)});
942
 
          }
943
 
        }
944
 
        intact = intact2;
945
 
      }
946
 
      return intact;
947
 
    }
948
 
 
949
 
    function patchDisplay(from, to, intact) {
950
 
      // The first pass removes the DOM nodes that aren't intact.
951
 
      if (!intact.length) lineDiv.innerHTML = "";
952
 
      else {
953
 
        function killNode(node) {
954
 
          var tmp = node.nextSibling;
955
 
          node.parentNode.removeChild(node);
956
 
          return tmp;
957
 
        }
958
 
        var domPos = 0, curNode = lineDiv.firstChild, n;
959
 
        for (var i = 0; i < intact.length; ++i) {
960
 
          var cur = intact[i];
961
 
          while (cur.domStart > domPos) {curNode = killNode(curNode); domPos++;}
962
 
          for (var j = 0, e = cur.to - cur.from; j < e; ++j) {curNode = curNode.nextSibling; domPos++;}
963
 
        }
964
 
        while (curNode) curNode = killNode(curNode);
965
 
      }
966
 
      // This pass fills in the lines that actually changed.
967
 
      var nextIntact = intact.shift(), curNode = lineDiv.firstChild, j = from;
968
 
      var scratch = targetDocument.createElement("div"), newElt;
969
 
      doc.iter(from, to, function(line) {
970
 
        if (nextIntact && nextIntact.to == j) nextIntact = intact.shift();
971
 
        if (!nextIntact || nextIntact.from > j) {
972
 
          if (line.hidden) var html = scratch.innerHTML = "<pre></pre>";
973
 
          else {
974
 
            var html = '<pre>' + line.getHTML(tabText) + '</pre>';
975
 
            // Kludge to make sure the styled element lies behind the selection (by z-index)
976
 
            if (line.className)
977
 
              html = '<div style="position: relative"><pre class="' + line.className +
978
 
              '" style="position: absolute; left: 0; right: 0; top: 0; bottom: 0; z-index: -2">&#160;</pre>' + html + "</div>";
979
 
          }
980
 
          scratch.innerHTML = html;
981
 
          lineDiv.insertBefore(scratch.firstChild, curNode);
982
 
        } else {
983
 
          curNode = curNode.nextSibling;
984
 
        }
985
 
        ++j;
986
 
      });
987
 
    }
988
 
 
989
 
    function updateGutter() {
990
 
      if (!options.gutter && !options.lineNumbers) return;
991
 
      var hText = mover.offsetHeight, hEditor = scroller.clientHeight;
992
 
      gutter.style.height = (hText - hEditor < 2 ? hEditor : hText) + "px";
993
 
      var html = [], i = showingFrom;
994
 
      doc.iter(showingFrom, Math.max(showingTo, showingFrom + 1), function(line) {
995
 
        if (line.hidden) {
996
 
          html.push("<pre></pre>");
997
 
        } else {
998
 
          var marker = line.gutterMarker;
999
 
          var text = options.lineNumbers ? i + options.firstLineNumber : null;
1000
 
          if (marker && marker.text)
1001
 
            text = marker.text.replace("%N%", text != null ? text : "");
1002
 
          else if (text == null)
1003
 
            text = "\u00a0";
1004
 
          html.push((marker && marker.style ? '<pre class="' + marker.style + '">' : "<pre>"), text);
1005
 
          for (var j = 1; j < line.height; ++j) html.push("<br/>&#160;");
1006
 
          html.push("</pre>");
1007
 
        }
1008
 
        ++i;
1009
 
      });
1010
 
      gutter.style.display = "none";
1011
 
      gutterText.innerHTML = html.join("");
1012
 
      var minwidth = String(doc.size).length, firstNode = gutterText.firstChild, val = eltText(firstNode), pad = "";
1013
 
      while (val.length + pad.length < minwidth) pad += "\u00a0";
1014
 
      if (pad) firstNode.insertBefore(targetDocument.createTextNode(pad), firstNode.firstChild);
1015
 
      gutter.style.display = "";
1016
 
      lineSpace.style.marginLeft = gutter.offsetWidth + "px";
1017
 
      gutterDirty = false;
1018
 
    }
1019
 
    function updateSelection() {
1020
 
      var collapsed = posEq(sel.from, sel.to);
1021
 
      var fromPos = localCoords(sel.from, true);
1022
 
      var toPos = collapsed ? fromPos : localCoords(sel.to, true);
1023
 
      var headPos = sel.inverted ? fromPos : toPos, th = textHeight();
1024
 
      var wrapOff = eltOffset(wrapper), lineOff = eltOffset(lineDiv);
1025
 
      inputDiv.style.top = Math.max(0, Math.min(scroller.offsetHeight, headPos.y + lineOff.top - wrapOff.top)) + "px";
1026
 
      inputDiv.style.left = Math.max(0, Math.min(scroller.offsetWidth, headPos.x + lineOff.left - wrapOff.left)) + "px";
1027
 
      if (collapsed) {
1028
 
        cursor.style.top = headPos.y + "px";
1029
 
        cursor.style.left = (options.lineWrapping ? Math.min(headPos.x, lineSpace.offsetWidth) : headPos.x) + "px";
1030
 
        cursor.style.display = "";
1031
 
        selectionDiv.style.display = "none";
1032
 
      } else {
1033
 
        var sameLine = fromPos.y == toPos.y, html = "";
1034
 
        function add(left, top, right, height) {
1035
 
          html += '<div class="CodeMirror-selected" style="position: absolute; left: ' + left +
1036
 
            'px; top: ' + top + 'px; right: ' + right + 'px; height: ' + height + 'px"></div>';
1037
 
        }
1038
 
        if (sel.from.ch && fromPos.y >= 0) {
1039
 
          var right = sameLine ? lineSpace.clientWidth - toPos.x : 0;
1040
 
          add(fromPos.x, fromPos.y, right, th);
1041
 
        }
1042
 
        var middleStart = Math.max(0, fromPos.y + (sel.from.ch ? th : 0));
1043
 
        var middleHeight = Math.min(toPos.y, lineSpace.clientHeight) - middleStart;
1044
 
        if (middleHeight > 0.2 * th)
1045
 
          add(0, middleStart, 0, middleHeight);
1046
 
        if ((!sameLine || !sel.from.ch) && toPos.y < lineSpace.clientHeight - .5 * th)
1047
 
          add(0, toPos.y, lineSpace.clientWidth - toPos.x, th);
1048
 
        selectionDiv.innerHTML = html;
1049
 
        cursor.style.display = "none";
1050
 
        selectionDiv.style.display = "";
1051
 
      }
1052
 
    }
1053
 
 
1054
 
    function setShift(val) {
1055
 
      if (val) shiftSelecting = shiftSelecting || (sel.inverted ? sel.to : sel.from);
1056
 
      else shiftSelecting = null;
1057
 
    }
1058
 
    function setSelectionUser(from, to) {
1059
 
      var sh = shiftSelecting && clipPos(shiftSelecting);
1060
 
      if (sh) {
1061
 
        if (posLess(sh, from)) from = sh;
1062
 
        else if (posLess(to, sh)) to = sh;
1063
 
      }
1064
 
      setSelection(from, to);
1065
 
      userSelChange = true;
1066
 
    }
1067
 
    // Update the selection. Last two args are only used by
1068
 
    // updateLines, since they have to be expressed in the line
1069
 
    // numbers before the update.
1070
 
    function setSelection(from, to, oldFrom, oldTo) {
1071
 
      goalColumn = null;
1072
 
      if (oldFrom == null) {oldFrom = sel.from.line; oldTo = sel.to.line;}
1073
 
      if (posEq(sel.from, from) && posEq(sel.to, to)) return;
1074
 
      if (posLess(to, from)) {var tmp = to; to = from; from = tmp;}
1075
 
 
1076
 
      // Skip over hidden lines.
1077
 
      if (from.line != oldFrom) from = skipHidden(from, oldFrom, sel.from.ch);
1078
 
      if (to.line != oldTo) to = skipHidden(to, oldTo, sel.to.ch);
1079
 
 
1080
 
      if (posEq(from, to)) sel.inverted = false;
1081
 
      else if (posEq(from, sel.to)) sel.inverted = false;
1082
 
      else if (posEq(to, sel.from)) sel.inverted = true;
1083
 
 
1084
 
      sel.from = from; sel.to = to;
1085
 
      selectionChanged = true;
1086
 
    }
1087
 
    function skipHidden(pos, oldLine, oldCh) {
1088
 
      function getNonHidden(dir) {
1089
 
        var lNo = pos.line + dir, end = dir == 1 ? doc.size : -1;
1090
 
        while (lNo != end) {
1091
 
          var line = getLine(lNo);
1092
 
          if (!line.hidden) {
1093
 
            var ch = pos.ch;
1094
 
            if (ch > oldCh || ch > line.text.length) ch = line.text.length;
1095
 
            return {line: lNo, ch: ch};
1096
 
          }
1097
 
          lNo += dir;
1098
 
        }
1099
 
      }
1100
 
      var line = getLine(pos.line);
1101
 
      if (!line.hidden) return pos;
1102
 
      if (pos.line >= oldLine) return getNonHidden(1) || getNonHidden(-1);
1103
 
      else return getNonHidden(-1) || getNonHidden(1);
1104
 
    }
1105
 
    function setCursor(line, ch, user) {
1106
 
      var pos = clipPos({line: line, ch: ch || 0});
1107
 
      (user ? setSelectionUser : setSelection)(pos, pos);
1108
 
    }
1109
 
 
1110
 
    function clipLine(n) {return Math.max(0, Math.min(n, doc.size-1));}
1111
 
    function clipPos(pos) {
1112
 
      if (pos.line < 0) return {line: 0, ch: 0};
1113
 
      if (pos.line >= doc.size) return {line: doc.size-1, ch: getLine(doc.size-1).text.length};
1114
 
      var ch = pos.ch, linelen = getLine(pos.line).text.length;
1115
 
      if (ch == null || ch > linelen) return {line: pos.line, ch: linelen};
1116
 
      else if (ch < 0) return {line: pos.line, ch: 0};
1117
 
      else return pos;
1118
 
    }
1119
 
 
1120
 
    function findPosH(dir, unit) {
1121
 
      var end = sel.inverted ? sel.from : sel.to, line = end.line, ch = end.ch;
1122
 
      var lineObj = getLine(line);
1123
 
      function findNextLine() {
1124
 
        for (var l = line + dir, e = dir < 0 ? -1 : doc.size; l != e; l += dir) {
1125
 
          var lo = getLine(l);
1126
 
          if (!lo.hidden) { line = l; lineObj = lo; return true; }
1127
 
        }
1128
 
      }
1129
 
      function moveOnce(boundToLine) {
1130
 
        if (ch == (dir < 0 ? 0 : lineObj.text.length)) {
1131
 
          if (!boundToLine && findNextLine()) ch = dir < 0 ? lineObj.text.length : 0;
1132
 
          else return false;
1133
 
        } else ch += dir;
1134
 
        return true;
1135
 
      }
1136
 
      if (unit == "char") moveOnce();
1137
 
      else if (unit == "column") moveOnce(true);
1138
 
      else if (unit == "word") {
1139
 
        var sawWord = false;
1140
 
        for (;;) {
1141
 
          if (dir < 0) if (!moveOnce()) break;
1142
 
          if (isWordChar(lineObj.text.charAt(ch))) sawWord = true;
1143
 
          else if (sawWord) {if (dir < 0) {dir = 1; moveOnce();} break;}
1144
 
          if (dir > 0) if (!moveOnce()) break;
1145
 
        }
1146
 
      }
1147
 
      return {line: line, ch: ch};
1148
 
    }
1149
 
    function moveH(dir, unit) {
1150
 
      var pos = dir < 0 ? sel.from : sel.to;
1151
 
      if (shiftSelecting || posEq(sel.from, sel.to)) pos = findPosH(dir, unit);
1152
 
      setCursor(pos.line, pos.ch, true);
1153
 
    }
1154
 
    function deleteH(dir, unit) {
1155
 
      if (!posEq(sel.from, sel.to)) replaceRange("", sel.from, sel.to);
1156
 
      else if (dir < 0) replaceRange("", findPosH(dir, unit), sel.to);
1157
 
      else replaceRange("", sel.from, findPosH(dir, unit));
1158
 
      userSelChange = true;
1159
 
    }
1160
 
    var goalColumn = null;
1161
 
    function moveV(dir, unit) {
1162
 
      var dist = 0, pos = localCoords(sel.inverted ? sel.from : sel.to, true);
1163
 
      if (goalColumn != null) pos.x = goalColumn;
1164
 
      if (unit == "page") dist = Math.min(scroller.clientHeight, window.innerHeight || document.documentElement.clientHeight);
1165
 
      else if (unit == "line") dist = textHeight();
1166
 
      var target = coordsChar(pos.x, pos.y + dist * dir + 2);
1167
 
      setCursor(target.line, target.ch, true);
1168
 
      goalColumn = pos.x;
1169
 
    }
1170
 
 
1171
 
    function selectWordAt(pos) {
1172
 
      var line = getLine(pos.line).text;
1173
 
      var start = pos.ch, end = pos.ch;
1174
 
      while (start > 0 && isWordChar(line.charAt(start - 1))) --start;
1175
 
      while (end < line.length && isWordChar(line.charAt(end))) ++end;
1176
 
      setSelectionUser({line: pos.line, ch: start}, {line: pos.line, ch: end});
1177
 
    }
1178
 
    function selectLine(line) {
1179
 
      setSelectionUser({line: line, ch: 0}, {line: line, ch: getLine(line).text.length});
1180
 
    }
1181
 
    function indentSelected(mode) {
1182
 
      if (posEq(sel.from, sel.to)) return indentLine(sel.from.line, mode);
1183
 
      var e = sel.to.line - (sel.to.ch ? 0 : 1);
1184
 
      for (var i = sel.from.line; i <= e; ++i) indentLine(i, mode);
1185
 
    }
1186
 
 
1187
 
    function indentLine(n, how) {
1188
 
      if (!how) how = "add";
1189
 
      if (how == "smart") {
1190
 
        if (!mode.indent) how = "prev";
1191
 
        else var state = getStateBefore(n);
1192
 
      }
1193
 
 
1194
 
      var line = getLine(n), curSpace = line.indentation(options.tabSize),
1195
 
          curSpaceString = line.text.match(/^\s*/)[0], indentation;
1196
 
      if (how == "prev") {
1197
 
        if (n) indentation = getLine(n-1).indentation(options.tabSize);
1198
 
        else indentation = 0;
1199
 
      }
1200
 
      else if (how == "smart") indentation = mode.indent(state, line.text.slice(curSpaceString.length), line.text);
1201
 
      else if (how == "add") indentation = curSpace + options.indentUnit;
1202
 
      else if (how == "subtract") indentation = curSpace - options.indentUnit;
1203
 
      indentation = Math.max(0, indentation);
1204
 
      var diff = indentation - curSpace;
1205
 
 
1206
 
      if (!diff) {
1207
 
        if (sel.from.line != n && sel.to.line != n) return;
1208
 
        var indentString = curSpaceString;
1209
 
      }
1210
 
      else {
1211
 
        var indentString = "", pos = 0;
1212
 
        if (options.indentWithTabs)
1213
 
          for (var i = Math.floor(indentation / options.tabSize); i; --i) {pos += options.tabSize; indentString += "\t";}
1214
 
        while (pos < indentation) {++pos; indentString += " ";}
1215
 
      }
1216
 
 
1217
 
      replaceRange(indentString, {line: n, ch: 0}, {line: n, ch: curSpaceString.length});
1218
 
    }
1219
 
 
1220
 
    function loadMode() {
1221
 
      mode = CodeMirror.getMode(options, options.mode);
1222
 
      doc.iter(0, doc.size, function(line) { line.stateAfter = null; });
1223
 
      work = [0];
1224
 
      startWorker();
1225
 
    }
1226
 
    function gutterChanged() {
1227
 
      var visible = options.gutter || options.lineNumbers;
1228
 
      gutter.style.display = visible ? "" : "none";
1229
 
      if (visible) gutterDirty = true;
1230
 
      else lineDiv.parentNode.style.marginLeft = 0;
1231
 
    }
1232
 
    function wrappingChanged(from, to) {
1233
 
      if (options.lineWrapping) {
1234
 
        wrapper.className += " CodeMirror-wrap";
1235
 
        var perLine = scroller.clientWidth / charWidth() - 3;
1236
 
        doc.iter(0, doc.size, function(line) {
1237
 
          if (line.hidden) return;
1238
 
          var guess = Math.ceil(line.text.length / perLine) || 1;
1239
 
          if (guess != 1) updateLineHeight(line, guess);
1240
 
        });
1241
 
        lineSpace.style.width = code.style.width = "";
1242
 
      } else {
1243
 
        wrapper.className = wrapper.className.replace(" CodeMirror-wrap", "");
1244
 
        maxWidth = null; maxLine = "";
1245
 
        doc.iter(0, doc.size, function(line) {
1246
 
          if (line.height != 1 && !line.hidden) updateLineHeight(line, 1);
1247
 
          if (line.text.length > maxLine.length) maxLine = line.text;
1248
 
        });
1249
 
      }
1250
 
      changes.push({from: 0, to: doc.size});
1251
 
    }
1252
 
    function computeTabText() {
1253
 
      for (var str = '<span class="cm-tab">', i = 0; i < options.tabSize; ++i) str += " ";
1254
 
      return str + "</span>";
1255
 
    }
1256
 
    function tabsChanged() {
1257
 
      tabText = computeTabText();
1258
 
      updateDisplay(true);
1259
 
    }
1260
 
    function themeChanged() {
1261
 
      scroller.className = scroller.className.replace(/\s*cm-s-\w+/g, "") +
1262
 
        options.theme.replace(/(^|\s)\s*/g, " cm-s-");
1263
 
    }
1264
 
 
1265
 
    function TextMarker() { this.set = []; }
1266
 
    TextMarker.prototype.clear = operation(function() {
1267
 
      var min = Infinity, max = -Infinity;
1268
 
      for (var i = 0, e = this.set.length; i < e; ++i) {
1269
 
        var line = this.set[i], mk = line.marked;
1270
 
        if (!mk || !line.parent) continue;
1271
 
        var lineN = lineNo(line);
1272
 
        min = Math.min(min, lineN); max = Math.max(max, lineN);
1273
 
        for (var j = 0; j < mk.length; ++j)
1274
 
          if (mk[j].set == this.set) mk.splice(j--, 1);
1275
 
      }
1276
 
      if (min != Infinity)
1277
 
        changes.push({from: min, to: max + 1});
1278
 
    });
1279
 
    TextMarker.prototype.find = function() {
1280
 
      var from, to;
1281
 
      for (var i = 0, e = this.set.length; i < e; ++i) {
1282
 
        var line = this.set[i], mk = line.marked;
1283
 
        for (var j = 0; j < mk.length; ++j) {
1284
 
          var mark = mk[j];
1285
 
          if (mark.set == this.set) {
1286
 
            if (mark.from != null || mark.to != null) {
1287
 
              var found = lineNo(line);
1288
 
              if (found != null) {
1289
 
                if (mark.from != null) from = {line: found, ch: mark.from};
1290
 
                if (mark.to != null) to = {line: found, ch: mark.to};
1291
 
              }
1292
 
            }
1293
 
          }
1294
 
        }
1295
 
      }
1296
 
      return {from: from, to: to};
1297
 
    };
1298
 
 
1299
 
    function markText(from, to, className) {
1300
 
      from = clipPos(from); to = clipPos(to);
1301
 
      var tm = new TextMarker();
1302
 
      function add(line, from, to, className) {
1303
 
        getLine(line).addMark(new MarkedText(from, to, className, tm.set));
1304
 
      }
1305
 
      if (from.line == to.line) add(from.line, from.ch, to.ch, className);
1306
 
      else {
1307
 
        add(from.line, from.ch, null, className);
1308
 
        for (var i = from.line + 1, e = to.line; i < e; ++i)
1309
 
          add(i, null, null, className);
1310
 
        add(to.line, null, to.ch, className);
1311
 
      }
1312
 
      changes.push({from: from.line, to: to.line + 1});
1313
 
      return tm;
1314
 
    }
1315
 
 
1316
 
    function setBookmark(pos) {
1317
 
      pos = clipPos(pos);
1318
 
      var bm = new Bookmark(pos.ch);
1319
 
      getLine(pos.line).addMark(bm);
1320
 
      return bm;
1321
 
    }
1322
 
 
1323
 
    function addGutterMarker(line, text, className) {
1324
 
      if (typeof line == "number") line = getLine(clipLine(line));
1325
 
      line.gutterMarker = {text: text, style: className};
1326
 
      gutterDirty = true;
1327
 
      return line;
1328
 
    }
1329
 
    function removeGutterMarker(line) {
1330
 
      if (typeof line == "number") line = getLine(clipLine(line));
1331
 
      line.gutterMarker = null;
1332
 
      gutterDirty = true;
1333
 
    }
1334
 
 
1335
 
    function changeLine(handle, op) {
1336
 
      var no = handle, line = handle;
1337
 
      if (typeof handle == "number") line = getLine(clipLine(handle));
1338
 
      else no = lineNo(handle);
1339
 
      if (no == null) return null;
1340
 
      if (op(line, no)) changes.push({from: no, to: no + 1});
1341
 
      else return null;
1342
 
      return line;
1343
 
    }
1344
 
    function setLineClass(handle, className) {
1345
 
      return changeLine(handle, function(line) {
1346
 
        if (line.className != className) {
1347
 
          line.className = className;
1348
 
          return true;
1349
 
        }
1350
 
      });
1351
 
    }
1352
 
    function setLineHidden(handle, hidden) {
1353
 
      return changeLine(handle, function(line, no) {
1354
 
        if (line.hidden != hidden) {
1355
 
          line.hidden = hidden;
1356
 
          updateLineHeight(line, hidden ? 0 : 1);
1357
 
          var fline = sel.from.line, tline = sel.to.line;
1358
 
          if (hidden && (fline == no || tline == no)) {
1359
 
            var from = fline == no ? skipHidden({line: fline, ch: 0}, fline, 0) : sel.from;
1360
 
            var to = tline == no ? skipHidden({line: tline, ch: 0}, tline, 0) : sel.to;
1361
 
            setSelection(from, to);
1362
 
          }
1363
 
          return (gutterDirty = true);
1364
 
        }
1365
 
      });
1366
 
    }
1367
 
 
1368
 
    function lineInfo(line) {
1369
 
      if (typeof line == "number") {
1370
 
        if (!isLine(line)) return null;
1371
 
        var n = line;
1372
 
        line = getLine(line);
1373
 
        if (!line) return null;
1374
 
      }
1375
 
      else {
1376
 
        var n = lineNo(line);
1377
 
        if (n == null) return null;
1378
 
      }
1379
 
      var marker = line.gutterMarker;
1380
 
      return {line: n, handle: line, text: line.text, markerText: marker && marker.text,
1381
 
              markerClass: marker && marker.style, lineClass: line.className};
1382
 
    }
1383
 
 
1384
 
    function stringWidth(str) {
1385
 
      measure.innerHTML = "<pre><span>x</span></pre>";
1386
 
      measure.firstChild.firstChild.firstChild.nodeValue = str;
1387
 
      return measure.firstChild.firstChild.offsetWidth || 10;
1388
 
    }
1389
 
    // These are used to go from pixel positions to character
1390
 
    // positions, taking varying character widths into account.
1391
 
    function charFromX(line, x) {
1392
 
      if (x <= 0) return 0;
1393
 
      var lineObj = getLine(line), text = lineObj.text;
1394
 
      function getX(len) {
1395
 
        measure.innerHTML = "<pre><span>" + lineObj.getHTML(tabText, len) + "</span></pre>";
1396
 
        return measure.firstChild.firstChild.offsetWidth;
1397
 
      }
1398
 
      var from = 0, fromX = 0, to = text.length, toX;
1399
 
      // Guess a suitable upper bound for our search.
1400
 
      var estimated = Math.min(to, Math.ceil(x / charWidth()));
1401
 
      for (;;) {
1402
 
        var estX = getX(estimated);
1403
 
        if (estX <= x && estimated < to) estimated = Math.min(to, Math.ceil(estimated * 1.2));
1404
 
        else {toX = estX; to = estimated; break;}
1405
 
      }
1406
 
      if (x > toX) return to;
1407
 
      // Try to guess a suitable lower bound as well.
1408
 
      estimated = Math.floor(to * 0.8); estX = getX(estimated);
1409
 
      if (estX < x) {from = estimated; fromX = estX;}
1410
 
      // Do a binary search between these bounds.
1411
 
      for (;;) {
1412
 
        if (to - from <= 1) return (toX - x > x - fromX) ? from : to;
1413
 
        var middle = Math.ceil((from + to) / 2), middleX = getX(middle);
1414
 
        if (middleX > x) {to = middle; toX = middleX;}
1415
 
        else {from = middle; fromX = middleX;}
1416
 
      }
1417
 
    }
1418
 
 
1419
 
    var tempId = Math.floor(Math.random() * 0xffffff).toString(16);
1420
 
    function measureLine(line, ch) {
1421
 
      if (ch == 0) return {top: 0, left: 0};
1422
 
      var extra = "";
1423
 
      // Include extra text at the end to make sure the measured line is wrapped in the right way.
1424
 
      if (options.lineWrapping) {
1425
 
        var end = line.text.indexOf(" ", ch + 2);
1426
 
        extra = htmlEscape(line.text.slice(ch + 1, end < 0 ? line.text.length : end + (ie ? 5 : 0)));
1427
 
      }
1428
 
      measure.innerHTML = "<pre>" + line.getHTML(tabText, ch) +
1429
 
        '<span id="CodeMirror-temp-' + tempId + '">' + htmlEscape(line.text.charAt(ch) || " ") + "</span>" +
1430
 
        extra + "</pre>";
1431
 
      var elt = document.getElementById("CodeMirror-temp-" + tempId);
1432
 
      var top = elt.offsetTop, left = elt.offsetLeft;
1433
 
      // Older IEs report zero offsets for spans directly after a wrap
1434
 
      if (ie && top == 0 && left == 0) {
1435
 
        var backup = document.createElement("span");
1436
 
        backup.innerHTML = "x";
1437
 
        elt.parentNode.insertBefore(backup, elt.nextSibling);
1438
 
        top = backup.offsetTop;
1439
 
      }
1440
 
      return {top: top, left: left};
1441
 
    }
1442
 
    function localCoords(pos, inLineWrap) {
1443
 
      var x, lh = textHeight(), y = lh * (heightAtLine(doc, pos.line) - (inLineWrap ? displayOffset : 0));
1444
 
      if (pos.ch == 0) x = 0;
1445
 
      else {
1446
 
        var sp = measureLine(getLine(pos.line), pos.ch);
1447
 
        x = sp.left;
1448
 
        if (options.lineWrapping) y += Math.max(0, sp.top);
1449
 
      }
1450
 
      return {x: x, y: y, yBot: y + lh};
1451
 
    }
1452
 
    // Coords must be lineSpace-local
1453
 
    function coordsChar(x, y) {
1454
 
      if (y < 0) y = 0;
1455
 
      var th = textHeight(), cw = charWidth(), heightPos = displayOffset + Math.floor(y / th);
1456
 
      var lineNo = lineAtHeight(doc, heightPos);
1457
 
      if (lineNo >= doc.size) return {line: doc.size - 1, ch: getLine(doc.size - 1).text.length};
1458
 
      var lineObj = getLine(lineNo), text = lineObj.text;
1459
 
      var tw = options.lineWrapping, innerOff = tw ? heightPos - heightAtLine(doc, lineNo) : 0;
1460
 
      if (x <= 0 && innerOff == 0) return {line: lineNo, ch: 0};
1461
 
      function getX(len) {
1462
 
        var sp = measureLine(lineObj, len);
1463
 
        if (tw) {
1464
 
          var off = Math.round(sp.top / th);
1465
 
          return Math.max(0, sp.left + (off - innerOff) * scroller.clientWidth);
1466
 
        }
1467
 
        return sp.left;
1468
 
      }
1469
 
      var from = 0, fromX = 0, to = text.length, toX;
1470
 
      // Guess a suitable upper bound for our search.
1471
 
      var estimated = Math.min(to, Math.ceil((x + innerOff * scroller.clientWidth * .9) / cw));
1472
 
      for (;;) {
1473
 
        var estX = getX(estimated);
1474
 
        if (estX <= x && estimated < to) estimated = Math.min(to, Math.ceil(estimated * 1.2));
1475
 
        else {toX = estX; to = estimated; break;}
1476
 
      }
1477
 
      if (x > toX) return {line: lineNo, ch: to};
1478
 
      // Try to guess a suitable lower bound as well.
1479
 
      estimated = Math.floor(to * 0.8); estX = getX(estimated);
1480
 
      if (estX < x) {from = estimated; fromX = estX;}
1481
 
      // Do a binary search between these bounds.
1482
 
      for (;;) {
1483
 
        if (to - from <= 1) return {line: lineNo, ch: (toX - x > x - fromX) ? from : to};
1484
 
        var middle = Math.ceil((from + to) / 2), middleX = getX(middle);
1485
 
        if (middleX > x) {to = middle; toX = middleX;}
1486
 
        else {from = middle; fromX = middleX;}
1487
 
      }
1488
 
    }
1489
 
    function pageCoords(pos) {
1490
 
      var local = localCoords(pos, true), off = eltOffset(lineSpace);
1491
 
      return {x: off.left + local.x, y: off.top + local.y, yBot: off.top + local.yBot};
1492
 
    }
1493
 
 
1494
 
    var cachedHeight, cachedHeightFor, measureText;
1495
 
    function textHeight() {
1496
 
      if (measureText == null) {
1497
 
        measureText = "<pre>";
1498
 
        for (var i = 0; i < 49; ++i) measureText += "x<br/>";
1499
 
        measureText += "x</pre>";
1500
 
      }
1501
 
      var offsetHeight = lineDiv.clientHeight;
1502
 
      if (offsetHeight == cachedHeightFor) return cachedHeight;
1503
 
      cachedHeightFor = offsetHeight;
1504
 
      measure.innerHTML = measureText;
1505
 
      cachedHeight = measure.firstChild.offsetHeight / 50 || 1;
1506
 
      measure.innerHTML = "";
1507
 
      return cachedHeight;
1508
 
    }
1509
 
    var cachedWidth, cachedWidthFor = 0;
1510
 
    function charWidth() {
1511
 
      if (scroller.clientWidth == cachedWidthFor) return cachedWidth;
1512
 
      cachedWidthFor = scroller.clientWidth;
1513
 
      return (cachedWidth = stringWidth("x"));
1514
 
    }
1515
 
    function paddingTop() {return lineSpace.offsetTop;}
1516
 
    function paddingLeft() {return lineSpace.offsetLeft;}
1517
 
 
1518
 
    function posFromMouse(e, liberal) {
1519
 
      var offW = eltOffset(scroller, true), x, y;
1520
 
      // Fails unpredictably on IE[67] when mouse is dragged around quickly.
1521
 
      try { x = e.clientX; y = e.clientY; } catch (e) { return null; }
1522
 
      // This is a mess of a heuristic to try and determine whether a
1523
 
      // scroll-bar was clicked or not, and to return null if one was
1524
 
      // (and !liberal).
1525
 
      if (!liberal && (x - offW.left > scroller.clientWidth || y - offW.top > scroller.clientHeight))
1526
 
        return null;
1527
 
      var offL = eltOffset(lineSpace, true);
1528
 
      return coordsChar(x - offL.left, y - offL.top);
1529
 
    }
1530
 
    function onContextMenu(e) {
1531
 
      var pos = posFromMouse(e);
1532
 
      if (!pos || window.opera) return; // Opera is difficult.
1533
 
      if (posEq(sel.from, sel.to) || posLess(pos, sel.from) || !posLess(pos, sel.to))
1534
 
        operation(setCursor)(pos.line, pos.ch);
1535
 
 
1536
 
      var oldCSS = input.style.cssText;
1537
 
      inputDiv.style.position = "absolute";
1538
 
      input.style.cssText = "position: fixed; width: 30px; height: 30px; top: " + (e.clientY - 5) +
1539
 
        "px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: white; " +
1540
 
        "border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);";
1541
 
      leaveInputAlone = true;
1542
 
      var val = input.value = getSelection();
1543
 
      focusInput();
1544
 
      selectInput(input);
1545
 
      function rehide() {
1546
 
        var newVal = splitLines(input.value).join("\n");
1547
 
        if (newVal != val) operation(replaceSelection)(newVal, "end");
1548
 
        inputDiv.style.position = "relative";
1549
 
        input.style.cssText = oldCSS;
1550
 
        leaveInputAlone = false;
1551
 
        resetInput(true);
1552
 
        slowPoll();
1553
 
      }
1554
 
 
1555
 
      if (gecko) {
1556
 
        e_stop(e);
1557
 
        var mouseup = connect(window, "mouseup", function() {
1558
 
          mouseup();
1559
 
          setTimeout(rehide, 20);
1560
 
        }, true);
1561
 
      }
1562
 
      else {
1563
 
        setTimeout(rehide, 50);
1564
 
      }
1565
 
    }
1566
 
 
1567
 
    // Cursor-blinking
1568
 
    function restartBlink() {
1569
 
      clearInterval(blinker);
1570
 
      var on = true;
1571
 
      cursor.style.visibility = "";
1572
 
      blinker = setInterval(function() {
1573
 
        cursor.style.visibility = (on = !on) ? "" : "hidden";
1574
 
      }, 650);
1575
 
    }
1576
 
 
1577
 
    var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<"};
1578
 
    function matchBrackets(autoclear) {
1579
 
      var head = sel.inverted ? sel.from : sel.to, line = getLine(head.line), pos = head.ch - 1;
1580
 
      var match = (pos >= 0 && matching[line.text.charAt(pos)]) || matching[line.text.charAt(++pos)];
1581
 
      if (!match) return;
1582
 
      var ch = match.charAt(0), forward = match.charAt(1) == ">", d = forward ? 1 : -1, st = line.styles;
1583
 
      for (var off = pos + 1, i = 0, e = st.length; i < e; i+=2)
1584
 
        if ((off -= st[i].length) <= 0) {var style = st[i+1]; break;}
1585
 
 
1586
 
      var stack = [line.text.charAt(pos)], re = /[(){}[\]]/;
1587
 
      function scan(line, from, to) {
1588
 
        if (!line.text) return;
1589
 
        var st = line.styles, pos = forward ? 0 : line.text.length - 1, cur;
1590
 
        for (var i = forward ? 0 : st.length - 2, e = forward ? st.length : -2; i != e; i += 2*d) {
1591
 
          var text = st[i];
1592
 
          if (st[i+1] != null && st[i+1] != style) {pos += d * text.length; continue;}
1593
 
          for (var j = forward ? 0 : text.length - 1, te = forward ? text.length : -1; j != te; j += d, pos+=d) {
1594
 
            if (pos >= from && pos < to && re.test(cur = text.charAt(j))) {
1595
 
              var match = matching[cur];
1596
 
              if (match.charAt(1) == ">" == forward) stack.push(cur);
1597
 
              else if (stack.pop() != match.charAt(0)) return {pos: pos, match: false};
1598
 
              else if (!stack.length) return {pos: pos, match: true};
1599
 
            }
1600
 
          }
1601
 
        }
1602
 
      }
1603
 
      for (var i = head.line, e = forward ? Math.min(i + 100, doc.size) : Math.max(-1, i - 100); i != e; i+=d) {
1604
 
        var line = getLine(i), first = i == head.line;
1605
 
        var found = scan(line, first && forward ? pos + 1 : 0, first && !forward ? pos : line.text.length);
1606
 
        if (found) break;
1607
 
      }
1608
 
      if (!found) found = {pos: null, match: false};
1609
 
      var style = found.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket";
1610
 
      var one = markText({line: head.line, ch: pos}, {line: head.line, ch: pos+1}, style),
1611
 
          two = found.pos != null && markText({line: i, ch: found.pos}, {line: i, ch: found.pos + 1}, style);
1612
 
      var clear = operation(function(){one.clear(); two && two.clear();});
1613
 
      if (autoclear) setTimeout(clear, 800);
1614
 
      else bracketHighlighted = clear;
1615
 
    }
1616
 
 
1617
 
    // Finds the line to start with when starting a parse. Tries to
1618
 
    // find a line with a stateAfter, so that it can start with a
1619
 
    // valid state. If that fails, it returns the line with the
1620
 
    // smallest indentation, which tends to need the least context to
1621
 
    // parse correctly.
1622
 
    function findStartLine(n) {
1623
 
      var minindent, minline;
1624
 
      for (var search = n, lim = n - 40; search > lim; --search) {
1625
 
        if (search == 0) return 0;
1626
 
        var line = getLine(search-1);
1627
 
        if (line.stateAfter) return search;
1628
 
        var indented = line.indentation(options.tabSize);
1629
 
        if (minline == null || minindent > indented) {
1630
 
          minline = search - 1;
1631
 
          minindent = indented;
1632
 
        }
1633
 
      }
1634
 
      return minline;
1635
 
    }
1636
 
    function getStateBefore(n) {
1637
 
      var start = findStartLine(n), state = start && getLine(start-1).stateAfter;
1638
 
      if (!state) state = startState(mode);
1639
 
      else state = copyState(mode, state);
1640
 
      doc.iter(start, n, function(line) {
1641
 
        line.highlight(mode, state, options.tabSize);
1642
 
        line.stateAfter = copyState(mode, state);
1643
 
      });
1644
 
      if (start < n) changes.push({from: start, to: n});
1645
 
      if (n < doc.size && !getLine(n).stateAfter) work.push(n);
1646
 
      return state;
1647
 
    }
1648
 
    function highlightLines(start, end) {
1649
 
      var state = getStateBefore(start);
1650
 
      doc.iter(start, end, function(line) {
1651
 
        line.highlight(mode, state, options.tabSize);
1652
 
        line.stateAfter = copyState(mode, state);
1653
 
      });
1654
 
    }
1655
 
    function highlightWorker() {
1656
 
      var end = +new Date + options.workTime;
1657
 
      var foundWork = work.length;
1658
 
      while (work.length) {
1659
 
        if (!getLine(showingFrom).stateAfter) var task = showingFrom;
1660
 
        else var task = work.pop();
1661
 
        if (task >= doc.size) continue;
1662
 
        var start = findStartLine(task), state = start && getLine(start-1).stateAfter;
1663
 
        if (state) state = copyState(mode, state);
1664
 
        else state = startState(mode);
1665
 
 
1666
 
        var unchanged = 0, compare = mode.compareStates, realChange = false,
1667
 
            i = start, bail = false;
1668
 
        doc.iter(i, doc.size, function(line) {
1669
 
          var hadState = line.stateAfter;
1670
 
          if (+new Date > end) {
1671
 
            work.push(i);
1672
 
            startWorker(options.workDelay);
1673
 
            if (realChange) changes.push({from: task, to: i + 1});
1674
 
            return (bail = true);
1675
 
          }
1676
 
          var changed = line.highlight(mode, state, options.tabSize);
1677
 
          if (changed) realChange = true;
1678
 
          line.stateAfter = copyState(mode, state);
1679
 
          if (compare) {
1680
 
            if (hadState && compare(hadState, state)) return true;
1681
 
          } else {
1682
 
            if (changed !== false || !hadState) unchanged = 0;
1683
 
            else if (++unchanged > 3 && (!mode.indent || mode.indent(hadState, "") == mode.indent(state, "")))
1684
 
              return true;
1685
 
          }
1686
 
          ++i;
1687
 
        });
1688
 
        if (bail) return;
1689
 
        if (realChange) changes.push({from: task, to: i + 1});
1690
 
      }
1691
 
      if (foundWork && options.onHighlightComplete)
1692
 
        options.onHighlightComplete(instance);
1693
 
    }
1694
 
    function startWorker(time) {
1695
 
      if (!work.length) return;
1696
 
      highlight.set(time, operation(highlightWorker));
1697
 
    }
1698
 
 
1699
 
    // Operations are used to wrap changes in such a way that each
1700
 
    // change won't have to update the cursor and display (which would
1701
 
    // be awkward, slow, and error-prone), but instead updates are
1702
 
    // batched and then all combined and executed at once.
1703
 
    function startOperation() {
1704
 
      updateInput = userSelChange = textChanged = null;
1705
 
      changes = []; selectionChanged = false; callbacks = [];
1706
 
    }
1707
 
    function endOperation() {
1708
 
      var reScroll = false, updated;
1709
 
      if (selectionChanged) reScroll = !scrollCursorIntoView();
1710
 
      if (changes.length) updated = updateDisplay(changes, true);
1711
 
      else {
1712
 
        if (selectionChanged) updateSelection();
1713
 
        if (gutterDirty) updateGutter();
1714
 
      }
1715
 
      if (reScroll) scrollCursorIntoView();
1716
 
      if (selectionChanged) {scrollEditorIntoView(); restartBlink();}
1717
 
 
1718
 
      if (focused && !leaveInputAlone &&
1719
 
          (updateInput === true || (updateInput !== false && selectionChanged)))
1720
 
        resetInput(userSelChange);
1721
 
 
1722
 
      if (selectionChanged && options.matchBrackets)
1723
 
        setTimeout(operation(function() {
1724
 
          if (bracketHighlighted) {bracketHighlighted(); bracketHighlighted = null;}
1725
 
          if (posEq(sel.from, sel.to)) matchBrackets(false);
1726
 
        }), 20);
1727
 
      var tc = textChanged, cbs = callbacks; // these can be reset by callbacks
1728
 
      if (selectionChanged && options.onCursorActivity)
1729
 
        options.onCursorActivity(instance);
1730
 
      if (tc && options.onChange && instance)
1731
 
        options.onChange(instance, tc);
1732
 
      for (var i = 0; i < cbs.length; ++i) cbs[i](instance);
1733
 
      if (updated && options.onUpdate) options.onUpdate(instance);
1734
 
    }
1735
 
    var nestedOperation = 0;
1736
 
    function operation(f) {
1737
 
      return function() {
1738
 
        if (!nestedOperation++) startOperation();
1739
 
        try {var result = f.apply(this, arguments);}
1740
 
        finally {if (!--nestedOperation) endOperation();}
1741
 
        return result;
1742
 
      };
1743
 
    }
1744
 
 
1745
 
    for (var ext in extensions)
1746
 
      if (extensions.propertyIsEnumerable(ext) &&
1747
 
          !instance.propertyIsEnumerable(ext))
1748
 
        instance[ext] = extensions[ext];
1749
 
    return instance;
1750
 
  } // (end of function CodeMirror)
1751
 
 
1752
 
  // The default configuration options.
1753
 
  CodeMirror.defaults = {
1754
 
    value: "",
1755
 
    mode: null,
1756
 
    theme: "default",
1757
 
    indentUnit: 2,
1758
 
    indentWithTabs: false,
1759
 
    smartIndent: true,
1760
 
    tabSize: 4,
1761
 
    keyMap: "default",
1762
 
    extraKeys: null,
1763
 
    electricChars: true,
1764
 
    onKeyEvent: null,
1765
 
    lineWrapping: false,
1766
 
    lineNumbers: false,
1767
 
    gutter: false,
1768
 
    fixedGutter: false,
1769
 
    firstLineNumber: 1,
1770
 
    readOnly: false,
1771
 
    onChange: null,
1772
 
    onCursorActivity: null,
1773
 
    onGutterClick: null,
1774
 
    onHighlightComplete: null,
1775
 
    onUpdate: null,
1776
 
    onFocus: null, onBlur: null, onScroll: null,
1777
 
    matchBrackets: false,
1778
 
    workTime: 100,
1779
 
    workDelay: 200,
1780
 
    pollInterval: 100,
1781
 
    undoDepth: 40,
1782
 
    tabindex: null,
1783
 
    document: window.document
1784
 
  };
1785
 
 
1786
 
  var ios = /AppleWebKit/.test(navigator.userAgent) && /Mobile\/\w+/.test(navigator.userAgent);
1787
 
  var mac = ios || /Mac/.test(navigator.platform);
1788
 
  var win = /Win/.test(navigator.platform);
1789
 
 
1790
 
  // Known modes, by name and by MIME
1791
 
  var modes = {}, mimeModes = {};
1792
 
  CodeMirror.defineMode = function(name, mode) {
1793
 
    if (!CodeMirror.defaults.mode && name != "null") CodeMirror.defaults.mode = name;
1794
 
    modes[name] = mode;
1795
 
  };
1796
 
  CodeMirror.defineMIME = function(mime, spec) {
1797
 
    mimeModes[mime] = spec;
1798
 
  };
1799
 
  CodeMirror.getMode = function(options, spec) {
1800
 
    if (typeof spec == "string" && mimeModes.hasOwnProperty(spec))
1801
 
      spec = mimeModes[spec];
1802
 
    if (typeof spec == "string")
1803
 
      var mname = spec, config = {};
1804
 
    else if (spec != null)
1805
 
      var mname = spec.name, config = spec;
1806
 
    var mfactory = modes[mname];
1807
 
    if (!mfactory) {
1808
 
      if (window.console) console.warn("No mode " + mname + " found, falling back to plain text.");
1809
 
      return CodeMirror.getMode(options, "text/plain");
1810
 
    }
1811
 
    return mfactory(options, config || {});
1812
 
  };
1813
 
  CodeMirror.listModes = function() {
1814
 
    var list = [];
1815
 
    for (var m in modes)
1816
 
      if (modes.propertyIsEnumerable(m)) list.push(m);
1817
 
    return list;
1818
 
  };
1819
 
  CodeMirror.listMIMEs = function() {
1820
 
    var list = [];
1821
 
    for (var m in mimeModes)
1822
 
      if (mimeModes.propertyIsEnumerable(m)) list.push({mime: m, mode: mimeModes[m]});
1823
 
    return list;
1824
 
  };
1825
 
 
1826
 
  var extensions = CodeMirror.extensions = {};
1827
 
  CodeMirror.defineExtension = function(name, func) {
1828
 
    extensions[name] = func;
1829
 
  };
1830
 
 
1831
 
  var commands = CodeMirror.commands = {
1832
 
    selectAll: function(cm) {cm.setSelection({line: 0, ch: 0}, {line: cm.lineCount() - 1});},
1833
 
    killLine: function(cm) {
1834
 
      var from = cm.getCursor(true), to = cm.getCursor(false), sel = !posEq(from, to);
1835
 
      if (!sel && cm.getLine(from.line).length == from.ch) cm.replaceRange("", from, {line: from.line + 1, ch: 0});
1836
 
      else cm.replaceRange("", from, sel ? to : {line: from.line});
1837
 
    },
1838
 
    deleteLine: function(cm) {var l = cm.getCursor().line; cm.replaceRange("", {line: l, ch: 0}, {line: l});},
1839
 
    undo: function(cm) {cm.undo();},
1840
 
    redo: function(cm) {cm.redo();},
1841
 
    goDocStart: function(cm) {cm.setCursor(0, 0, true);},
1842
 
    goDocEnd: function(cm) {cm.setSelection({line: cm.lineCount() - 1}, null, true);},
1843
 
    goLineStart: function(cm) {cm.setCursor(cm.getCursor().line, 0, true);},
1844
 
    goLineStartSmart: function(cm) {
1845
 
      var cur = cm.getCursor();
1846
 
      var text = cm.getLine(cur.line), firstNonWS = Math.max(0, text.search(/\S/));
1847
 
      cm.setCursor(cur.line, cur.ch <= firstNonWS && cur.ch ? 0 : firstNonWS, true);
1848
 
    },
1849
 
    goLineEnd: function(cm) {cm.setSelection({line: cm.getCursor().line}, null, true);},
1850
 
    goLineUp: function(cm) {cm.moveV(-1, "line");},
1851
 
    goLineDown: function(cm) {cm.moveV(1, "line");},
1852
 
    goPageUp: function(cm) {cm.moveV(-1, "page");},
1853
 
    goPageDown: function(cm) {cm.moveV(1, "page");},
1854
 
    goCharLeft: function(cm) {cm.moveH(-1, "char");},
1855
 
    goCharRight: function(cm) {cm.moveH(1, "char");},
1856
 
    goColumnLeft: function(cm) {cm.moveH(-1, "column");},
1857
 
    goColumnRight: function(cm) {cm.moveH(1, "column");},
1858
 
    goWordLeft: function(cm) {cm.moveH(-1, "word");},
1859
 
    goWordRight: function(cm) {cm.moveH(1, "word");},
1860
 
    delCharLeft: function(cm) {cm.deleteH(-1, "char");},
1861
 
    delCharRight: function(cm) {cm.deleteH(1, "char");},
1862
 
    delWordLeft: function(cm) {cm.deleteH(-1, "word");},
1863
 
    delWordRight: function(cm) {cm.deleteH(1, "word");},
1864
 
    indentAuto: function(cm) {cm.indentSelection("smart");},
1865
 
    indentMore: function(cm) {cm.indentSelection("add");},
1866
 
    indentLess: function(cm) {cm.indentSelection("subtract");},
1867
 
    insertTab: function(cm) {cm.replaceSelection("\t", "end");},
1868
 
    transposeChars: function(cm) {
1869
 
      var cur = cm.getCursor(), line = cm.getLine(cur.line);
1870
 
      if (cur.ch > 0 && cur.ch < line.length - 1)
1871
 
        cm.replaceRange(line.charAt(cur.ch) + line.charAt(cur.ch - 1),
1872
 
                        {line: cur.line, ch: cur.ch - 1}, {line: cur.line, ch: cur.ch + 1});
1873
 
    },
1874
 
    newlineAndIndent: function(cm) {
1875
 
      cm.replaceSelection("\n", "end");
1876
 
      cm.indentLine(cm.getCursor().line);
1877
 
    },
1878
 
    toggleOverwrite: function(cm) {cm.toggleOverwrite();}
1879
 
  };
1880
 
 
1881
 
  var keyMap = CodeMirror.keyMap = {};
1882
 
  keyMap.basic = {
1883
 
    "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
1884
 
    "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
1885
 
    "Delete": "delCharRight", "Backspace": "delCharLeft", "Tab": "indentMore", "Shift-Tab": "indentLess",
1886
 
    "Enter": "newlineAndIndent", "Insert": "toggleOverwrite"
1887
 
  };
1888
 
  // Note that the save and find-related commands aren't defined by
1889
 
  // default. Unknown commands are simply ignored.
1890
 
  keyMap.pcDefault = {
1891
 
    "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
1892
 
    "Ctrl-Home": "goDocStart", "Alt-Up": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Down": "goDocEnd",
1893
 
    "Ctrl-Left": "goWordLeft", "Ctrl-Right": "goWordRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
1894
 
    "Ctrl-Backspace": "delWordLeft", "Ctrl-Delete": "delWordRight", "Ctrl-S": "save", "Ctrl-F": "find",
1895
 
    "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
1896
 
    fallthrough: "basic"
1897
 
  };
1898
 
  keyMap.macDefault = {
1899
 
    "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
1900
 
    "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goWordLeft",
1901
 
    "Alt-Right": "goWordRight", "Cmd-Left": "goLineStart", "Cmd-Right": "goLineEnd", "Alt-Backspace": "delWordLeft",
1902
 
    "Ctrl-Alt-Backspace": "delWordRight", "Alt-Delete": "delWordRight", "Cmd-S": "save", "Cmd-F": "find",
1903
 
    "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
1904
 
    fallthrough: ["basic", "emacsy"]
1905
 
  };
1906
 
  keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault;
1907
 
  keyMap.emacsy = {
1908
 
    "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
1909
 
    "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
1910
 
    "Ctrl-V": "goPageUp", "Shift-Ctrl-V": "goPageDown", "Ctrl-D": "delCharRight", "Ctrl-H": "delCharLeft",
1911
 
    "Alt-D": "delWordRight", "Alt-Backspace": "delWordLeft", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars"
1912
 
  };
1913
 
 
1914
 
  function lookupKey(name, extraMap, map) {
1915
 
    function lookup(name, map, ft) {
1916
 
      var found = map[name];
1917
 
      if (found != null) return found;
1918
 
      if (ft == null) ft = map.fallthrough;
1919
 
      if (ft == null) return map.catchall;
1920
 
      if (typeof ft == "string") return lookup(name, keyMap[ft]);
1921
 
      for (var i = 0, e = ft.length; i < e; ++i) {
1922
 
        found = lookup(name, keyMap[ft[i]]);
1923
 
        if (found != null) return found;
1924
 
      }
1925
 
      return null;
1926
 
    }
1927
 
    return extraMap ? lookup(name, extraMap, map) : lookup(name, keyMap[map]);
1928
 
  }
1929
 
  function isModifierKey(event) {
1930
 
    var name = keyNames[e_prop(event, "keyCode")];
1931
 
    return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod";
1932
 
  }
1933
 
 
1934
 
  CodeMirror.fromTextArea = function(textarea, options) {
1935
 
    if (!options) options = {};
1936
 
    options.value = textarea.value;
1937
 
    if (!options.tabindex && textarea.tabindex)
1938
 
      options.tabindex = textarea.tabindex;
1939
 
 
1940
 
    function save() {textarea.value = instance.getValue();}
1941
 
    if (textarea.form) {
1942
 
      // Deplorable hack to make the submit method do the right thing.
1943
 
      var rmSubmit = connect(textarea.form, "submit", save, true);
1944
 
      if (typeof textarea.form.submit == "function") {
1945
 
        var realSubmit = textarea.form.submit;
1946
 
        function wrappedSubmit() {
1947
 
          save();
1948
 
          textarea.form.submit = realSubmit;
1949
 
          textarea.form.submit();
1950
 
          textarea.form.submit = wrappedSubmit;
1951
 
        }
1952
 
        textarea.form.submit = wrappedSubmit;
1953
 
      }
1954
 
    }
1955
 
 
1956
 
    textarea.style.display = "none";
1957
 
    var instance = CodeMirror(function(node) {
1958
 
      textarea.parentNode.insertBefore(node, textarea.nextSibling);
1959
 
    }, options);
1960
 
    instance.save = save;
1961
 
    instance.getTextArea = function() { return textarea; };
1962
 
    instance.toTextArea = function() {
1963
 
      save();
1964
 
      textarea.parentNode.removeChild(instance.getWrapperElement());
1965
 
      textarea.style.display = "";
1966
 
      if (textarea.form) {
1967
 
        rmSubmit();
1968
 
        if (typeof textarea.form.submit == "function")
1969
 
          textarea.form.submit = realSubmit;
1970
 
      }
1971
 
    };
1972
 
    return instance;
1973
 
  };
1974
 
 
1975
 
  // Utility functions for working with state. Exported because modes
1976
 
  // sometimes need to do this.
1977
 
  function copyState(mode, state) {
1978
 
    if (state === true) return state;
1979
 
    if (mode.copyState) return mode.copyState(state);
1980
 
    var nstate = {};
1981
 
    for (var n in state) {
1982
 
      var val = state[n];
1983
 
      if (val instanceof Array) val = val.concat([]);
1984
 
      nstate[n] = val;
1985
 
    }
1986
 
    return nstate;
1987
 
  }
1988
 
  CodeMirror.copyState = copyState;
1989
 
  function startState(mode, a1, a2) {
1990
 
    return mode.startState ? mode.startState(a1, a2) : true;
1991
 
  }
1992
 
  CodeMirror.startState = startState;
1993
 
 
1994
 
  // The character stream used by a mode's parser.
1995
 
  function StringStream(string, tabSize) {
1996
 
    this.pos = this.start = 0;
1997
 
    this.string = string;
1998
 
    this.tabSize = tabSize || 8;
1999
 
  }
2000
 
  StringStream.prototype = {
2001
 
    eol: function() {return this.pos >= this.string.length;},
2002
 
    sol: function() {return this.pos == 0;},
2003
 
    peek: function() {return this.string.charAt(this.pos);},
2004
 
    next: function() {
2005
 
      if (this.pos < this.string.length)
2006
 
        return this.string.charAt(this.pos++);
2007
 
    },
2008
 
    eat: function(match) {
2009
 
      var ch = this.string.charAt(this.pos);
2010
 
      if (typeof match == "string") var ok = ch == match;
2011
 
      else var ok = ch && (match.test ? match.test(ch) : match(ch));
2012
 
      if (ok) {++this.pos; return ch;}
2013
 
    },
2014
 
    eatWhile: function(match) {
2015
 
      var start = this.pos;
2016
 
      while (this.eat(match)){}
2017
 
      return this.pos > start;
2018
 
    },
2019
 
    eatSpace: function() {
2020
 
      var start = this.pos;
2021
 
      while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) ++this.pos;
2022
 
      return this.pos > start;
2023
 
    },
2024
 
    skipToEnd: function() {this.pos = this.string.length;},
2025
 
    skipTo: function(ch) {
2026
 
      var found = this.string.indexOf(ch, this.pos);
2027
 
      if (found > -1) {this.pos = found; return true;}
2028
 
    },
2029
 
    backUp: function(n) {this.pos -= n;},
2030
 
    column: function() {return countColumn(this.string, this.start, this.tabSize);},
2031
 
    indentation: function() {return countColumn(this.string, null, this.tabSize);},
2032
 
    match: function(pattern, consume, caseInsensitive) {
2033
 
      if (typeof pattern == "string") {
2034
 
        function cased(str) {return caseInsensitive ? str.toLowerCase() : str;}
2035
 
        if (cased(this.string).indexOf(cased(pattern), this.pos) == this.pos) {
2036
 
          if (consume !== false) this.pos += pattern.length;
2037
 
          return true;
2038
 
        }
2039
 
      }
2040
 
      else {
2041
 
        var match = this.string.slice(this.pos).match(pattern);
2042
 
        if (match && consume !== false) this.pos += match[0].length;
2043
 
        return match;
2044
 
      }
2045
 
    },
2046
 
    current: function(){return this.string.slice(this.start, this.pos);}
2047
 
  };
2048
 
  CodeMirror.StringStream = StringStream;
2049
 
 
2050
 
  function MarkedText(from, to, className, set) {
2051
 
    this.from = from; this.to = to; this.style = className; this.set = set;
2052
 
  }
2053
 
  MarkedText.prototype = {
2054
 
    attach: function(line) { this.set.push(line); },
2055
 
    detach: function(line) {
2056
 
      var ix = indexOf(this.set, line);
2057
 
      if (ix > -1) this.set.splice(ix, 1);
2058
 
    },
2059
 
    split: function(pos, lenBefore) {
2060
 
      if (this.to <= pos && this.to != null) return null;
2061
 
      var from = this.from < pos || this.from == null ? null : this.from - pos + lenBefore;
2062
 
      var to = this.to == null ? null : this.to - pos + lenBefore;
2063
 
      return new MarkedText(from, to, this.style, this.set);
2064
 
    },
2065
 
    dup: function() { return new MarkedText(null, null, this.style, this.set); },
2066
 
    clipTo: function(fromOpen, from, toOpen, to, diff) {
2067
 
      if (this.from != null && this.from >= from)
2068
 
        this.from = Math.max(to, this.from) + diff;
2069
 
      if (this.to != null && this.to > from)
2070
 
        this.to = to < this.to ? this.to + diff : from;
2071
 
      if (fromOpen && to > this.from && (to < this.to || this.to == null))
2072
 
        this.from = null;
2073
 
      if (toOpen && (from < this.to || this.to == null) && (from > this.from || this.from == null))
2074
 
        this.to = null;
2075
 
    },
2076
 
    isDead: function() { return this.from != null && this.to != null && this.from >= this.to; },
2077
 
    sameSet: function(x) { return this.set == x.set; }
2078
 
  };
2079
 
 
2080
 
  function Bookmark(pos) {
2081
 
    this.from = pos; this.to = pos; this.line = null;
2082
 
  }
2083
 
  Bookmark.prototype = {
2084
 
    attach: function(line) { this.line = line; },
2085
 
    detach: function(line) { if (this.line == line) this.line = null; },
2086
 
    split: function(pos, lenBefore) {
2087
 
      if (pos < this.from) {
2088
 
        this.from = this.to = (this.from - pos) + lenBefore;
2089
 
        return this;
2090
 
      }
2091
 
    },
2092
 
    isDead: function() { return this.from > this.to; },
2093
 
    clipTo: function(fromOpen, from, toOpen, to, diff) {
2094
 
      if ((fromOpen || from < this.from) && (toOpen || to > this.to)) {
2095
 
        this.from = 0; this.to = -1;
2096
 
      } else if (this.from > from) {
2097
 
        this.from = this.to = Math.max(to, this.from) + diff;
2098
 
      }
2099
 
    },
2100
 
    sameSet: function(x) { return false; },
2101
 
    find: function() {
2102
 
      if (!this.line || !this.line.parent) return null;
2103
 
      return {line: lineNo(this.line), ch: this.from};
2104
 
    },
2105
 
    clear: function() {
2106
 
      if (this.line) {
2107
 
        var found = indexOf(this.line.marked, this);
2108
 
        if (found != -1) this.line.marked.splice(found, 1);
2109
 
        this.line = null;
2110
 
      }
2111
 
    }
2112
 
  };
2113
 
 
2114
 
  // Line objects. These hold state related to a line, including
2115
 
  // highlighting info (the styles array).
2116
 
  function Line(text, styles) {
2117
 
    this.styles = styles || [text, null];
2118
 
    this.text = text;
2119
 
    this.height = 1;
2120
 
    this.marked = this.gutterMarker = this.className = this.handlers = null;
2121
 
    this.stateAfter = this.parent = this.hidden = null;
2122
 
  }
2123
 
  Line.inheritMarks = function(text, orig) {
2124
 
    var ln = new Line(text), mk = orig && orig.marked;
2125
 
    if (mk) {
2126
 
      for (var i = 0; i < mk.length; ++i) {
2127
 
        if (mk[i].to == null && mk[i].style) {
2128
 
          var newmk = ln.marked || (ln.marked = []), mark = mk[i];
2129
 
          var nmark = mark.dup(); newmk.push(nmark); nmark.attach(ln);
2130
 
        }
2131
 
      }
2132
 
    }
2133
 
    return ln;
2134
 
  }
2135
 
  Line.prototype = {
2136
 
    // Replace a piece of a line, keeping the styles around it intact.
2137
 
    replace: function(from, to_, text) {
2138
 
      var st = [], mk = this.marked, to = to_ == null ? this.text.length : to_;
2139
 
      copyStyles(0, from, this.styles, st);
2140
 
      if (text) st.push(text, null);
2141
 
      copyStyles(to, this.text.length, this.styles, st);
2142
 
      this.styles = st;
2143
 
      this.text = this.text.slice(0, from) + text + this.text.slice(to);
2144
 
      this.stateAfter = null;
2145
 
      if (mk) {
2146
 
        var diff = text.length - (to - from);
2147
 
        for (var i = 0; i < mk.length; ++i) {
2148
 
          var mark = mk[i];
2149
 
          mark.clipTo(from == null, from || 0, to_ == null, to, diff);
2150
 
          if (mark.isDead()) {mark.detach(this); mk.splice(i--, 1);}
2151
 
        }
2152
 
      }
2153
 
    },
2154
 
    // Split a part off a line, keeping styles and markers intact.
2155
 
    split: function(pos, textBefore) {
2156
 
      var st = [textBefore, null], mk = this.marked;
2157
 
      copyStyles(pos, this.text.length, this.styles, st);
2158
 
      var taken = new Line(textBefore + this.text.slice(pos), st);
2159
 
      if (mk) {
2160
 
        for (var i = 0; i < mk.length; ++i) {
2161
 
          var mark = mk[i];
2162
 
          var newmark = mark.split(pos, textBefore.length);
2163
 
          if (newmark) {
2164
 
            if (!taken.marked) taken.marked = [];
2165
 
            taken.marked.push(newmark); newmark.attach(taken);
2166
 
          }
2167
 
        }
2168
 
      }
2169
 
      return taken;
2170
 
    },
2171
 
    append: function(line) {
2172
 
      var mylen = this.text.length, mk = line.marked, mymk = this.marked;
2173
 
      this.text += line.text;
2174
 
      copyStyles(0, line.text.length, line.styles, this.styles);
2175
 
      if (mymk) {
2176
 
        for (var i = 0; i < mymk.length; ++i)
2177
 
          if (mymk[i].to == null) mymk[i].to = mylen;
2178
 
      }
2179
 
      if (mk && mk.length) {
2180
 
        if (!mymk) this.marked = mymk = [];
2181
 
        outer: for (var i = 0; i < mk.length; ++i) {
2182
 
          var mark = mk[i];
2183
 
          if (!mark.from) {
2184
 
            for (var j = 0; j < mymk.length; ++j) {
2185
 
              var mymark = mymk[j];
2186
 
              if (mymark.to == mylen && mymark.sameSet(mark)) {
2187
 
                mymark.to = mark.to == null ? null : mark.to + mylen;
2188
 
                if (mymark.isDead()) {
2189
 
                  mymark.detach(this);
2190
 
                  mk.splice(i--, 1);
2191
 
                }
2192
 
                continue outer;
2193
 
              }
2194
 
            }
2195
 
          }
2196
 
          mymk.push(mark);
2197
 
          mark.attach(this);
2198
 
          mark.from += mylen;
2199
 
          if (mark.to != null) mark.to += mylen;
2200
 
        }
2201
 
      }
2202
 
    },
2203
 
    fixMarkEnds: function(other) {
2204
 
      var mk = this.marked, omk = other.marked;
2205
 
      if (!mk) return;
2206
 
      for (var i = 0; i < mk.length; ++i) {
2207
 
        var mark = mk[i], close = mark.to == null;
2208
 
        if (close && omk) {
2209
 
          for (var j = 0; j < omk.length; ++j)
2210
 
            if (omk[j].sameSet(mark)) {close = false; break;}
2211
 
        }
2212
 
        if (close) mark.to = this.text.length;
2213
 
      }
2214
 
    },
2215
 
    fixMarkStarts: function() {
2216
 
      var mk = this.marked;
2217
 
      if (!mk) return;
2218
 
      for (var i = 0; i < mk.length; ++i)
2219
 
        if (mk[i].from == null) mk[i].from = 0;
2220
 
    },
2221
 
    addMark: function(mark) {
2222
 
      mark.attach(this);
2223
 
      if (this.marked == null) this.marked = [];
2224
 
      this.marked.push(mark);
2225
 
      this.marked.sort(function(a, b){return (a.from || 0) - (b.from || 0);});
2226
 
    },
2227
 
    // Run the given mode's parser over a line, update the styles
2228
 
    // array, which contains alternating fragments of text and CSS
2229
 
    // classes.
2230
 
    highlight: function(mode, state, tabSize) {
2231
 
      var stream = new StringStream(this.text, tabSize), st = this.styles, pos = 0;
2232
 
      var changed = false, curWord = st[0], prevWord;
2233
 
      if (this.text == "" && mode.blankLine) mode.blankLine(state);
2234
 
      while (!stream.eol()) {
2235
 
        var style = mode.token(stream, state);
2236
 
        var substr = this.text.slice(stream.start, stream.pos);
2237
 
        stream.start = stream.pos;
2238
 
        if (pos && st[pos-1] == style)
2239
 
          st[pos-2] += substr;
2240
 
        else if (substr) {
2241
 
          if (!changed && (st[pos+1] != style || (pos && st[pos-2] != prevWord))) changed = true;
2242
 
          st[pos++] = substr; st[pos++] = style;
2243
 
          prevWord = curWord; curWord = st[pos];
2244
 
        }
2245
 
        // Give up when line is ridiculously long
2246
 
        if (stream.pos > 5000) {
2247
 
          st[pos++] = this.text.slice(stream.pos); st[pos++] = null;
2248
 
          break;
2249
 
        }
2250
 
      }
2251
 
      if (st.length != pos) {st.length = pos; changed = true;}
2252
 
      if (pos && st[pos-2] != prevWord) changed = true;
2253
 
      // Short lines with simple highlights return null, and are
2254
 
      // counted as changed by the driver because they are likely to
2255
 
      // highlight the same way in various contexts.
2256
 
      return changed || (st.length < 5 && this.text.length < 10 ? null : false);
2257
 
    },
2258
 
    // Fetch the parser token for a given character. Useful for hacks
2259
 
    // that want to inspect the mode state (say, for completion).
2260
 
    getTokenAt: function(mode, state, ch) {
2261
 
      var txt = this.text, stream = new StringStream(txt);
2262
 
      while (stream.pos < ch && !stream.eol()) {
2263
 
        stream.start = stream.pos;
2264
 
        var style = mode.token(stream, state);
2265
 
      }
2266
 
      return {start: stream.start,
2267
 
              end: stream.pos,
2268
 
              string: stream.current(),
2269
 
              className: style || null,
2270
 
              state: state};
2271
 
    },
2272
 
    indentation: function(tabSize) {return countColumn(this.text, null, tabSize);},
2273
 
    // Produces an HTML fragment for the line, taking selection,
2274
 
    // marking, and highlighting into account.
2275
 
    getHTML: function(tabText, endAt) {
2276
 
      var html = [], first = true;
2277
 
      function span(text, style) {
2278
 
        if (!text) return;
2279
 
        // Work around a bug where, in some compat modes, IE ignores leading spaces
2280
 
        if (first && ie && text.charAt(0) == " ") text = "\u00a0" + text.slice(1);
2281
 
        first = false;
2282
 
        if (style) html.push('<span class="', style, '">', htmlEscape(text).replace(/\t/g, tabText), "</span>");
2283
 
        else html.push(htmlEscape(text).replace(/\t/g, tabText));
2284
 
      }
2285
 
      var st = this.styles, allText = this.text, marked = this.marked;
2286
 
      var len = allText.length;
2287
 
      if (endAt != null) len = Math.min(endAt, len);
2288
 
      function styleToClass(style) {
2289
 
        if (!style) return null;
2290
 
        return "cm-" + style.replace(/ +/g, " cm-");
2291
 
      }
2292
 
 
2293
 
      if (!allText && endAt == null)
2294
 
        span(" ");
2295
 
      else if (!marked || !marked.length)
2296
 
        for (var i = 0, ch = 0; ch < len; i+=2) {
2297
 
          var str = st[i], style = st[i+1], l = str.length;
2298
 
          if (ch + l > len) str = str.slice(0, len - ch);
2299
 
          ch += l;
2300
 
          span(str, styleToClass(style));
2301
 
        }
2302
 
      else {
2303
 
        var pos = 0, i = 0, text = "", style, sg = 0;
2304
 
        var nextChange = marked[0].from || 0, marks = [], markpos = 0;
2305
 
        function advanceMarks() {
2306
 
          var m;
2307
 
          while (markpos < marked.length &&
2308
 
                 ((m = marked[markpos]).from == pos || m.from == null)) {
2309
 
            if (m.style != null) marks.push(m);
2310
 
            ++markpos;
2311
 
          }
2312
 
          nextChange = markpos < marked.length ? marked[markpos].from : Infinity;
2313
 
          for (var i = 0; i < marks.length; ++i) {
2314
 
            var to = marks[i].to || Infinity;
2315
 
            if (to == pos) marks.splice(i--, 1);
2316
 
            else nextChange = Math.min(to, nextChange);
2317
 
          }
2318
 
        }
2319
 
        var m = 0;
2320
 
        while (pos < len) {
2321
 
          if (nextChange == pos) advanceMarks();
2322
 
          var upto = Math.min(len, nextChange);
2323
 
          while (true) {
2324
 
            if (text) {
2325
 
              var end = pos + text.length;
2326
 
              var appliedStyle = style;
2327
 
              for (var j = 0; j < marks.length; ++j)
2328
 
                appliedStyle = (appliedStyle ? appliedStyle + " " : "") + marks[j].style;
2329
 
              span(end > upto ? text.slice(0, upto - pos) : text, appliedStyle);
2330
 
              if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;}
2331
 
              pos = end;
2332
 
            }
2333
 
            text = st[i++]; style = styleToClass(st[i++]);
2334
 
          }
2335
 
        }
2336
 
      }
2337
 
      return html.join("");
2338
 
    },
2339
 
    cleanUp: function() {
2340
 
      this.parent = null;
2341
 
      if (this.marked)
2342
 
        for (var i = 0, e = this.marked.length; i < e; ++i) this.marked[i].detach(this);
2343
 
    }
2344
 
  };
2345
 
  // Utility used by replace and split above
2346
 
  function copyStyles(from, to, source, dest) {
2347
 
    for (var i = 0, pos = 0, state = 0; pos < to; i+=2) {
2348
 
      var part = source[i], end = pos + part.length;
2349
 
      if (state == 0) {
2350
 
        if (end > from) dest.push(part.slice(from - pos, Math.min(part.length, to - pos)), source[i+1]);
2351
 
        if (end >= from) state = 1;
2352
 
      }
2353
 
      else if (state == 1) {
2354
 
        if (end > to) dest.push(part.slice(0, to - pos), source[i+1]);
2355
 
        else dest.push(part, source[i+1]);
2356
 
      }
2357
 
      pos = end;
2358
 
    }
2359
 
  }
2360
 
 
2361
 
  // Data structure that holds the sequence of lines.
2362
 
  function LeafChunk(lines) {
2363
 
    this.lines = lines;
2364
 
    this.parent = null;
2365
 
    for (var i = 0, e = lines.length, height = 0; i < e; ++i) {
2366
 
      lines[i].parent = this;
2367
 
      height += lines[i].height;
2368
 
    }
2369
 
    this.height = height;
2370
 
  }
2371
 
  LeafChunk.prototype = {
2372
 
    chunkSize: function() { return this.lines.length; },
2373
 
    remove: function(at, n, callbacks) {
2374
 
      for (var i = at, e = at + n; i < e; ++i) {
2375
 
        var line = this.lines[i];
2376
 
        this.height -= line.height;
2377
 
        line.cleanUp();
2378
 
        if (line.handlers)
2379
 
          for (var j = 0; j < line.handlers.length; ++j) callbacks.push(line.handlers[j]);
2380
 
      }
2381
 
      this.lines.splice(at, n);
2382
 
    },
2383
 
    collapse: function(lines) {
2384
 
      lines.splice.apply(lines, [lines.length, 0].concat(this.lines));
2385
 
    },
2386
 
    insertHeight: function(at, lines, height) {
2387
 
      this.height += height;
2388
 
      this.lines.splice.apply(this.lines, [at, 0].concat(lines));
2389
 
      for (var i = 0, e = lines.length; i < e; ++i) lines[i].parent = this;
2390
 
    },
2391
 
    iterN: function(at, n, op) {
2392
 
      for (var e = at + n; at < e; ++at)
2393
 
        if (op(this.lines[at])) return true;
2394
 
    }
2395
 
  };
2396
 
  function BranchChunk(children) {
2397
 
    this.children = children;
2398
 
    var size = 0, height = 0;
2399
 
    for (var i = 0, e = children.length; i < e; ++i) {
2400
 
      var ch = children[i];
2401
 
      size += ch.chunkSize(); height += ch.height;
2402
 
      ch.parent = this;
2403
 
    }
2404
 
    this.size = size;
2405
 
    this.height = height;
2406
 
    this.parent = null;
2407
 
  }
2408
 
  BranchChunk.prototype = {
2409
 
    chunkSize: function() { return this.size; },
2410
 
    remove: function(at, n, callbacks) {
2411
 
      this.size -= n;
2412
 
      for (var i = 0; i < this.children.length; ++i) {
2413
 
        var child = this.children[i], sz = child.chunkSize();
2414
 
        if (at < sz) {
2415
 
          var rm = Math.min(n, sz - at), oldHeight = child.height;
2416
 
          child.remove(at, rm, callbacks);
2417
 
          this.height -= oldHeight - child.height;
2418
 
          if (sz == rm) { this.children.splice(i--, 1); child.parent = null; }
2419
 
          if ((n -= rm) == 0) break;
2420
 
          at = 0;
2421
 
        } else at -= sz;
2422
 
      }
2423
 
      if (this.size - n < 25) {
2424
 
        var lines = [];
2425
 
        this.collapse(lines);
2426
 
        this.children = [new LeafChunk(lines)];
2427
 
        this.children[0].parent = this;
2428
 
      }
2429
 
    },
2430
 
    collapse: function(lines) {
2431
 
      for (var i = 0, e = this.children.length; i < e; ++i) this.children[i].collapse(lines);
2432
 
    },
2433
 
    insert: function(at, lines) {
2434
 
      var height = 0;
2435
 
      for (var i = 0, e = lines.length; i < e; ++i) height += lines[i].height;
2436
 
      this.insertHeight(at, lines, height);
2437
 
    },
2438
 
    insertHeight: function(at, lines, height) {
2439
 
      this.size += lines.length;
2440
 
      this.height += height;
2441
 
      for (var i = 0, e = this.children.length; i < e; ++i) {
2442
 
        var child = this.children[i], sz = child.chunkSize();
2443
 
        if (at <= sz) {
2444
 
          child.insertHeight(at, lines, height);
2445
 
          if (child.lines && child.lines.length > 50) {
2446
 
            while (child.lines.length > 50) {
2447
 
              var spilled = child.lines.splice(child.lines.length - 25, 25);
2448
 
              var newleaf = new LeafChunk(spilled);
2449
 
              child.height -= newleaf.height;
2450
 
              this.children.splice(i + 1, 0, newleaf);
2451
 
              newleaf.parent = this;
2452
 
            }
2453
 
            this.maybeSpill();
2454
 
          }
2455
 
          break;
2456
 
        }
2457
 
        at -= sz;
2458
 
      }
2459
 
    },
2460
 
    maybeSpill: function() {
2461
 
      if (this.children.length <= 10) return;
2462
 
      var me = this;
2463
 
      do {
2464
 
        var spilled = me.children.splice(me.children.length - 5, 5);
2465
 
        var sibling = new BranchChunk(spilled);
2466
 
        if (!me.parent) { // Become the parent node
2467
 
          var copy = new BranchChunk(me.children);
2468
 
          copy.parent = me;
2469
 
          me.children = [copy, sibling];
2470
 
          me = copy;
2471
 
        } else {
2472
 
          me.size -= sibling.size;
2473
 
          me.height -= sibling.height;
2474
 
          var myIndex = indexOf(me.parent.children, me);
2475
 
          me.parent.children.splice(myIndex + 1, 0, sibling);
2476
 
        }
2477
 
        sibling.parent = me.parent;
2478
 
      } while (me.children.length > 10);
2479
 
      me.parent.maybeSpill();
2480
 
    },
2481
 
    iter: function(from, to, op) { this.iterN(from, to - from, op); },
2482
 
    iterN: function(at, n, op) {
2483
 
      for (var i = 0, e = this.children.length; i < e; ++i) {
2484
 
        var child = this.children[i], sz = child.chunkSize();
2485
 
        if (at < sz) {
2486
 
          var used = Math.min(n, sz - at);
2487
 
          if (child.iterN(at, used, op)) return true;
2488
 
          if ((n -= used) == 0) break;
2489
 
          at = 0;
2490
 
        } else at -= sz;
2491
 
      }
2492
 
    }
2493
 
  };
2494
 
 
2495
 
  function getLineAt(chunk, n) {
2496
 
    while (!chunk.lines) {
2497
 
      for (var i = 0;; ++i) {
2498
 
        var child = chunk.children[i], sz = child.chunkSize();
2499
 
        if (n < sz) { chunk = child; break; }
2500
 
        n -= sz;
2501
 
      }
2502
 
    }
2503
 
    return chunk.lines[n];
2504
 
  }
2505
 
  function lineNo(line) {
2506
 
    if (line.parent == null) return null;
2507
 
    var cur = line.parent, no = indexOf(cur.lines, line);
2508
 
    for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {
2509
 
      for (var i = 0, e = chunk.children.length; ; ++i) {
2510
 
        if (chunk.children[i] == cur) break;
2511
 
        no += chunk.children[i].chunkSize();
2512
 
      }
2513
 
    }
2514
 
    return no;
2515
 
  }
2516
 
  function lineAtHeight(chunk, h) {
2517
 
    var n = 0;
2518
 
    outer: do {
2519
 
      for (var i = 0, e = chunk.children.length; i < e; ++i) {
2520
 
        var child = chunk.children[i], ch = child.height;
2521
 
        if (h < ch) { chunk = child; continue outer; }
2522
 
        h -= ch;
2523
 
        n += child.chunkSize();
2524
 
      }
2525
 
      return n;
2526
 
    } while (!chunk.lines);
2527
 
    for (var i = 0, e = chunk.lines.length; i < e; ++i) {
2528
 
      var line = chunk.lines[i], lh = line.height;
2529
 
      if (h < lh) break;
2530
 
      h -= lh;
2531
 
    }
2532
 
    return n + i;
2533
 
  }
2534
 
  function heightAtLine(chunk, n) {
2535
 
    var h = 0;
2536
 
    outer: do {
2537
 
      for (var i = 0, e = chunk.children.length; i < e; ++i) {
2538
 
        var child = chunk.children[i], sz = child.chunkSize();
2539
 
        if (n < sz) { chunk = child; continue outer; }
2540
 
        n -= sz;
2541
 
        h += child.height;
2542
 
      }
2543
 
      return h;
2544
 
    } while (!chunk.lines);
2545
 
    for (var i = 0; i < n; ++i) h += chunk.lines[i].height;
2546
 
    return h;
2547
 
  }
2548
 
 
2549
 
  // The history object 'chunks' changes that are made close together
2550
 
  // and at almost the same time into bigger undoable units.
2551
 
  function History() {
2552
 
    this.time = 0;
2553
 
    this.done = []; this.undone = [];
2554
 
  }
2555
 
  History.prototype = {
2556
 
    addChange: function(start, added, old) {
2557
 
      this.undone.length = 0;
2558
 
      var time = +new Date, cur = this.done[this.done.length - 1], last = cur && cur[cur.length - 1];
2559
 
      var dtime = time - this.time;
2560
 
      if (dtime > 400 || !last) {
2561
 
        this.done.push([{start: start, added: added, old: old}]);
2562
 
      } else if (last.start > start + added || last.start + last.added < start - last.added + last.old.length) {
2563
 
        cur.push({start: start, added: added, old: old});
2564
 
      } else {
2565
 
        var oldoff = 0;
2566
 
        if (start < last.start) {
2567
 
          for (var i = last.start - start - 1; i >= 0; --i)
2568
 
            last.old.unshift(old[i]);
2569
 
          last.added += last.start - start;
2570
 
          last.start = start;
2571
 
        }
2572
 
        else if (last.start < start) {
2573
 
          oldoff = start - last.start;
2574
 
          added += oldoff;
2575
 
        }
2576
 
        for (var i = last.added - oldoff, e = old.length; i < e; ++i)
2577
 
          last.old.push(old[i]);
2578
 
        if (last.added < added) last.added = added;
2579
 
      }
2580
 
      this.time = time;
2581
 
    }
2582
 
  };
2583
 
 
2584
 
  function stopMethod() {e_stop(this);}
2585
 
  // Ensure an event has a stop method.
2586
 
  function addStop(event) {
2587
 
    if (!event.stop) event.stop = stopMethod;
2588
 
    return event;
2589
 
  }
2590
 
 
2591
 
  function e_preventDefault(e) {
2592
 
    if (e.preventDefault) e.preventDefault();
2593
 
    else e.returnValue = false;
2594
 
  }
2595
 
  function e_stopPropagation(e) {
2596
 
    if (e.stopPropagation) e.stopPropagation();
2597
 
    else e.cancelBubble = true;
2598
 
  }
2599
 
  function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);}
2600
 
  CodeMirror.e_stop = e_stop;
2601
 
  CodeMirror.e_preventDefault = e_preventDefault;
2602
 
  CodeMirror.e_stopPropagation = e_stopPropagation;
2603
 
 
2604
 
  function e_target(e) {return e.target || e.srcElement;}
2605
 
  function e_button(e) {
2606
 
    if (e.which) return e.which;
2607
 
    else if (e.button & 1) return 1;
2608
 
    else if (e.button & 2) return 3;
2609
 
    else if (e.button & 4) return 2;
2610
 
  }
2611
 
 
2612
 
  // Allow 3rd-party code to override event properties by adding an override
2613
 
  // object to an event object.
2614
 
  function e_prop(e, prop) {
2615
 
    var overridden = e.override && e.override.hasOwnProperty(prop);
2616
 
    return overridden ? e.override[prop] : e[prop];
2617
 
  }
2618
 
 
2619
 
  // Event handler registration. If disconnect is true, it'll return a
2620
 
  // function that unregisters the handler.
2621
 
  function connect(node, type, handler, disconnect) {
2622
 
    if (typeof node.addEventListener == "function") {
2623
 
      node.addEventListener(type, handler, false);
2624
 
      if (disconnect) return function() {node.removeEventListener(type, handler, false);};
2625
 
    }
2626
 
    else {
2627
 
      var wrapHandler = function(event) {handler(event || window.event);};
2628
 
      node.attachEvent("on" + type, wrapHandler);
2629
 
      if (disconnect) return function() {node.detachEvent("on" + type, wrapHandler);};
2630
 
    }
2631
 
  }
2632
 
  CodeMirror.connect = connect;
2633
 
 
2634
 
  function Delayed() {this.id = null;}
2635
 
  Delayed.prototype = {set: function(ms, f) {clearTimeout(this.id); this.id = setTimeout(f, ms);}};
2636
 
 
2637
 
  // Detect drag-and-drop
2638
 
  var dragAndDrop = function() {
2639
 
    // IE8 has ondragstart and ondrop properties, but doesn't seem to
2640
 
    // actually support ondragstart the way it's supposed to work.
2641
 
    if (/MSIE [1-8]\b/.test(navigator.userAgent)) return false;
2642
 
    var div = document.createElement('div');
2643
 
    return "draggable" in div;
2644
 
  }();
2645
 
 
2646
 
  var gecko = /gecko\/\d{7}/i.test(navigator.userAgent);
2647
 
  var ie = /MSIE \d/.test(navigator.userAgent);
2648
 
  var webkit = /WebKit\//.test(navigator.userAgent);
2649
 
 
2650
 
  var lineSep = "\n";
2651
 
  // Feature-detect whether newlines in textareas are converted to \r\n
2652
 
  (function () {
2653
 
    var te = document.createElement("textarea");
2654
 
    te.value = "foo\nbar";
2655
 
    if (te.value.indexOf("\r") > -1) lineSep = "\r\n";
2656
 
  }());
2657
 
 
2658
 
  // Counts the column offset in a string, taking tabs into account.
2659
 
  // Used mostly to find indentation.
2660
 
  function countColumn(string, end, tabSize) {
2661
 
    if (end == null) {
2662
 
      end = string.search(/[^\s\u00a0]/);
2663
 
      if (end == -1) end = string.length;
2664
 
    }
2665
 
    for (var i = 0, n = 0; i < end; ++i) {
2666
 
      if (string.charAt(i) == "\t") n += tabSize - (n % tabSize);
2667
 
      else ++n;
2668
 
    }
2669
 
    return n;
2670
 
  }
2671
 
 
2672
 
  function computedStyle(elt) {
2673
 
    if (elt.currentStyle) return elt.currentStyle;
2674
 
    return window.getComputedStyle(elt, null);
2675
 
  }
2676
 
 
2677
 
  // Find the position of an element by following the offsetParent chain.
2678
 
  // If screen==true, it returns screen (rather than page) coordinates.
2679
 
  function eltOffset(node, screen) {
2680
 
    var bod = node.ownerDocument.body;
2681
 
    var x = 0, y = 0, skipBody = false;
2682
 
    for (var n = node; n; n = n.offsetParent) {
2683
 
      var ol = n.offsetLeft, ot = n.offsetTop;
2684
 
      // Firefox reports weird inverted offsets when the body has a border.
2685
 
      if (n == bod) { x += Math.abs(ol); y += Math.abs(ot); }
2686
 
      else { x += ol, y += ot; }
2687
 
      if (screen && computedStyle(n).position == "fixed")
2688
 
        skipBody = true;
2689
 
    }
2690
 
    var e = screen && !skipBody ? null : bod;
2691
 
    for (var n = node.parentNode; n != e; n = n.parentNode)
2692
 
      if (n.scrollLeft != null) { x -= n.scrollLeft; y -= n.scrollTop;}
2693
 
    return {left: x, top: y};
2694
 
  }
2695
 
  // Use the faster and saner getBoundingClientRect method when possible.
2696
 
  if (document.documentElement.getBoundingClientRect != null) eltOffset = function(node, screen) {
2697
 
    // Take the parts of bounding client rect that we are interested in so we are able to edit if need be,
2698
 
    // since the returned value cannot be changed externally (they are kept in sync as the element moves within the page)
2699
 
    try { var box = node.getBoundingClientRect(); box = { top: box.top, left: box.left }; }
2700
 
    catch(e) { box = {top: 0, left: 0}; }
2701
 
    if (!screen) {
2702
 
      // Get the toplevel scroll, working around browser differences.
2703
 
      if (window.pageYOffset == null) {
2704
 
        var t = document.documentElement || document.body.parentNode;
2705
 
        if (t.scrollTop == null) t = document.body;
2706
 
        box.top += t.scrollTop; box.left += t.scrollLeft;
2707
 
      } else {
2708
 
        box.top += window.pageYOffset; box.left += window.pageXOffset;
2709
 
      }
2710
 
    }
2711
 
    return box;
2712
 
  };
2713
 
 
2714
 
  // Get a node's text content.
2715
 
  function eltText(node) {
2716
 
    return node.textContent || node.innerText || node.nodeValue || "";
2717
 
  }
2718
 
  function selectInput(node) {
2719
 
    if (ios) { // Mobile Safari apparently has a bug where select() is broken.
2720
 
      node.selectionStart = 0;
2721
 
      node.selectionEnd = node.value.length;
2722
 
    } else node.select();
2723
 
  }
2724
 
 
2725
 
  // Operations on {line, ch} objects.
2726
 
  function posEq(a, b) {return a.line == b.line && a.ch == b.ch;}
2727
 
  function posLess(a, b) {return a.line < b.line || (a.line == b.line && a.ch < b.ch);}
2728
 
  function copyPos(x) {return {line: x.line, ch: x.ch};}
2729
 
 
2730
 
  var escapeElement = document.createElement("pre");
2731
 
  function htmlEscape(str) {
2732
 
    escapeElement.textContent = str;
2733
 
    return escapeElement.innerHTML;
2734
 
  }
2735
 
  // Recent (late 2011) Opera betas insert bogus newlines at the start
2736
 
  // of the textContent, so we strip those.
2737
 
  if (htmlEscape("a") == "\na")
2738
 
    htmlEscape = function(str) {
2739
 
      escapeElement.textContent = str;
2740
 
      return escapeElement.innerHTML.slice(1);
2741
 
    };
2742
 
  // Some IEs don't preserve tabs through innerHTML
2743
 
  else if (htmlEscape("\t") != "\t")
2744
 
    htmlEscape = function(str) {
2745
 
      escapeElement.innerHTML = "";
2746
 
      escapeElement.appendChild(document.createTextNode(str));
2747
 
      return escapeElement.innerHTML;
2748
 
    };
2749
 
  CodeMirror.htmlEscape = htmlEscape;
2750
 
 
2751
 
  // Used to position the cursor after an undo/redo by finding the
2752
 
  // last edited character.
2753
 
  function editEnd(from, to) {
2754
 
    if (!to) return 0;
2755
 
    if (!from) return to.length;
2756
 
    for (var i = from.length, j = to.length; i >= 0 && j >= 0; --i, --j)
2757
 
      if (from.charAt(i) != to.charAt(j)) break;
2758
 
    return j + 1;
2759
 
  }
2760
 
 
2761
 
  function indexOf(collection, elt) {
2762
 
    if (collection.indexOf) return collection.indexOf(elt);
2763
 
    for (var i = 0, e = collection.length; i < e; ++i)
2764
 
      if (collection[i] == elt) return i;
2765
 
    return -1;
2766
 
  }
2767
 
  function isWordChar(ch) {
2768
 
    return /\w/.test(ch) || ch.toUpperCase() != ch.toLowerCase();
2769
 
  }
2770
 
 
2771
 
  // See if "".split is the broken IE version, if so, provide an
2772
 
  // alternative way to split lines.
2773
 
  var splitLines = "\n\nb".split(/\n/).length != 3 ? function(string) {
2774
 
    var pos = 0, nl, result = [];
2775
 
    while ((nl = string.indexOf("\n", pos)) > -1) {
2776
 
      result.push(string.slice(pos, string.charAt(nl-1) == "\r" ? nl - 1 : nl));
2777
 
      pos = nl + 1;
2778
 
    }
2779
 
    result.push(string.slice(pos));
2780
 
    return result;
2781
 
  } : function(string){return string.split(/\r?\n/);};
2782
 
  CodeMirror.splitLines = splitLines;
2783
 
 
2784
 
  var hasSelection = window.getSelection ? function(te) {
2785
 
    try { return te.selectionStart != te.selectionEnd; }
2786
 
    catch(e) { return false; }
2787
 
  } : function(te) {
2788
 
    try {var range = te.ownerDocument.selection.createRange();}
2789
 
    catch(e) {}
2790
 
    if (!range || range.parentElement() != te) return false;
2791
 
    return range.compareEndPoints("StartToEnd", range) != 0;
2792
 
  };
2793
 
 
2794
 
  CodeMirror.defineMode("null", function() {
2795
 
    return {token: function(stream) {stream.skipToEnd();}};
2796
 
  });
2797
 
  CodeMirror.defineMIME("text/plain", "null");
2798
 
 
2799
 
  var keyNames = {3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
2800
 
                  19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
2801
 
                  36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
2802
 
                  46: "Delete", 59: ";", 91: "Mod", 92: "Mod", 93: "Mod", 186: ";", 187: "=", 188: ",",
2803
 
                  189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\", 221: "]", 222: "'", 63276: "PageUp",
2804
 
                  63277: "PageDown", 63275: "End", 63273: "Home", 63234: "Left", 63232: "Up", 63235: "Right",
2805
 
                  63233: "Down", 63302: "Insert", 63272: "Delete"};
2806
 
  CodeMirror.keyNames = keyNames;
2807
 
  (function() {
2808
 
    // Number keys
2809
 
    for (var i = 0; i < 10; i++) keyNames[i + 48] = String(i);
2810
 
    // Alphabetic keys
2811
 
    for (var i = 65; i <= 90; i++) keyNames[i] = String.fromCharCode(i);
2812
 
    // Function keys
2813
 
    for (var i = 1; i <= 12; i++) keyNames[i + 111] = keyNames[i + 63235] = "F" + i;
2814
 
  })();
2815
 
 
2816
 
  return CodeMirror;
2817
 
})();