You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3581 lines
155 KiB

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