WP Editor - Version 1.1.0.2

Version Description

  • Added ability to customize tab characters and size for all editors
  • Added ability to set custom editor heights for all editors
  • Updated CodeMirror library to 2.33
  • Updated CSS to work with new version of CodeMirror
  • Fixed issue with media button toolbar not inserting shortcodes/content when in visual mode
  • Fixed issue with blockquote QuickTag inserting twice
Download this release

Release Info

Developer benjaminprojas
Plugin Icon 128x128 WP Editor
Version 1.1.0.2
Comparing to
See all releases

Code changes from version 1.1.0.1 to 1.1.0.2

classes/WPEditorPosts.php CHANGED
@@ -13,7 +13,10 @@ class WPEditorPosts {
13
  'lineWrapping' => WPEditorSetting::getValue('enable_post_line_wrapping') == 1 ? true : false,
14
  'enterImgUrl' => __('Enter the URL of the image:', 'wpeditor'),
15
  'enterImgDescription' => __('Enter a description of the image:', 'wpeditor'),
16
- 'lookupWord' => __('Enter a word to look up:', 'wpeditor')
 
 
 
17
  );
18
  wp_enqueue_script('wp-editor-posts-jquery');
19
  wp_localize_script('wp-editor-posts-jquery', 'WPEPosts', $post_editor_settings);
13
  'lineWrapping' => WPEditorSetting::getValue('enable_post_line_wrapping') == 1 ? true : false,
14
  'enterImgUrl' => __('Enter the URL of the image:', 'wpeditor'),
15
  'enterImgDescription' => __('Enter a description of the image:', 'wpeditor'),
16
+ 'lookupWord' => __('Enter a word to look up:', 'wpeditor'),
17
+ 'tabSize' => WPEditorSetting::getValue('enable_post_tab_size') ? WPEditorSetting::getValue('enable_post_tab_size') : 4,
18
+ 'indentWithTabs' => WPEditorSetting::getValue('enable_post_tab_size') == 'tabs' ? true : false,
19
+ 'editorHeight' => WPEditorSetting::getValue('enable_post_editor_height') ? WPEditorSetting::getValue('enable_post_editor_height') : false
20
  );
21
  wp_enqueue_script('wp-editor-posts-jquery');
22
  wp_localize_script('wp-editor-posts-jquery', 'WPEPosts', $post_editor_settings);
extensions/codemirror/codemirror.css CHANGED
@@ -43,6 +43,36 @@ background-image: linear-gradient(bottom,#E3E3E3,white);
43
  is visible outside of the scrolling box. */
44
  position: relative;
45
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
  .CodeMirror-fullscreen {
47
  display:block;
48
  position:fixed !important;
43
  is visible outside of the scrolling box. */
44
  position: relative;
45
  }
46
+
47
+ /* Vertical scrollbar */
48
+ .CodeMirror-scrollbar {
49
+ position: absolute;
50
+ right: 0; top: 0;
51
+ overflow-x: hidden;
52
+ overflow-y: scroll;
53
+ z-index: 5;
54
+ }
55
+ .CodeMirror-scrollbar-inner {
56
+ /* This needs to have a nonzero width in order for the scrollbar to appear
57
+ in Firefox and IE9. */
58
+ width: 1px;
59
+ }
60
+ .CodeMirror-scrollbar.cm-sb-overlap {
61
+ /* Ensure that the scrollbar appears in Lion, and that it overlaps the content
62
+ rather than sitting to the right of it. */
63
+ position: absolute;
64
+ z-index: 1;
65
+ float: none;
66
+ right: 0;
67
+ min-width: 12px;
68
+ }
69
+ .CodeMirror-scrollbar.cm-sb-nonoverlap {
70
+ min-width: 12px;
71
+ }
72
+ .CodeMirror-scrollbar.cm-sb-ie7 {
73
+ min-width: 18px;
74
+ }
75
+
76
  .CodeMirror-fullscreen {
77
  display:block;
78
  position:fixed !important;
extensions/codemirror/js/codemirror.js CHANGED
@@ -1,12 +1,13 @@
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
 
7
  // CodeMirror is the only global var we claim
8
- var CodeMirror = (function() {
9
- // This is the function that produces an editor instance. It's
 
10
  // closure is used to store the editor state.
11
  function CodeMirror(place, givenOptions) {
12
  // Determine effective options based on given values and defaults.
@@ -15,42 +16,55 @@ var CodeMirror = (function() {
15
  if (defaults.hasOwnProperty(opt))
16
  options[opt] = (givenOptions && givenOptions.hasOwnProperty(opt) ? givenOptions : defaults)[opt];
17
 
18
- var targetDocument = options["document"];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  // The element in which the editor lives.
20
- var wrapper = targetDocument.createElement("div");
21
- wrapper.className = "CodeMirror" + (options.lineWrapping ? " CodeMirror-wrap" : "");
22
- // This mess creates the base DOM structure for the editor.
23
- wrapper.innerHTML =
24
- '<div style="overflow: hidden; position: relative; width: 3px; height: 0px;">' + // Wraps and hides input textarea
25
- '<textarea style="position: absolute; padding: 0; width: 1px;" wrap="off" ' +
26
- 'autocorrect="off" autocapitalize="off"></textarea></div>' +
27
- '<div class="CodeMirror-scroll" tabindex="-1">' +
28
- '<div style="position: relative">' + // Set to the height of the text, causes scrolling
29
- '<div style="position: relative">' + // Moved around its parent to cover visible view
30
- '<div class="CodeMirror-gutter"><div class="CodeMirror-gutter-text"></div></div>' +
31
- // Provides positioning relative to (visible) text origin
32
- '<div class="CodeMirror-lines"><div style="position: relative">' +
33
- '<div style="position: absolute; width: 100%; height: 0; overflow: hidden; visibility: hidden"></div>' +
34
- '<pre class="CodeMirror-cursor">&#160;</pre>' + // Absolutely positioned blinky cursor
35
- '<div></div>' + // This DIV contains the actual code
36
- '</div></div></div></div></div>';
37
  if (place.appendChild) place.appendChild(wrapper); else place(wrapper);
38
- // I've never seen more elegant code in my life.
39
- var inputDiv = wrapper.firstChild, input = inputDiv.firstChild,
40
- scroller = wrapper.lastChild, code = scroller.firstChild,
41
- mover = code.firstChild, gutter = mover.firstChild, gutterText = gutter.firstChild,
42
- lineSpace = gutter.nextSibling.firstChild, measure = lineSpace.firstChild,
43
- cursor = measure.nextSibling, lineDiv = cursor.nextSibling;
44
- themeChanged();
45
  // Needed to hide big blue blinking cursor on Mobile Safari
46
- if (/AppleWebKit/.test(navigator.userAgent) && /Mobile\/\w+/.test(navigator.userAgent)) input.style.width = "0px";
47
- if (!webkit) lineSpace.draggable = true;
 
48
  if (options.tabindex != null) input.tabIndex = options.tabindex;
 
49
  if (!options.gutter && !options.lineNumbers) gutter.style.display = "none";
 
 
 
 
 
 
 
 
 
 
50
 
51
  // Check for problem with IE innerHTML not working when we have a
52
  // P (or similar) parent node.
53
- try { stringWidth("x"); }
54
  catch (e) {
55
  if (e.message.match(/runtime/i))
56
  e = new Error("A CodeMirror inside a P-style element does not work in Internet Explorer. (innerHTML bug)");
@@ -71,19 +85,23 @@ var CodeMirror = (function() {
71
  var sel = {from: {line: 0, ch: 0}, to: {line: 0, ch: 0}, inverted: false};
72
  // Selection-related flags. shiftSelecting obviously tracks
73
  // whether the user is holding shift.
74
- var shiftSelecting, lastClick, lastDoubleClick, draggingText, overwrite = false;
 
75
  // Variables used by startOperation/endOperation to track what
76
  // happened during the operation.
77
  var updateInput, userSelChange, changes, textChanged, selectionChanged, leaveInputAlone,
78
  gutterDirty, callbacks;
79
  // Current visible range (may be bigger than the view window).
80
  var displayOffset = 0, showingFrom = 0, showingTo = 0, lastSizeC = 0;
81
- // bracketHighlighted is used to remember that a backet has been
82
  // marked.
83
  var bracketHighlighted;
84
  // Tracks the maximum line length so that the horizontal scrollbar
85
  // can be kept static when scrolling.
86
- var maxLine = "", maxWidth, tabText = computeTabText();
 
 
 
87
 
88
  // Initialize the content.
89
  operation(function(){setValue(options.value || ""); updateInput = false;})();
@@ -92,18 +110,18 @@ var CodeMirror = (function() {
92
  // Register our event handlers.
93
  connect(scroller, "mousedown", operation(onMouseDown));
94
  connect(scroller, "dblclick", operation(onDoubleClick));
95
- connect(lineSpace, "dragstart", onDragStart);
96
  connect(lineSpace, "selectstart", e_preventDefault);
97
  // Gecko browsers fire contextmenu *after* opening the menu, at
98
  // which point we can't mess with it anymore. Context menu is
99
  // handled in onMouseDown for Gecko.
100
  if (!gecko) connect(scroller, "contextmenu", onContextMenu);
101
- connect(scroller, "scroll", function() {
102
- updateDisplay([]);
103
- if (options.fixedGutter) gutter.style.left = scroller.scrollLeft + "px";
104
- if (options.onScroll) options.onScroll(instance);
105
- });
106
- connect(window, "resize", function() {updateDisplay(true);});
 
107
  connect(input, "keyup", operation(onKeyUp));
108
  connect(input, "input", fastPoll);
109
  connect(input, "keydown", operation(onKeyDown));
@@ -111,17 +129,32 @@ var CodeMirror = (function() {
111
  connect(input, "focus", onFocus);
112
  connect(input, "blur", onBlur);
113
 
114
- connect(scroller, "dragenter", e_stop);
115
- connect(scroller, "dragover", e_stop);
116
- connect(scroller, "drop", operation(onDrop));
 
 
 
 
 
 
 
117
  connect(scroller, "paste", function(){focusInput(); fastPoll();});
118
  connect(input, "paste", fastPoll);
119
- connect(input, "cut", operation(function(){replaceSelection("");}));
 
 
 
 
 
 
 
 
120
 
121
  // IE throws unspecified error in certain cases, when
122
  // trying to access activeElement before onload
123
- var hasFocus; try { hasFocus = (targetDocument.activeElement == input); } catch(e) { }
124
- if (hasFocus) setTimeout(onFocus, 20);
125
  else onBlur();
126
 
127
  function isLine(l) {return l >= 0 && l < doc.size;}
@@ -135,47 +168,71 @@ var CodeMirror = (function() {
135
  setValue: operation(setValue),
136
  getSelection: getSelection,
137
  replaceSelection: operation(replaceSelection),
138
- focus: function(){focusInput(); onFocus(); fastPoll();},
139
  setOption: function(option, value) {
140
  var oldVal = options[option];
141
  options[option] = value;
142
  if (option == "mode" || option == "indentUnit") loadMode();
143
- else if (option == "readOnly" && value) {onBlur(); input.blur();}
 
144
  else if (option == "theme") themeChanged();
145
  else if (option == "lineWrapping" && oldVal != value) operation(wrappingChanged)();
146
- else if (option == "tabSize") operation(tabsChanged)();
147
- if (option == "lineNumbers" || option == "gutter" || option == "firstLineNumber" || option == "theme")
148
- operation(gutterChanged)();
 
 
 
 
149
  },
150
  getOption: function(option) {return options[option];},
151
  undo: operation(undo),
152
  redo: operation(redo),
153
  indentLine: operation(function(n, dir) {
154
- if (isLine(n)) indentLine(n, dir == null ? "smart" : dir ? "add" : "subtract");
 
 
 
 
155
  }),
156
  indentSelection: operation(indentSelected),
157
  historySize: function() {return {undo: history.done.length, redo: history.undone.length};},
158
  clearHistory: function() {history = new History();},
 
 
 
 
 
 
 
 
 
159
  matchBrackets: operation(function(){matchBrackets(true);}),
160
  getTokenAt: operation(function(pos) {
161
  pos = clipPos(pos);
162
- return getLine(pos.line).getTokenAt(mode, getStateBefore(pos.line), pos.ch);
163
  }),
164
  getStateAfter: function(line) {
165
  line = clipLine(line == null ? doc.size - 1: line);
166
  return getStateBefore(line + 1);
167
  },
168
- cursorCoords: function(start){
169
  if (start == null) start = sel.inverted;
170
- return pageCoords(start ? sel.from : sel.to);
 
 
 
 
 
 
171
  },
172
- charCoords: function(pos){return pageCoords(clipPos(pos));},
173
  coordsChar: function(coords) {
174
  var off = eltOffset(lineSpace);
175
  return coordsChar(coords.x - off.left, coords.y - off.top);
176
  },
177
  markText: operation(markText),
178
  setBookmark: setBookmark,
 
179
  setMarker: operation(addGutterMarker),
180
  clearMarker: operation(removeGutterMarker),
181
  setLineClass: operation(setLineClass),
@@ -190,15 +247,16 @@ var CodeMirror = (function() {
190
  return line;
191
  },
192
  lineInfo: lineInfo,
 
193
  addWidget: function(pos, node, scroll, vert, horiz) {
194
  pos = localCoords(clipPos(pos));
195
  var top = pos.yBot, left = pos.x;
196
  node.style.position = "absolute";
197
- code.appendChild(node);
198
  if (vert == "over") top = pos.y;
199
  else if (vert == "near") {
200
  var vspace = Math.max(scroller.offsetHeight, doc.height * textHeight()),
201
- hspace = Math.max(code.clientWidth, lineSpace.clientWidth) - paddingLeft();
202
  if (pos.yBot + node.offsetHeight > vspace && pos.y > node.offsetHeight)
203
  top = pos.y - node.offsetHeight;
204
  if (left + node.offsetWidth > hspace)
@@ -207,11 +265,11 @@ var CodeMirror = (function() {
207
  node.style.top = (top + paddingTop()) + "px";
208
  node.style.left = node.style.right = "";
209
  if (horiz == "right") {
210
- left = code.clientWidth - node.offsetWidth;
211
  node.style.right = "0px";
212
  } else {
213
  if (horiz == "left") left = 0;
214
- else if (horiz == "middle") left = (code.clientWidth - node.offsetWidth) / 2;
215
  node.style.left = (left + paddingLeft()) + "px";
216
  }
217
  if (scroll)
@@ -241,14 +299,23 @@ var CodeMirror = (function() {
241
  if (isLine(line)) replaceRange("", {line: line, ch: 0}, clipPos({line: line+1, ch: 0}));
242
  }),
243
  replaceRange: operation(replaceRange),
244
- getRange: function(from, to) {return getRange(clipPos(from), clipPos(to));},
245
 
 
246
  execCommand: function(cmd) {return commands[cmd](instance);},
247
  // Stuff used by commands, probably not much use to outside code.
248
  moveH: operation(moveH),
249
  deleteH: operation(deleteH),
250
  moveV: operation(moveV),
251
- toggleOverwrite: function() {overwrite = !overwrite;},
 
 
 
 
 
 
 
 
252
 
253
  posFromIndex: function(off) {
254
  var lineNo = 0, ch;
@@ -268,9 +335,32 @@ var CodeMirror = (function() {
268
  });
269
  return index;
270
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
271
 
272
  operation: function(f){return operation(f)();},
273
- refresh: function(){updateDisplay(true);},
 
 
 
 
 
274
  getInputField: function(){return input;},
275
  getWrapperElement: function(){return wrapper;},
276
  getScrollerElement: function(){return scroller;},
@@ -290,17 +380,36 @@ var CodeMirror = (function() {
290
  splitLines(code), top, top);
291
  updateInput = true;
292
  }
293
- function getValue(code) {
294
  var text = [];
295
  doc.iter(0, doc.size, function(line) { text.push(line.text); });
296
- return text.join("\n");
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
297
  }
298
 
299
  function onMouseDown(e) {
300
- setShift(e.shiftKey);
301
  // Check whether this is a click in a widget
302
  for (var n = e_target(e); n != wrapper; n = n.parentNode)
303
- if (n.parentNode == code && n != mover) return;
304
 
305
  // See if this is a click in the gutter
306
  for (var n = e_target(e); n != wrapper; n = n.parentNode)
@@ -314,10 +423,12 @@ var CodeMirror = (function() {
314
 
315
  switch (e_button(e)) {
316
  case 3:
317
- if (gecko && !mac) onContextMenu(e);
318
  return;
319
  case 2:
320
  if (start) setCursor(start.line, start.ch, true);
 
 
321
  return;
322
  }
323
  // For button 1, if it was clicked inside the editor
@@ -327,44 +438,66 @@ var CodeMirror = (function() {
327
 
328
  if (!focused) onFocus();
329
 
330
- var now = +new Date;
331
  if (lastDoubleClick && lastDoubleClick.time > now - 400 && posEq(lastDoubleClick.pos, start)) {
 
332
  e_preventDefault(e);
333
  setTimeout(focusInput, 20);
334
- return selectLine(start.line);
335
  } else if (lastClick && lastClick.time > now - 400 && posEq(lastClick.pos, start)) {
 
336
  lastDoubleClick = {time: now, pos: start};
337
  e_preventDefault(e);
338
- return selectWordAt(start);
 
339
  } else { lastClick = {time: now, pos: start}; }
340
 
 
 
 
 
 
 
 
 
 
 
341
  var last = start, going;
342
- if (dragAndDrop && !posEq(sel.from, sel.to) &&
343
- !posLess(start, sel.from) && !posLess(sel.to, start)) {
344
  // Let the drag handler handle this.
345
- if (webkit) lineSpace.draggable = true;
346
- var up = connect(targetDocument, "mouseup", operation(function(e2) {
347
- if (webkit) lineSpace.draggable = false;
348
- draggingText = false;
349
- up();
350
- if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) {
351
- e_preventDefault(e2);
352
- setCursor(start.line, start.ch, true);
353
- focusInput();
354
- }
355
- }), true);
356
  draggingText = true;
 
 
357
  return;
358
  }
359
  e_preventDefault(e);
360
- setCursor(start.line, start.ch, true);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
361
 
362
  function extend(e) {
363
  var cur = posFromMouse(e, true);
364
  if (cur && !posEq(cur, last)) {
365
  if (!focused) onFocus();
366
  last = cur;
367
- setSelectionUser(start, cur);
368
  updateInput = false;
369
  var visible = visibleLines();
370
  if (cur.line >= visible.to || cur.line < visible.from)
@@ -372,36 +505,36 @@ var CodeMirror = (function() {
372
  }
373
  }
374
 
375
- var move = connect(targetDocument, "mousemove", operation(function(e) {
376
- clearTimeout(going);
377
- e_preventDefault(e);
378
- extend(e);
379
- }), true);
380
- var up = connect(targetDocument, "mouseup", operation(function(e) {
381
  clearTimeout(going);
382
  var cur = posFromMouse(e);
383
- if (cur) setSelectionUser(start, cur);
384
  e_preventDefault(e);
385
  focusInput();
386
  updateInput = true;
387
  move(); up();
 
 
 
 
 
 
388
  }), true);
 
389
  }
390
  function onDoubleClick(e) {
391
  for (var n = e_target(e); n != wrapper; n = n.parentNode)
392
  if (n.parentNode == gutterText) return e_preventDefault(e);
393
- var start = posFromMouse(e);
394
- if (!start) return;
395
- lastDoubleClick = {time: +new Date, pos: start};
396
  e_preventDefault(e);
397
- selectWordAt(start);
398
  }
399
  function onDrop(e) {
400
- e.preventDefault();
 
401
  var pos = posFromMouse(e, true), files = e.dataTransfer.files;
402
  if (!pos || options.readOnly) return;
403
  if (files && files.length && window.FileReader && window.File) {
404
- function loadFile(file, i) {
 
405
  var reader = new FileReader;
406
  reader.onload = function() {
407
  text[i] = reader.result;
@@ -414,19 +547,21 @@ var CodeMirror = (function() {
414
  }
415
  };
416
  reader.readAsText(file);
417
- }
418
- var n = files.length, text = Array(n), read = 0;
419
  for (var i = 0; i < n; ++i) loadFile(files[i], i);
420
- }
421
- else {
 
422
  try {
423
  var text = e.dataTransfer.getData("Text");
424
  if (text) {
425
- var end = replaceRange(text, pos, pos);
426
- var curFrom = sel.from, curTo = sel.to;
427
- setSelectionUser(pos, end);
428
- if (draggingText) replaceRange("", curFrom, curTo);
429
- focusInput();
 
 
430
  }
431
  }
432
  catch(e){}
@@ -434,80 +569,126 @@ var CodeMirror = (function() {
434
  }
435
  function onDragStart(e) {
436
  var txt = getSelection();
437
- // This will reset escapeElement
438
- htmlEscape(txt);
439
- e.dataTransfer.setDragImage(escapeElement, 0, 0);
440
  e.dataTransfer.setData("Text", txt);
441
- }
442
- function handleKeyBinding(e) {
443
- var name = keyNames[e.keyCode], next = keyMap[options.keyMap].auto, bound, dropShift;
444
- if (name == null || e.altGraphKey) {
445
- if (next) options.keyMap = next;
446
- return null;
447
- }
448
- if (e.altKey) name = "Alt-" + name;
449
- if (e.ctrlKey) name = "Ctrl-" + name;
450
- if (e.metaKey) name = "Cmd-" + name;
451
- if (e.shiftKey && (bound = lookupKey("Shift-" + name, options.extraKeys, options.keyMap))) {
452
- dropShift = true;
453
- } else {
454
- bound = lookupKey(name, options.extraKeys, options.keyMap);
455
  }
 
 
 
456
  if (typeof bound == "string") {
457
- if (commands.propertyIsEnumerable(bound)) bound = commands[bound];
458
- else bound = null;
459
- }
460
- if (next && (bound || !isModifierKey(e))) options.keyMap = next;
461
- if (!bound) return false;
462
- if (dropShift) {
463
- var prevShift = shiftSelecting;
464
- shiftSelecting = null;
465
  bound(instance);
 
 
 
 
466
  shiftSelecting = prevShift;
467
- } else bound(instance);
468
- e_preventDefault(e);
469
  return true;
470
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
471
  var lastStoppedKey = null;
472
  function onKeyDown(e) {
473
  if (!focused) onFocus();
474
- var code = e.keyCode;
 
 
 
475
  // IE does strange things with escape.
476
- if (ie && code == 27) { e.returnValue = false; }
477
- setShift(code == 16 || e.shiftKey);
478
  // First give onKeyEvent option a chance to handle this.
479
- if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return;
480
  var handled = handleKeyBinding(e);
481
- if (window.opera) {
482
- lastStoppedKey = handled ? e.keyCode : null;
483
  // Opera has no cut event... we try to at least catch the key combo
484
- if (!handled && (mac ? e.metaKey : e.ctrlKey) && e.keyCode == 88)
485
  replaceSelection("");
486
  }
487
  }
488
  function onKeyPress(e) {
489
- if (window.opera && e.keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;}
490
  if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return;
491
- if (window.opera && !e.which && handleKeyBinding(e)) return;
492
- if (options.electricChars && mode.electricChars) {
493
- var ch = String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode);
 
 
494
  if (mode.electricChars.indexOf(ch) > -1)
495
  setTimeout(operation(function() {indentLine(sel.to.line, "smart");}), 75);
496
  }
 
497
  fastPoll();
498
  }
499
  function onKeyUp(e) {
500
  if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return;
501
- if (e.keyCode == 16) shiftSelecting = null;
502
  }
503
 
504
  function onFocus() {
505
- if (options.readOnly) return;
506
  if (!focused) {
507
  if (options.onFocus) options.onFocus(instance);
508
  focused = true;
509
- if (wrapper.className.search(/\bCodeMirror-focused\b/) == -1)
510
- wrapper.className += " CodeMirror-focused";
511
  if (!leaveInputAlone) resetInput(true);
512
  }
513
  slowPoll();
@@ -517,7 +698,11 @@ var CodeMirror = (function() {
517
  if (focused) {
518
  if (options.onBlur) options.onBlur(instance);
519
  focused = false;
520
- wrapper.className = wrapper.className.replace(" CodeMirror-focused", "");
 
 
 
 
521
  }
522
  clearInterval(blinker);
523
  setTimeout(function() {if (!focused) shiftSelecting = null;}, 150);
@@ -526,6 +711,7 @@ var CodeMirror = (function() {
526
  // Replace the range from from to to by the strings in newText.
527
  // Afterwards, set the selection to selFrom, selTo.
528
  function updateLines(from, to, newText, selFrom, selTo) {
 
529
  if (history) {
530
  var old = [];
531
  doc.iter(from.line, to.line + 1, function(line) { old.push(line.text); });
@@ -535,25 +721,29 @@ var CodeMirror = (function() {
535
  updateLinesNoUndo(from, to, newText, selFrom, selTo);
536
  }
537
  function unredoHelper(from, to) {
538
- var change = from.pop();
539
- if (change) {
 
 
540
  var replaced = [], end = change.start + change.added;
541
  doc.iter(change.start, end, function(line) { replaced.push(line.text); });
542
- to.push({start: change.start, added: change.old.length, old: replaced});
543
- var pos = clipPos({line: change.start + change.old.length - 1,
544
- ch: editEnd(replaced[replaced.length-1], change.old[change.old.length-1])});
545
  updateLinesNoUndo({line: change.start, ch: 0}, {line: end - 1, ch: getLine(end-1).text.length}, change.old, pos, pos);
546
- updateInput = true;
547
  }
 
 
548
  }
549
  function undo() {unredoHelper(history.done, history.undone);}
550
  function redo() {unredoHelper(history.undone, history.done);}
551
 
552
  function updateLinesNoUndo(from, to, newText, selFrom, selTo) {
553
- var recomputeMaxLength = false, maxLineLength = maxLine.length;
 
554
  if (!options.lineWrapping)
555
- doc.iter(from.line, to.line, function(line) {
556
- if (line.text.length == maxLineLength) {recomputeMaxLength = true; return true;}
557
  });
558
  if (from.line != to.line || newText.length > 1) gutterDirty = true;
559
 
@@ -600,29 +790,21 @@ var CodeMirror = (function() {
600
  doc.insert(from.line + 1, added);
601
  }
602
  if (options.lineWrapping) {
603
- var perLine = scroller.clientWidth / charWidth() - 3;
604
  doc.iter(from.line, from.line + newText.length, function(line) {
605
  if (line.hidden) return;
606
  var guess = Math.ceil(line.text.length / perLine) || 1;
607
  if (guess != line.height) updateLineHeight(line, guess);
608
  });
609
  } else {
610
- doc.iter(from.line, i + newText.length, function(line) {
611
  var l = line.text;
612
- if (l.length > maxLineLength) {
613
- maxLine = l; maxLineLength = l.length; maxWidth = null;
614
  recomputeMaxLength = false;
615
  }
616
  });
617
- if (recomputeMaxLength) {
618
- maxLineLength = 0; maxLine = ""; maxWidth = null;
619
- doc.iter(0, doc.size, function(line) {
620
- var l = line.text;
621
- if (l.length > maxLineLength) {
622
- maxLineLength = l.length; maxLine = l;
623
- }
624
- });
625
- }
626
  }
627
 
628
  // Add these lines to the work array, so that they will be
@@ -648,10 +830,52 @@ var CodeMirror = (function() {
648
 
649
  // Update the selection
650
  function updateLine(n) {return n <= Math.min(to.line, to.line + lendiff) ? n : n + lendiff;}
651
- setSelection(selFrom, selTo, updateLine(sel.from.line), updateLine(sel.to.line));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
652
 
653
- // Make sure the scroll-size div has the correct height.
654
- code.style.height = (doc.height * textHeight() + 2 * paddingTop()) + "px";
 
 
 
 
 
 
 
 
655
  }
656
 
657
  function replaceRange(code, from, to) {
@@ -687,19 +911,18 @@ var CodeMirror = (function() {
687
  updateLines(from, to, code, newSel.from, newSel.to);
688
  }
689
 
690
- function getRange(from, to) {
691
  var l1 = from.line, l2 = to.line;
692
  if (l1 == l2) return getLine(l1).text.slice(from.ch, to.ch);
693
  var code = [getLine(l1).text.slice(from.ch)];
694
  doc.iter(l1 + 1, l2, function(line) { code.push(line.text); });
695
  code.push(getLine(l2).text.slice(0, to.ch));
696
- return code.join("\n");
697
  }
698
- function getSelection() {
699
- return getRange(sel.from, sel.to);
700
  }
701
 
702
- var pollingFast = false; // Ensures slowPoll doesn't cancel fastPoll
703
  function slowPoll() {
704
  if (pollingFast) return;
705
  poll.set(options.pollInterval, function() {
@@ -729,7 +952,7 @@ var CodeMirror = (function() {
729
  // supported or compatible enough yet to rely on.)
730
  var prevInput = "";
731
  function readInput() {
732
- if (leaveInputAlone || !focused || hasSelection(input)) return false;
733
  var text = input.value;
734
  if (text == prevInput) return false;
735
  shiftSelecting = null;
@@ -740,76 +963,95 @@ var CodeMirror = (function() {
740
  else if (overwrite && posEq(sel.from, sel.to))
741
  sel.to = {line: sel.to.line, ch: Math.min(getLine(sel.to.line).text.length, sel.to.ch + (text.length - same))};
742
  replaceSelection(text.slice(same), "end");
743
- prevInput = text;
 
744
  return true;
745
  }
746
  function resetInput(user) {
747
  if (!posEq(sel.from, sel.to)) {
748
  prevInput = "";
749
  input.value = getSelection();
750
- input.select();
751
  } else if (user) prevInput = input.value = "";
752
  }
753
 
754
  function focusInput() {
755
- if (!options.readOnly) input.focus();
756
  }
757
 
758
- function scrollEditorIntoView() {
759
- if (!cursor.getBoundingClientRect) return;
760
- var rect = cursor.getBoundingClientRect();
761
- // IE returns bogus coordinates when the instance sits inside of an iframe and the cursor is hidden
762
- if (ie && rect.top == rect.bottom) return;
763
- var winH = window.innerHeight || Math.max(document.body.offsetHeight, document.documentElement.offsetHeight);
764
- if (rect.top < 0 || rect.bottom > winH) cursor.scrollIntoView();
765
- }
766
  function scrollCursorIntoView() {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
767
  var cursor = localCoords(sel.inverted ? sel.from : sel.to);
768
  var x = options.lineWrapping ? Math.min(cursor.x, lineSpace.offsetWidth) : cursor.x;
769
- return scrollIntoView(x, cursor.y, x, cursor.yBot);
770
  }
771
  function scrollIntoView(x1, y1, x2, y2) {
772
- var pl = paddingLeft(), pt = paddingTop(), lh = textHeight();
 
 
 
 
 
773
  y1 += pt; y2 += pt; x1 += pl; x2 += pl;
774
- var screen = scroller.clientHeight, screentop = scroller.scrollTop, scrolled = false, result = true;
775
- if (y1 < screentop) {scroller.scrollTop = Math.max(0, y1 - 2*lh); scrolled = true;}
776
- else if (y2 > screentop + screen) {scroller.scrollTop = y2 + lh - screen; scrolled = true;}
 
 
777
 
778
  var screenw = scroller.clientWidth, screenleft = scroller.scrollLeft;
779
  var gutterw = options.fixedGutter ? gutter.clientWidth : 0;
780
- if (x1 < screenleft + gutterw) {
781
- if (x1 < 50) x1 = 0;
782
- scroller.scrollLeft = Math.max(0, x1 - 10 - gutterw);
783
- scrolled = true;
 
 
784
  }
785
- else if (x2 > screenw + screenleft - 3) {
786
- scroller.scrollLeft = x2 + 10 - screenw;
787
- scrolled = true;
788
- if (x2 > code.clientWidth) result = false;
789
- }
790
- if (scrolled && options.onScroll) options.onScroll(instance);
791
  return result;
792
  }
793
 
794
- function visibleLines() {
795
- var lh = textHeight(), top = scroller.scrollTop - paddingTop();
796
- var from_height = Math.max(0, Math.floor(top / lh));
797
- var to_height = Math.ceil((top + scroller.clientHeight) / lh);
798
- return {from: lineAtHeight(doc, from_height),
799
- to: lineAtHeight(doc, to_height)};
800
  }
801
  // Uses a set of changes plus the current scroll position to
802
  // determine which DOM updates have to be made, and makes the
803
  // updates.
804
- function updateDisplay(changes, suppressCallback) {
805
  if (!scroller.clientWidth) {
806
  showingFrom = showingTo = displayOffset = 0;
807
  return;
808
  }
809
  // Compute the new visible window
810
- var visible = visibleLines();
 
 
811
  // Bail out if the visible area is already rendered and nothing changed.
812
- if (changes !== true && changes.length == 0 && visible.from >= showingFrom && visible.to <= showingTo) return;
 
 
 
813
  var from = Math.max(visible.from - 100, 0), to = Math.min(doc.size, visible.to + 100);
814
  if (showingFrom < from && from - showingFrom < 20) from = showingFrom;
815
  if (showingTo > to && showingTo - to < 20) to = Math.min(doc.size, showingTo);
@@ -827,24 +1069,27 @@ var CodeMirror = (function() {
827
  if (range.from >= range.to) intact.splice(i--, 1);
828
  else intactLines += range.to - range.from;
829
  }
830
- if (intactLines == to - from) return;
 
 
 
831
  intact.sort(function(a, b) {return a.domStart - b.domStart;});
832
 
833
  var th = textHeight(), gutterDisplay = gutter.style.display;
834
- lineDiv.style.display = gutter.style.display = "none";
835
  patchDisplay(from, to, intact);
836
- lineDiv.style.display = "";
837
 
838
- // Position the mover div to align with the lines it's supposed
839
- // to be showing (which will cover the visible display)
840
  var different = from != showingFrom || to != showingTo || lastSizeC != scroller.clientHeight + th;
841
  // This is just a bogus formula that detects when the editor is
842
  // resized or the font size changes.
843
  if (different) lastSizeC = scroller.clientHeight + th;
 
 
 
 
844
  showingFrom = from; showingTo = to;
845
  displayOffset = heightAtLine(doc, from);
846
- mover.style.top = (displayOffset * th) + "px";
847
- code.style.height = (doc.height * th + 2 * paddingTop()) + "px";
848
 
849
  // Since this is all rather error prone, it is honoured with the
850
  // only assertion in the whole file.
@@ -852,30 +1097,34 @@ var CodeMirror = (function() {
852
  throw new Error("BAD PATCH! " + JSON.stringify(intact) + " size=" + (showingTo - showingFrom) +
853
  " nodes=" + lineDiv.childNodes.length);
854
 
855
- if (options.lineWrapping) {
856
- maxWidth = scroller.clientWidth;
857
- var curNode = lineDiv.firstChild;
858
  doc.iter(showingFrom, showingTo, function(line) {
 
 
 
 
859
  if (!line.hidden) {
860
  var height = Math.round(curNode.offsetHeight / th) || 1;
861
- if (line.height != height) {updateLineHeight(line, height); gutterDirty = true;}
 
 
 
862
  }
863
  curNode = curNode.nextSibling;
864
  });
865
- } else {
866
- if (maxWidth == null) maxWidth = stringWidth(maxLine);
867
- if (maxWidth > scroller.clientWidth) {
868
- lineSpace.style.width = maxWidth + "px";
869
- // Needed to prevent odd wrapping/hiding of widgets placed in here.
870
- code.style.width = "";
871
- code.style.width = scroller.scrollWidth + "px";
872
- } else {
873
- lineSpace.style.width = code.style.width = "";
874
- }
875
  }
 
 
 
876
  gutter.style.display = gutterDisplay;
877
- if (different || gutterDirty) updateGutter();
878
- updateCursor();
 
 
 
 
879
  if (!suppressCallback && options.onUpdate) options.onUpdate(instance);
880
  return true;
881
  }
@@ -904,14 +1153,14 @@ var CodeMirror = (function() {
904
  }
905
 
906
  function patchDisplay(from, to, intact) {
 
 
 
 
 
907
  // The first pass removes the DOM nodes that aren't intact.
908
- if (!intact.length) lineDiv.innerHTML = "";
909
  else {
910
- function killNode(node) {
911
- var tmp = node.nextSibling;
912
- node.parentNode.removeChild(node);
913
- return tmp;
914
- }
915
  var domPos = 0, curNode = lineDiv.firstChild, n;
916
  for (var i = 0; i < intact.length; ++i) {
917
  var cur = intact[i];
@@ -922,22 +1171,20 @@ var CodeMirror = (function() {
922
  }
923
  // This pass fills in the lines that actually changed.
924
  var nextIntact = intact.shift(), curNode = lineDiv.firstChild, j = from;
925
- var sfrom = sel.from.line, sto = sel.to.line, inSel = sfrom < from && sto >= from;
926
- var scratch = targetDocument.createElement("div"), newElt;
927
  doc.iter(from, to, function(line) {
928
- var ch1 = null, ch2 = null;
929
- if (inSel) {
930
- ch1 = 0;
931
- if (sto == j) {inSel = false; ch2 = sel.to.ch;}
932
- } else if (sfrom == j) {
933
- if (sto == j) {ch1 = sel.from.ch; ch2 = sel.to.ch;}
934
- else {inSel = true; ch1 = sel.from.ch;}
935
- }
936
  if (nextIntact && nextIntact.to == j) nextIntact = intact.shift();
937
  if (!nextIntact || nextIntact.from > j) {
938
- if (line.hidden) scratch.innerHTML = "<pre></pre>";
939
- else scratch.innerHTML = line.getHTML(ch1, ch2, true, tabText);
940
- lineDiv.insertBefore(scratch.firstChild, curNode);
 
 
 
 
 
 
 
 
941
  } else {
942
  curNode = curNode.nextSibling;
943
  }
@@ -949,44 +1196,79 @@ var CodeMirror = (function() {
949
  if (!options.gutter && !options.lineNumbers) return;
950
  var hText = mover.offsetHeight, hEditor = scroller.clientHeight;
951
  gutter.style.height = (hText - hEditor < 2 ? hEditor : hText) + "px";
952
- var html = [], i = showingFrom;
953
  doc.iter(showingFrom, Math.max(showingTo, showingFrom + 1), function(line) {
954
  if (line.hidden) {
955
- html.push("<pre></pre>");
956
  } else {
957
  var marker = line.gutterMarker;
958
- var text = options.lineNumbers ? i + options.firstLineNumber : null;
959
  if (marker && marker.text)
960
  text = marker.text.replace("%N%", text != null ? text : "");
961
  else if (text == null)
962
  text = "\u00a0";
963
- html.push((marker && marker.style ? '<pre class="' + marker.style + '">' : "<pre>"), text);
964
- for (var j = 1; j < line.height; ++j) html.push("<br/>&#160;");
965
- html.push("</pre>");
 
 
 
 
966
  }
967
  ++i;
968
  });
969
  gutter.style.display = "none";
970
- gutterText.innerHTML = html.join("");
971
- var minwidth = String(doc.size).length, firstNode = gutterText.firstChild, val = eltText(firstNode), pad = "";
972
- while (val.length + pad.length < minwidth) pad += "\u00a0";
973
- if (pad) firstNode.insertBefore(targetDocument.createTextNode(pad), firstNode.firstChild);
 
 
 
 
974
  gutter.style.display = "";
 
975
  lineSpace.style.marginLeft = gutter.offsetWidth + "px";
976
  gutterDirty = false;
 
977
  }
978
- function updateCursor() {
979
- var head = sel.inverted ? sel.from : sel.to, lh = textHeight();
980
- var pos = localCoords(head, true);
 
 
981
  var wrapOff = eltOffset(wrapper), lineOff = eltOffset(lineDiv);
982
- inputDiv.style.top = (pos.y + lineOff.top - wrapOff.top) + "px";
983
- inputDiv.style.left = (pos.x + lineOff.left - wrapOff.left) + "px";
984
- if (posEq(sel.from, sel.to)) {
985
- cursor.style.top = pos.y + "px";
986
- cursor.style.left = (options.lineWrapping ? Math.min(pos.x, lineSpace.offsetWidth) : pos.x) + "px";
987
  cursor.style.display = "";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
988
  }
989
- else cursor.style.display = "none";
990
  }
991
 
992
  function setShift(val) {
@@ -1012,37 +1294,32 @@ var CodeMirror = (function() {
1012
  if (posLess(to, from)) {var tmp = to; to = from; from = tmp;}
1013
 
1014
  // Skip over hidden lines.
1015
- if (from.line != oldFrom) from = skipHidden(from, oldFrom, sel.from.ch);
 
 
 
 
 
1016
  if (to.line != oldTo) to = skipHidden(to, oldTo, sel.to.ch);
1017
 
1018
  if (posEq(from, to)) sel.inverted = false;
1019
  else if (posEq(from, sel.to)) sel.inverted = false;
1020
  else if (posEq(to, sel.from)) sel.inverted = true;
1021
 
1022
- // Some ugly logic used to only mark the lines that actually did
1023
- // see a change in selection as changed, rather than the whole
1024
- // selected range.
1025
- if (posEq(from, to)) {
1026
- if (!posEq(sel.from, sel.to))
1027
- changes.push({from: oldFrom, to: oldTo + 1});
1028
- }
1029
- else if (posEq(sel.from, sel.to)) {
1030
- changes.push({from: from.line, to: to.line + 1});
1031
- }
1032
- else {
1033
- if (!posEq(from, sel.from)) {
1034
- if (from.line < oldFrom)
1035
- changes.push({from: from.line, to: Math.min(to.line, oldFrom) + 1});
1036
- else
1037
- changes.push({from: oldFrom, to: Math.min(oldTo, from.line) + 1});
1038
- }
1039
- if (!posEq(to, sel.to)) {
1040
- if (to.line < oldTo)
1041
- changes.push({from: Math.max(oldFrom, from.line), to: oldTo + 1});
1042
- else
1043
- changes.push({from: Math.max(from.line, oldTo), to: to.line + 1});
1044
  }
1045
  }
 
1046
  sel.from = from; sel.to = to;
1047
  selectionChanged = true;
1048
  }
@@ -1053,13 +1330,14 @@ var CodeMirror = (function() {
1053
  var line = getLine(lNo);
1054
  if (!line.hidden) {
1055
  var ch = pos.ch;
1056
- if (ch > oldCh || ch > line.text.length) ch = line.text.length;
1057
  return {line: lNo, ch: ch};
1058
  }
1059
  lNo += dir;
1060
  }
1061
  }
1062
  var line = getLine(pos.line);
 
1063
  if (!line.hidden) return pos;
1064
  if (pos.line >= oldLine) return getNonHidden(1) || getNonHidden(-1);
1065
  else return getNonHidden(-1) || getNonHidden(1);
@@ -1119,26 +1397,37 @@ var CodeMirror = (function() {
1119
  else replaceRange("", sel.from, findPosH(dir, unit));
1120
  userSelChange = true;
1121
  }
1122
- var goalColumn = null;
1123
  function moveV(dir, unit) {
1124
  var dist = 0, pos = localCoords(sel.inverted ? sel.from : sel.to, true);
1125
  if (goalColumn != null) pos.x = goalColumn;
1126
- if (unit == "page") dist = scroller.clientHeight;
1127
- else if (unit == "line") dist = textHeight();
1128
- var target = coordsChar(pos.x, pos.y + dist * dir + 2);
 
 
 
 
 
1129
  setCursor(target.line, target.ch, true);
1130
  goalColumn = pos.x;
1131
  }
1132
 
1133
- function selectWordAt(pos) {
1134
  var line = getLine(pos.line).text;
1135
  var start = pos.ch, end = pos.ch;
1136
- while (start > 0 && isWordChar(line.charAt(start - 1))) --start;
1137
- while (end < line.length && isWordChar(line.charAt(end))) ++end;
1138
- setSelectionUser({line: pos.line, ch: start}, {line: pos.line, ch: end});
 
 
 
 
 
 
 
1139
  }
1140
  function selectLine(line) {
1141
- setSelectionUser({line: line, ch: 0}, {line: line, ch: getLine(line).text.length});
1142
  }
1143
  function indentSelected(mode) {
1144
  if (posEq(sel.from, sel.to)) return indentLine(sel.from.line, mode);
@@ -1155,28 +1444,26 @@ var CodeMirror = (function() {
1155
 
1156
  var line = getLine(n), curSpace = line.indentation(options.tabSize),
1157
  curSpaceString = line.text.match(/^\s*/)[0], indentation;
 
 
 
 
1158
  if (how == "prev") {
1159
  if (n) indentation = getLine(n-1).indentation(options.tabSize);
1160
  else indentation = 0;
1161
  }
1162
- else if (how == "smart") indentation = mode.indent(state, line.text.slice(curSpaceString.length), line.text);
1163
  else if (how == "add") indentation = curSpace + options.indentUnit;
1164
  else if (how == "subtract") indentation = curSpace - options.indentUnit;
1165
  indentation = Math.max(0, indentation);
1166
  var diff = indentation - curSpace;
1167
 
1168
- if (!diff) {
1169
- if (sel.from.line != n && sel.to.line != n) return;
1170
- var indentString = curSpaceString;
1171
- }
1172
- else {
1173
- var indentString = "", pos = 0;
1174
- if (options.indentWithTabs)
1175
- for (var i = Math.floor(indentation / options.tabSize); i; --i) {pos += options.tabSize; indentString += "\t";}
1176
- while (pos < indentation) {++pos; indentString += " ";}
1177
- }
1178
 
1179
- replaceRange(indentString, {line: n, ch: 0}, {line: n, ch: curSpaceString.length});
 
1180
  }
1181
 
1182
  function loadMode() {
@@ -1200,29 +1487,32 @@ var CodeMirror = (function() {
1200
  var guess = Math.ceil(line.text.length / perLine) || 1;
1201
  if (guess != 1) updateLineHeight(line, guess);
1202
  });
1203
- lineSpace.style.width = code.style.width = "";
1204
  } else {
1205
  wrapper.className = wrapper.className.replace(" CodeMirror-wrap", "");
1206
- maxWidth = null; maxLine = "";
1207
  doc.iter(0, doc.size, function(line) {
1208
  if (line.height != 1 && !line.hidden) updateLineHeight(line, 1);
1209
- if (line.text.length > maxLine.length) maxLine = line.text;
1210
  });
1211
  }
1212
  changes.push({from: 0, to: doc.size});
1213
  }
1214
- function computeTabText() {
1215
- for (var str = '<span class="cm-tab">', i = 0; i < options.tabSize; ++i) str += " ";
1216
- return str + "</span>";
1217
- }
1218
- function tabsChanged() {
1219
- tabText = computeTabText();
1220
- updateDisplay(true);
1221
  }
1222
  function themeChanged() {
1223
- scroller.className = scroller.className.replace(/\s*cm-s-\w+/g, "") +
1224
  options.theme.replace(/(^|\s)\s*/g, " cm-s-");
1225
  }
 
 
 
 
 
1226
 
1227
  function TextMarker() { this.set = []; }
1228
  TextMarker.prototype.clear = operation(function() {
@@ -1233,7 +1523,7 @@ var CodeMirror = (function() {
1233
  var lineN = lineNo(line);
1234
  min = Math.min(min, lineN); max = Math.max(max, lineN);
1235
  for (var j = 0; j < mk.length; ++j)
1236
- if (mk[j].set == this.set) mk.splice(j--, 1);
1237
  }
1238
  if (min != Infinity)
1239
  changes.push({from: min, to: max + 1});
@@ -1244,7 +1534,7 @@ var CodeMirror = (function() {
1244
  var line = this.set[i], mk = line.marked;
1245
  for (var j = 0; j < mk.length; ++j) {
1246
  var mark = mk[j];
1247
- if (mark.set == this.set) {
1248
  if (mark.from != null || mark.to != null) {
1249
  var found = lineNo(line);
1250
  if (found != null) {
@@ -1261,8 +1551,9 @@ var CodeMirror = (function() {
1261
  function markText(from, to, className) {
1262
  from = clipPos(from); to = clipPos(to);
1263
  var tm = new TextMarker();
 
1264
  function add(line, from, to, className) {
1265
- getLine(line).addMark(new MarkedText(from, to, className, tm.set));
1266
  }
1267
  if (from.line == to.line) add(from.line, from.ch, to.ch, className);
1268
  else {
@@ -1282,6 +1573,19 @@ var CodeMirror = (function() {
1282
  return bm;
1283
  }
1284
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1285
  function addGutterMarker(line, text, className) {
1286
  if (typeof line == "number") line = getLine(clipLine(line));
1287
  line.gutterMarker = {text: text, style: className};
@@ -1303,10 +1607,11 @@ var CodeMirror = (function() {
1303
  else return null;
1304
  return line;
1305
  }
1306
- function setLineClass(handle, className) {
1307
  return changeLine(handle, function(line) {
1308
- if (line.className != className) {
1309
  line.className = className;
 
1310
  return true;
1311
  }
1312
  });
@@ -1315,10 +1620,22 @@ var CodeMirror = (function() {
1315
  return changeLine(handle, function(line, no) {
1316
  if (line.hidden != hidden) {
1317
  line.hidden = hidden;
 
 
 
 
 
 
 
1318
  updateLineHeight(line, hidden ? 0 : 1);
1319
- if (hidden && (sel.from.line == no || sel.to.line == no))
1320
- setSelection(skipHidden(sel.from, sel.from.line, sel.from.ch),
1321
- skipHidden(sel.to, sel.to.line, sel.to.ch));
 
 
 
 
 
1322
  return (gutterDirty = true);
1323
  }
1324
  });
@@ -1330,29 +1647,22 @@ var CodeMirror = (function() {
1330
  var n = line;
1331
  line = getLine(line);
1332
  if (!line) return null;
1333
- }
1334
- else {
1335
  var n = lineNo(line);
1336
  if (n == null) return null;
1337
  }
1338
  var marker = line.gutterMarker;
1339
  return {line: n, handle: line, text: line.text, markerText: marker && marker.text,
1340
- markerClass: marker && marker.style, lineClass: line.className};
1341
  }
1342
 
1343
- function stringWidth(str) {
1344
- measure.innerHTML = "<pre><span>x</span></pre>";
1345
- measure.firstChild.firstChild.firstChild.nodeValue = str;
1346
- return measure.firstChild.firstChild.offsetWidth || 10;
1347
- }
1348
  // These are used to go from pixel positions to character
1349
  // positions, taking varying character widths into account.
1350
  function charFromX(line, x) {
1351
  if (x <= 0) return 0;
1352
  var lineObj = getLine(line), text = lineObj.text;
1353
  function getX(len) {
1354
- measure.innerHTML = "<pre><span>" + lineObj.getHTML(null, null, false, tabText, len) + "</span></pre>";
1355
- return measure.firstChild.firstChild.offsetWidth;
1356
  }
1357
  var from = 0, fromX = 0, to = text.length, toX;
1358
  // Guess a suitable upper bound for our search.
@@ -1375,24 +1685,18 @@ var CodeMirror = (function() {
1375
  }
1376
  }
1377
 
1378
- var tempId = Math.floor(Math.random() * 0xffffff).toString(16);
1379
  function measureLine(line, ch) {
1380
- var extra = "";
1381
- // Include extra text at the end to make sure the measured line is wrapped in the right way.
1382
- if (options.lineWrapping) {
1383
- var end = line.text.indexOf(" ", ch + 2);
1384
- extra = htmlEscape(line.text.slice(ch + 1, end < 0 ? line.text.length : end + (ie ? 5 : 0)));
1385
- }
1386
- measure.innerHTML = "<pre>" + line.getHTML(null, null, false, tabText, ch) +
1387
- '<span id="CodeMirror-temp-' + tempId + '">' + htmlEscape(line.text.charAt(ch) || " ") + "</span>" +
1388
- extra + "</pre>";
1389
- var elt = document.getElementById("CodeMirror-temp-" + tempId);
1390
- var top = elt.offsetTop, left = elt.offsetLeft;
1391
  // Older IEs report zero offsets for spans directly after a wrap
1392
- if (ie && ch && top == 0 && left == 0) {
1393
- var backup = document.createElement("span");
1394
- backup.innerHTML = "x";
1395
- elt.parentNode.insertBefore(backup, elt.nextSibling);
1396
  top = backup.offsetTop;
1397
  }
1398
  return {top: top, left: left};
@@ -1409,17 +1713,19 @@ var CodeMirror = (function() {
1409
  }
1410
  // Coords must be lineSpace-local
1411
  function coordsChar(x, y) {
1412
- if (y < 0) y = 0;
1413
  var th = textHeight(), cw = charWidth(), heightPos = displayOffset + Math.floor(y / th);
 
1414
  var lineNo = lineAtHeight(doc, heightPos);
1415
  if (lineNo >= doc.size) return {line: doc.size - 1, ch: getLine(doc.size - 1).text.length};
1416
  var lineObj = getLine(lineNo), text = lineObj.text;
1417
  var tw = options.lineWrapping, innerOff = tw ? heightPos - heightAtLine(doc, lineNo) : 0;
1418
  if (x <= 0 && innerOff == 0) return {line: lineNo, ch: 0};
 
1419
  function getX(len) {
1420
  var sp = measureLine(lineObj, len);
1421
  if (tw) {
1422
  var off = Math.round(sp.top / th);
 
1423
  return Math.max(0, sp.left + (off - innerOff) * scroller.clientWidth);
1424
  }
1425
  return sp.left;
@@ -1438,9 +1744,12 @@ var CodeMirror = (function() {
1438
  if (estX < x) {from = estimated; fromX = estX;}
1439
  // Do a binary search between these bounds.
1440
  for (;;) {
1441
- if (to - from <= 1) return {line: lineNo, ch: (toX - x > x - fromX) ? from : to};
 
 
 
1442
  var middle = Math.ceil((from + to) / 2), middleX = getX(middle);
1443
- if (middleX > x) {to = middle; toX = middleX;}
1444
  else {from = middle; fromX = middleX;}
1445
  }
1446
  }
@@ -1449,26 +1758,32 @@ var CodeMirror = (function() {
1449
  return {x: off.left + local.x, y: off.top + local.y, yBot: off.top + local.yBot};
1450
  }
1451
 
1452
- var cachedHeight, cachedHeightFor, measureText;
1453
  function textHeight() {
1454
- if (measureText == null) {
1455
- measureText = "<pre>";
1456
- for (var i = 0; i < 49; ++i) measureText += "x<br/>";
1457
- measureText += "x</pre>";
 
 
 
1458
  }
1459
  var offsetHeight = lineDiv.clientHeight;
1460
  if (offsetHeight == cachedHeightFor) return cachedHeight;
1461
  cachedHeightFor = offsetHeight;
1462
- measure.innerHTML = measureText;
1463
  cachedHeight = measure.firstChild.offsetHeight / 50 || 1;
1464
- measure.innerHTML = "";
1465
  return cachedHeight;
1466
  }
1467
  var cachedWidth, cachedWidthFor = 0;
1468
  function charWidth() {
1469
  if (scroller.clientWidth == cachedWidthFor) return cachedWidth;
1470
  cachedWidthFor = scroller.clientWidth;
1471
- return (cachedWidth = stringWidth("x"));
 
 
 
1472
  }
1473
  function paddingTop() {return lineSpace.offsetTop;}
1474
  function paddingLeft() {return lineSpace.offsetLeft;}
@@ -1486,8 +1801,8 @@ var CodeMirror = (function() {
1486
  return coordsChar(x - offL.left, y - offL.top);
1487
  }
1488
  function onContextMenu(e) {
1489
- var pos = posFromMouse(e);
1490
- if (!pos || window.opera) return; // Opera is difficult.
1491
  if (posEq(sel.from, sel.to) || posLess(pos, sel.from) || !posLess(pos, sel.to))
1492
  operation(setCursor)(pos.line, pos.ch);
1493
 
@@ -1499,12 +1814,13 @@ var CodeMirror = (function() {
1499
  leaveInputAlone = true;
1500
  var val = input.value = getSelection();
1501
  focusInput();
1502
- input.select();
1503
  function rehide() {
1504
  var newVal = splitLines(input.value).join("\n");
1505
- if (newVal != val) operation(replaceSelection)(newVal, "end");
1506
  inputDiv.style.position = "relative";
1507
  input.style.cssText = oldCSS;
 
1508
  leaveInputAlone = false;
1509
  resetInput(true);
1510
  slowPoll();
@@ -1516,8 +1832,7 @@ var CodeMirror = (function() {
1516
  mouseup();
1517
  setTimeout(rehide, 20);
1518
  }, true);
1519
- }
1520
- else {
1521
  setTimeout(rehide, 50);
1522
  }
1523
  }
@@ -1529,7 +1844,7 @@ var CodeMirror = (function() {
1529
  cursor.style.visibility = "";
1530
  blinker = setInterval(function() {
1531
  cursor.style.visibility = (on = !on) ? "" : "hidden";
1532
- }, 650);
1533
  }
1534
 
1535
  var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<"};
@@ -1547,7 +1862,7 @@ var CodeMirror = (function() {
1547
  var st = line.styles, pos = forward ? 0 : line.text.length - 1, cur;
1548
  for (var i = forward ? 0 : st.length - 2, e = forward ? st.length : -2; i != e; i += 2*d) {
1549
  var text = st[i];
1550
- if (st[i+1] != null && st[i+1] != style) {pos += d * text.length; continue;}
1551
  for (var j = forward ? 0 : text.length - 1, te = forward ? text.length : -1; j != te; j += d, pos+=d) {
1552
  if (pos >= from && pos < to && re.test(cur = text.charAt(j))) {
1553
  var match = matching[cur];
@@ -1634,13 +1949,17 @@ var CodeMirror = (function() {
1634
  var changed = line.highlight(mode, state, options.tabSize);
1635
  if (changed) realChange = true;
1636
  line.stateAfter = copyState(mode, state);
 
1637
  if (compare) {
1638
- if (hadState && compare(hadState, state)) return true;
1639
- } else {
 
 
1640
  if (changed !== false || !hadState) unchanged = 0;
1641
  else if (++unchanged > 3 && (!mode.indent || mode.indent(hadState, "") == mode.indent(state, "")))
1642
- return true;
1643
  }
 
1644
  ++i;
1645
  });
1646
  if (bail) return;
@@ -1663,15 +1982,28 @@ var CodeMirror = (function() {
1663
  changes = []; selectionChanged = false; callbacks = [];
1664
  }
1665
  function endOperation() {
1666
- var reScroll = false, updated;
1667
- if (selectionChanged) reScroll = !scrollCursorIntoView();
1668
- if (changes.length) updated = updateDisplay(changes, true);
1669
- else {
1670
- if (selectionChanged) updateCursor();
 
 
 
 
 
 
 
 
 
 
 
 
 
1671
  if (gutterDirty) updateGutter();
1672
  }
1673
- if (reScroll) scrollCursorIntoView();
1674
- if (selectionChanged) {scrollEditorIntoView(); restartBlink();}
1675
 
1676
  if (focused && !leaveInputAlone &&
1677
  (updateInput === true || (updateInput !== false && selectionChanged)))
@@ -1682,11 +2014,11 @@ var CodeMirror = (function() {
1682
  if (bracketHighlighted) {bracketHighlighted(); bracketHighlighted = null;}
1683
  if (posEq(sel.from, sel.to)) matchBrackets(false);
1684
  }), 20);
1685
- var tc = textChanged, cbs = callbacks; // these can be reset by callbacks
1686
- if (selectionChanged && options.onCursorActivity)
 
 
1687
  options.onCursorActivity(instance);
1688
- if (tc && options.onChange && instance)
1689
- options.onChange(instance, tc);
1690
  for (var i = 0; i < cbs.length; ++i) cbs[i](instance);
1691
  if (updated && options.onUpdate) options.onUpdate(instance);
1692
  }
@@ -1700,6 +2032,11 @@ var CodeMirror = (function() {
1700
  };
1701
  }
1702
 
 
 
 
 
 
1703
  for (var ext in extensions)
1704
  if (extensions.propertyIsEnumerable(ext) &&
1705
  !instance.propertyIsEnumerable(ext))
@@ -1714,57 +2051,69 @@ var CodeMirror = (function() {
1714
  theme: "default",
1715
  indentUnit: 2,
1716
  indentWithTabs: false,
 
1717
  tabSize: 4,
1718
  keyMap: "default",
1719
  extraKeys: null,
1720
  electricChars: true,
 
1721
  onKeyEvent: null,
 
1722
  lineWrapping: false,
1723
  lineNumbers: false,
1724
  gutter: false,
1725
  fixedGutter: false,
1726
  firstLineNumber: 1,
1727
  readOnly: false,
 
1728
  onChange: null,
1729
  onCursorActivity: null,
 
1730
  onGutterClick: null,
1731
  onHighlightComplete: null,
1732
  onUpdate: null,
1733
  onFocus: null, onBlur: null, onScroll: null,
1734
  matchBrackets: false,
 
1735
  workTime: 100,
1736
  workDelay: 200,
1737
  pollInterval: 100,
1738
  undoDepth: 40,
1739
  tabindex: null,
1740
- document: window.document
 
1741
  };
1742
 
1743
- var mac = /Mac/.test(navigator.platform);
 
1744
  var win = /Win/.test(navigator.platform);
1745
 
1746
  // Known modes, by name and by MIME
1747
- var modes = {}, mimeModes = {};
1748
  CodeMirror.defineMode = function(name, mode) {
1749
  if (!CodeMirror.defaults.mode && name != "null") CodeMirror.defaults.mode = name;
 
 
 
 
1750
  modes[name] = mode;
1751
  };
1752
  CodeMirror.defineMIME = function(mime, spec) {
1753
  mimeModes[mime] = spec;
1754
  };
1755
- CodeMirror.getMode = function(options, spec) {
1756
  if (typeof spec == "string" && mimeModes.hasOwnProperty(spec))
1757
  spec = mimeModes[spec];
1758
- if (typeof spec == "string")
1759
- var mname = spec, config = {};
1760
- else if (spec != null)
1761
- var mname = spec.name, config = spec;
1762
- var mfactory = modes[mname];
1763
- if (!mfactory) {
1764
- if (window.console) console.warn("No mode " + mname + " found, falling back to plain text.");
1765
- return CodeMirror.getMode(options, "text/plain");
1766
- }
1767
- return mfactory(options, config || {});
1768
  };
1769
  CodeMirror.listModes = function() {
1770
  var list = [];
@@ -1821,6 +2170,10 @@ var CodeMirror = (function() {
1821
  indentMore: function(cm) {cm.indentSelection("add");},
1822
  indentLess: function(cm) {cm.indentSelection("subtract");},
1823
  insertTab: function(cm) {cm.replaceSelection("\t", "end");},
 
 
 
 
1824
  transposeChars: function(cm) {
1825
  var cur = cm.getCursor(), line = cm.getLine(cur.line);
1826
  if (cur.ch > 0 && cur.ch < line.length - 1)
@@ -1838,7 +2191,7 @@ var CodeMirror = (function() {
1838
  keyMap.basic = {
1839
  "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
1840
  "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
1841
- "Delete": "delCharRight", "Backspace": "delCharLeft", "Tab": "indentMore", "Shift-Tab": "indentLess",
1842
  "Enter": "newlineAndIndent", "Insert": "toggleOverwrite"
1843
  };
1844
  // Note that the save and find-related commands aren't defined by
@@ -1849,6 +2202,7 @@ var CodeMirror = (function() {
1849
  "Ctrl-Left": "goWordLeft", "Ctrl-Right": "goWordRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
1850
  "Ctrl-Backspace": "delWordLeft", "Ctrl-Delete": "delWordRight", "Ctrl-S": "save", "Ctrl-F": "find",
1851
  "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
 
1852
  fallthrough: "basic"
1853
  };
1854
  keyMap.macDefault = {
@@ -1857,6 +2211,7 @@ var CodeMirror = (function() {
1857
  "Alt-Right": "goWordRight", "Cmd-Left": "goLineStart", "Cmd-Right": "goLineEnd", "Alt-Backspace": "delWordLeft",
1858
  "Ctrl-Alt-Backspace": "delWordRight", "Alt-Delete": "delWordRight", "Cmd-S": "save", "Cmd-F": "find",
1859
  "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
 
1860
  fallthrough: ["basic", "emacsy"]
1861
  };
1862
  keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault;
@@ -1867,23 +2222,37 @@ var CodeMirror = (function() {
1867
  "Alt-D": "delWordRight", "Alt-Backspace": "delWordLeft", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars"
1868
  };
1869
 
1870
- function lookupKey(name, extraMap, map) {
1871
- function lookup(name, map, ft) {
 
 
 
 
 
1872
  var found = map[name];
1873
- if (found != null) return found;
1874
- if (ft == null) ft = map.fallthrough;
1875
- if (ft == null) return map.catchall;
1876
- if (typeof ft == "string") return lookup(name, keyMap[ft]);
1877
- for (var i = 0, e = ft.length; i < e; ++i) {
1878
- found = lookup(name, keyMap[ft[i]]);
1879
- if (found != null) return found;
 
 
 
 
 
 
 
 
1880
  }
1881
- return null;
1882
  }
1883
- return extraMap ? lookup(name, extraMap, map) : lookup(name, keyMap[map]);
 
1884
  }
1885
  function isModifierKey(event) {
1886
- var name = keyNames[event.keyCode];
1887
  return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod";
1888
  }
1889
 
@@ -1892,6 +2261,15 @@ var CodeMirror = (function() {
1892
  options.value = textarea.value;
1893
  if (!options.tabindex && textarea.tabindex)
1894
  options.tabindex = textarea.tabindex;
 
 
 
 
 
 
 
 
 
1895
 
1896
  function save() {textarea.value = instance.getValue();}
1897
  if (textarea.form) {
@@ -1899,13 +2277,12 @@ var CodeMirror = (function() {
1899
  var rmSubmit = connect(textarea.form, "submit", save, true);
1900
  if (typeof textarea.form.submit == "function") {
1901
  var realSubmit = textarea.form.submit;
1902
- function wrappedSubmit() {
1903
  save();
1904
  textarea.form.submit = realSubmit;
1905
  textarea.form.submit();
1906
  textarea.form.submit = wrappedSubmit;
1907
- }
1908
- textarea.form.submit = wrappedSubmit;
1909
  }
1910
  }
1911
 
@@ -1928,6 +2305,18 @@ var CodeMirror = (function() {
1928
  return instance;
1929
  };
1930
 
 
 
 
 
 
 
 
 
 
 
 
 
1931
  // Utility functions for working with state. Exported because modes
1932
  // sometimes need to do this.
1933
  function copyState(mode, state) {
@@ -1956,7 +2345,7 @@ var CodeMirror = (function() {
1956
  StringStream.prototype = {
1957
  eol: function() {return this.pos >= this.string.length;},
1958
  sol: function() {return this.pos == 0;},
1959
- peek: function() {return this.string.charAt(this.pos);},
1960
  next: function() {
1961
  if (this.pos < this.string.length)
1962
  return this.string.charAt(this.pos++);
@@ -1987,13 +2376,12 @@ var CodeMirror = (function() {
1987
  indentation: function() {return countColumn(this.string, null, this.tabSize);},
1988
  match: function(pattern, consume, caseInsensitive) {
1989
  if (typeof pattern == "string") {
1990
- function cased(str) {return caseInsensitive ? str.toLowerCase() : str;}
1991
  if (cased(this.string).indexOf(cased(pattern), this.pos) == this.pos) {
1992
  if (consume !== false) this.pos += pattern.length;
1993
  return true;
1994
  }
1995
- }
1996
- else {
1997
  var match = this.string.slice(this.pos).match(pattern);
1998
  if (match && consume !== false) this.pos += match[0].length;
1999
  return match;
@@ -2003,34 +2391,34 @@ var CodeMirror = (function() {
2003
  };
2004
  CodeMirror.StringStream = StringStream;
2005
 
2006
- function MarkedText(from, to, className, set) {
2007
- this.from = from; this.to = to; this.style = className; this.set = set;
2008
  }
2009
  MarkedText.prototype = {
2010
- attach: function(line) { this.set.push(line); },
2011
  detach: function(line) {
2012
- var ix = indexOf(this.set, line);
2013
- if (ix > -1) this.set.splice(ix, 1);
2014
  },
2015
  split: function(pos, lenBefore) {
2016
  if (this.to <= pos && this.to != null) return null;
2017
  var from = this.from < pos || this.from == null ? null : this.from - pos + lenBefore;
2018
  var to = this.to == null ? null : this.to - pos + lenBefore;
2019
- return new MarkedText(from, to, this.style, this.set);
2020
  },
2021
- dup: function() { return new MarkedText(null, null, this.style, this.set); },
2022
  clipTo: function(fromOpen, from, toOpen, to, diff) {
2023
- if (this.from != null && this.from >= from)
2024
- this.from = Math.max(to, this.from) + diff;
2025
- if (this.to != null && this.to > from)
2026
- this.to = to < this.to ? this.to + diff : from;
2027
  if (fromOpen && to > this.from && (to < this.to || this.to == null))
2028
  this.from = null;
 
 
2029
  if (toOpen && (from < this.to || this.to == null) && (from > this.from || this.from == null))
2030
  this.to = null;
 
 
2031
  },
2032
  isDead: function() { return this.from != null && this.to != null && this.from >= this.to; },
2033
- sameSet: function(x) { return this.set == x.set; }
2034
  };
2035
 
2036
  function Bookmark(pos) {
@@ -2067,14 +2455,22 @@ var CodeMirror = (function() {
2067
  }
2068
  };
2069
 
 
 
 
 
 
 
 
 
 
 
2070
  // Line objects. These hold state related to a line, including
2071
  // highlighting info (the styles array).
2072
  function Line(text, styles) {
2073
  this.styles = styles || [text, null];
2074
  this.text = text;
2075
  this.height = 1;
2076
- this.marked = this.gutterMarker = this.className = this.handlers = null;
2077
- this.stateAfter = this.parent = this.hidden = null;
2078
  }
2079
  Line.inheritMarks = function(text, orig) {
2080
  var ln = new Line(text), mk = orig && orig.marked;
@@ -2087,7 +2483,7 @@ var CodeMirror = (function() {
2087
  }
2088
  }
2089
  return ln;
2090
- }
2091
  Line.prototype = {
2092
  // Replace a piece of a line, keeping the styles around it intact.
2093
  replace: function(from, to_, text) {
@@ -2100,7 +2496,8 @@ var CodeMirror = (function() {
2100
  this.stateAfter = null;
2101
  if (mk) {
2102
  var diff = text.length - (to - from);
2103
- for (var i = 0, mark = mk[i]; i < mk.length; ++i) {
 
2104
  mark.clipTo(from == null, from || 0, to_ == null, to, diff);
2105
  if (mark.isDead()) {mark.detach(this); mk.splice(i--, 1);}
2106
  }
@@ -2118,6 +2515,7 @@ var CodeMirror = (function() {
2118
  if (newmark) {
2119
  if (!taken.marked) taken.marked = [];
2120
  taken.marked.push(newmark); newmark.attach(taken);
 
2121
  }
2122
  }
2123
  }
@@ -2158,11 +2556,20 @@ var CodeMirror = (function() {
2158
  fixMarkEnds: function(other) {
2159
  var mk = this.marked, omk = other.marked;
2160
  if (!mk) return;
2161
- for (var i = 0; i < mk.length; ++i) {
2162
  var mark = mk[i], close = mark.to == null;
2163
  if (close && omk) {
2164
- for (var j = 0; j < omk.length; ++j)
2165
- if (omk[j].sameSet(mark)) {close = false; break;}
 
 
 
 
 
 
 
 
 
2166
  }
2167
  if (close) mark.to = this.text.length;
2168
  }
@@ -2212,8 +2619,8 @@ var CodeMirror = (function() {
2212
  },
2213
  // Fetch the parser token for a given character. Useful for hacks
2214
  // that want to inspect the mode state (say, for completion).
2215
- getTokenAt: function(mode, state, ch) {
2216
- var txt = this.text, stream = new StringStream(txt);
2217
  while (stream.pos < ch && !stream.eol()) {
2218
  stream.start = stream.pos;
2219
  var style = mode.token(stream, state);
@@ -2227,74 +2634,126 @@ var CodeMirror = (function() {
2227
  indentation: function(tabSize) {return countColumn(this.text, null, tabSize);},
2228
  // Produces an HTML fragment for the line, taking selection,
2229
  // marking, and highlighting into account.
2230
- getHTML: function(sfrom, sto, includePre, tabText, endAt) {
2231
- var html = [], first = true;
2232
- if (includePre)
2233
- html.push(this.className ? '<pre class="' + this.className + '">': "<pre>");
2234
- function span(text, style) {
2235
  if (!text) return;
2236
  // Work around a bug where, in some compat modes, IE ignores leading spaces
2237
  if (first && ie && text.charAt(0) == " ") text = "\u00a0" + text.slice(1);
2238
  first = false;
2239
- if (style) html.push('<span class="', style, '">', htmlEscape(text).replace(/\t/g, tabText), "</span>");
2240
- else html.push(htmlEscape(text).replace(/\t/g, tabText));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2241
  }
 
2242
  var st = this.styles, allText = this.text, marked = this.marked;
2243
- if (sfrom == sto) sfrom = null;
2244
  var len = allText.length;
2245
- if (endAt != null) len = Math.min(endAt, len);
2246
-
2247
- if (!allText && endAt == null)
2248
- span(" ", sfrom != null && sto == null ? "CodeMirror-selected" : null);
2249
- else if (!marked && sfrom == null)
 
 
2250
  for (var i = 0, ch = 0; ch < len; i+=2) {
2251
  var str = st[i], style = st[i+1], l = str.length;
2252
  if (ch + l > len) str = str.slice(0, len - ch);
2253
  ch += l;
2254
- span(str, style && "cm-" + style);
2255
  }
2256
- else {
2257
  var pos = 0, i = 0, text = "", style, sg = 0;
2258
- var markpos = -1, mark = null;
2259
- function nextMark() {
2260
- if (marked) {
2261
- markpos += 1;
2262
- mark = (markpos < marked.length) ? marked[markpos] : null;
 
 
2263
  }
2264
- }
2265
- nextMark();
2266
- while (pos < len) {
2267
- var upto = len;
2268
- var extraStyle = "";
2269
- if (sfrom != null) {
2270
- if (sfrom > pos) upto = sfrom;
2271
- else if (sto == null || sto > pos) {
2272
- extraStyle = " CodeMirror-selected";
2273
- if (sto != null) upto = Math.min(upto, sto);
2274
- }
2275
  }
2276
- while (mark && mark.to != null && mark.to <= pos) nextMark();
2277
- if (mark) {
2278
- if (mark.from > pos) upto = Math.min(upto, mark.from);
2279
- else {
2280
- extraStyle += " " + mark.style;
2281
- if (mark.to != null) upto = Math.min(upto, mark.to);
 
 
 
 
 
 
 
 
2282
  }
2283
- }
2284
- for (;;) {
2285
- var end = pos + text.length;
2286
- var appliedStyle = style;
2287
- if (extraStyle) appliedStyle = style ? style + extraStyle : extraStyle;
2288
- span(end > upto ? text.slice(0, upto - pos) : text, appliedStyle);
2289
- if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;}
2290
- pos = end;
2291
- text = st[i++]; style = "cm-" + st[i++];
2292
  }
2293
  }
2294
- if (sfrom != null && sto == null) span(" ", "CodeMirror-selected");
2295
  }
2296
- if (includePre) html.push("</pre>");
2297
- return html.join("");
2298
  },
2299
  cleanUp: function() {
2300
  this.parent = null;
@@ -2309,8 +2768,7 @@ var CodeMirror = (function() {
2309
  if (state == 0) {
2310
  if (end > from) dest.push(part.slice(from - pos, Math.min(part.length, to - pos)), source[i+1]);
2311
  if (end >= from) state = 1;
2312
- }
2313
- else if (state == 1) {
2314
  if (end > to) dest.push(part.slice(0, to - pos), source[i+1]);
2315
  else dest.push(part, source[i+1]);
2316
  }
@@ -2345,7 +2803,7 @@ var CodeMirror = (function() {
2345
  },
2346
  insertHeight: function(at, lines, height) {
2347
  this.height += height;
2348
- this.lines.splice.apply(this.lines, [at, 0].concat(lines));
2349
  for (var i = 0, e = lines.length; i < e; ++i) lines[i].parent = this;
2350
  },
2351
  iterN: function(at, n, op) {
@@ -2384,6 +2842,7 @@ var CodeMirror = (function() {
2384
  var lines = [];
2385
  this.collapse(lines);
2386
  this.children = [new LeafChunk(lines)];
 
2387
  }
2388
  },
2389
  collapse: function(lines) {
@@ -2510,31 +2969,36 @@ var CodeMirror = (function() {
2510
  function History() {
2511
  this.time = 0;
2512
  this.done = []; this.undone = [];
 
 
2513
  }
2514
  History.prototype = {
2515
  addChange: function(start, added, old) {
2516
  this.undone.length = 0;
2517
- var time = +new Date, last = this.done[this.done.length - 1];
2518
- if (time - this.time > 400 || !last ||
2519
- last.start > start + added || last.start + last.added < start - last.added + last.old.length)
2520
- this.done.push({start: start, added: added, old: old});
2521
- else {
2522
- var oldoff = 0;
2523
- if (start < last.start) {
2524
- for (var i = last.start - start - 1; i >= 0; --i)
2525
- last.old.unshift(old[i]);
2526
- last.added += last.start - start;
2527
- last.start = start;
2528
- }
2529
- else if (last.start < start) {
2530
- oldoff = start - last.start;
2531
- added += oldoff;
2532
- }
2533
- for (var i = last.added - oldoff, e = old.length; i < e; ++i)
2534
- last.old.push(old[i]);
2535
- if (last.added < added) last.added = added;
2536
  }
2537
  this.time = time;
 
 
 
 
 
 
2538
  }
2539
  };
2540
 
@@ -2560,10 +3024,21 @@ var CodeMirror = (function() {
2560
 
2561
  function e_target(e) {return e.target || e.srcElement;}
2562
  function e_button(e) {
2563
- if (e.which) return e.which;
2564
- else if (e.button & 1) return 1;
2565
- else if (e.button & 2) return 3;
2566
- else if (e.button & 4) return 2;
 
 
 
 
 
 
 
 
 
 
 
2567
  }
2568
 
2569
  // Event handler registration. If disconnect is true, it'll return a
@@ -2572,8 +3047,7 @@ var CodeMirror = (function() {
2572
  if (typeof node.addEventListener == "function") {
2573
  node.addEventListener(type, handler, false);
2574
  if (disconnect) return function() {node.removeEventListener(type, handler, false);};
2575
- }
2576
- else {
2577
  var wrapHandler = function(event) {handler(event || window.event);};
2578
  node.attachEvent("on" + type, wrapHandler);
2579
  if (disconnect) return function() {node.detachEvent("on" + type, wrapHandler);};
@@ -2584,26 +3058,36 @@ var CodeMirror = (function() {
2584
  function Delayed() {this.id = null;}
2585
  Delayed.prototype = {set: function(ms, f) {clearTimeout(this.id); this.id = setTimeout(f, ms);}};
2586
 
 
 
2587
  // Detect drag-and-drop
2588
  var dragAndDrop = function() {
2589
- // IE8 has ondragstart and ondrop properties, but doesn't seem to
2590
- // actually support ondragstart the way it's supposed to work.
2591
- if (/MSIE [1-8]\b/.test(navigator.userAgent)) return false;
2592
- var div = document.createElement('div');
2593
- return "draggable" in div;
2594
  }();
2595
 
2596
- var gecko = /gecko\/\d{7}/i.test(navigator.userAgent);
2597
- var ie = /MSIE \d/.test(navigator.userAgent);
2598
- var webkit = /WebKit\//.test(navigator.userAgent);
2599
-
2600
- var lineSep = "\n";
2601
  // Feature-detect whether newlines in textareas are converted to \r\n
2602
- (function () {
2603
- var te = document.createElement("textarea");
2604
  te.value = "foo\nbar";
2605
- if (te.value.indexOf("\r") > -1) lineSep = "\r\n";
2606
- }());
 
 
 
 
 
 
 
 
 
 
 
 
 
2607
 
2608
  // Counts the column offset in a string, taking tabs into account.
2609
  // Used mostly to find indentation.
@@ -2619,31 +3103,7 @@ var CodeMirror = (function() {
2619
  return n;
2620
  }
2621
 
2622
- function computedStyle(elt) {
2623
- if (elt.currentStyle) return elt.currentStyle;
2624
- return window.getComputedStyle(elt, null);
2625
- }
2626
-
2627
- // Find the position of an element by following the offsetParent chain.
2628
- // If screen==true, it returns screen (rather than page) coordinates.
2629
  function eltOffset(node, screen) {
2630
- var bod = node.ownerDocument.body;
2631
- var x = 0, y = 0, skipBody = false;
2632
- for (var n = node; n; n = n.offsetParent) {
2633
- var ol = n.offsetLeft, ot = n.offsetTop;
2634
- // Firefox reports weird inverted offsets when the body has a border.
2635
- if (n == bod) { x += Math.abs(ol); y += Math.abs(ot); }
2636
- else { x += ol, y += ot; }
2637
- if (screen && computedStyle(n).position == "fixed")
2638
- skipBody = true;
2639
- }
2640
- var e = screen && !skipBody ? null : bod;
2641
- for (var n = node.parentNode; n != e; n = n.parentNode)
2642
- if (n.scrollLeft != null) { x -= n.scrollLeft; y -= n.scrollTop;}
2643
- return {left: x, top: y};
2644
- }
2645
- // Use the faster and saner getBoundingClientRect method when possible.
2646
- if (document.documentElement.getBoundingClientRect != null) eltOffset = function(node, screen) {
2647
  // Take the parts of bounding client rect that we are interested in so we are able to edit if need be,
2648
  // since the returned value cannot be changed externally (they are kept in sync as the element moves within the page)
2649
  try { var box = node.getBoundingClientRect(); box = { top: box.top, left: box.left }; }
@@ -2659,43 +3119,51 @@ var CodeMirror = (function() {
2659
  }
2660
  }
2661
  return box;
2662
- };
2663
 
2664
  // Get a node's text content.
2665
  function eltText(node) {
2666
  return node.textContent || node.innerText || node.nodeValue || "";
2667
  }
 
 
 
 
 
 
2668
 
2669
  // Operations on {line, ch} objects.
2670
  function posEq(a, b) {return a.line == b.line && a.ch == b.ch;}
2671
  function posLess(a, b) {return a.line < b.line || (a.line == b.line && a.ch < b.ch);}
2672
  function copyPos(x) {return {line: x.line, ch: x.ch};}
2673
 
2674
- var escapeElement = document.createElement("pre");
2675
- function htmlEscape(str) {
2676
- escapeElement.textContent = str;
2677
- return escapeElement.innerHTML;
 
 
 
2678
  }
2679
- // Recent (late 2011) Opera betas insert bogus newlines at the start
2680
- // of the textContent, so we strip those.
2681
- if (htmlEscape("a") == "\na")
2682
- htmlEscape = function(str) {
2683
- escapeElement.textContent = str;
2684
- return escapeElement.innerHTML.slice(1);
2685
- };
2686
- // Some IEs don't preserve tabs through innerHTML
2687
- else if (htmlEscape("\t") != "\t")
2688
- htmlEscape = function(str) {
2689
- escapeElement.innerHTML = "";
2690
- escapeElement.appendChild(document.createTextNode(str));
2691
- return escapeElement.innerHTML;
2692
- };
2693
- CodeMirror.htmlEscape = htmlEscape;
2694
 
2695
  // Used to position the cursor after an undo/redo by finding the
2696
  // last edited character.
2697
  function editEnd(from, to) {
2698
- if (!to) return from ? from.length : 0;
2699
  if (!from) return to.length;
2700
  for (var i = from.length, j = to.length; i >= 0 && j >= 0; --i, --j)
2701
  if (from.charAt(i) != to.charAt(j)) break;
@@ -2715,14 +3183,22 @@ var CodeMirror = (function() {
2715
  // See if "".split is the broken IE version, if so, provide an
2716
  // alternative way to split lines.
2717
  var splitLines = "\n\nb".split(/\n/).length != 3 ? function(string) {
2718
- var pos = 0, nl, result = [];
2719
- while ((nl = string.indexOf("\n", pos)) > -1) {
2720
- result.push(string.slice(pos, string.charAt(nl-1) == "\r" ? nl - 1 : nl));
2721
- pos = nl + 1;
 
 
 
 
 
 
 
 
 
2722
  }
2723
- result.push(string.slice(pos));
2724
  return result;
2725
- } : function(string){return string.split(/\r?\n/);};
2726
  CodeMirror.splitLines = splitLines;
2727
 
2728
  var hasSelection = window.getSelection ? function(te) {
@@ -2743,10 +3219,10 @@ var CodeMirror = (function() {
2743
  var keyNames = {3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
2744
  19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
2745
  36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
2746
- 46: "Delete", 59: ";", 91: "Mod", 92: "Mod", 93: "Mod", 186: ";", 187: "=", 188: ",",
2747
- 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\", 221: "]", 222: "'", 63276: "PageUp",
2748
- 63277: "PageDown", 63275: "End", 63273: "Home", 63234: "Left", 63232: "Up", 63235: "Right",
2749
- 63233: "Down", 63302: "Insert", 63272: "Delete"};
2750
  CodeMirror.keyNames = keyNames;
2751
  (function() {
2752
  // Number keys
1
+ // CodeMirror version 2.33
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
 
7
  // CodeMirror is the only global var we claim
8
+ window.CodeMirror = (function() {
9
+ "use strict";
10
+ // This is the function that produces an editor instance. Its
11
  // closure is used to store the editor state.
12
  function CodeMirror(place, givenOptions) {
13
  // Determine effective options based on given values and defaults.
16
  if (defaults.hasOwnProperty(opt))
17
  options[opt] = (givenOptions && givenOptions.hasOwnProperty(opt) ? givenOptions : defaults)[opt];
18
 
19
+ var input = elt("textarea", null, null, "position: absolute; padding: 0; width: 1px; height: 1em");
20
+ input.setAttribute("wrap", "off"); input.setAttribute("autocorrect", "off"); input.setAttribute("autocapitalize", "off");
21
+ // Wraps and hides input textarea
22
+ var inputDiv = elt("div", [input], null, "overflow: hidden; position: relative; width: 3px; height: 0px;");
23
+ // The empty scrollbar content, used solely for managing the scrollbar thumb.
24
+ var scrollbarInner = elt("div", null, "CodeMirror-scrollbar-inner");
25
+ // The vertical scrollbar. Horizontal scrolling is handled by the scroller itself.
26
+ var scrollbar = elt("div", [scrollbarInner], "CodeMirror-scrollbar");
27
+ // DIVs containing the selection and the actual code
28
+ var lineDiv = elt("div"), selectionDiv = elt("div", null, null, "position: relative; z-index: -1");
29
+ // Blinky cursor, and element used to ensure cursor fits at the end of a line
30
+ var cursor = elt("pre", "\u00a0", "CodeMirror-cursor"), widthForcer = elt("pre", "\u00a0", "CodeMirror-cursor", "visibility: hidden");
31
+ // Used to measure text size
32
+ var measure = elt("div", null, null, "position: absolute; width: 100%; height: 0px; overflow: hidden; visibility: hidden;");
33
+ var lineSpace = elt("div", [measure, cursor, widthForcer, selectionDiv, lineDiv], null, "position: relative; z-index: 0");
34
+ var gutterText = elt("div", null, "CodeMirror-gutter-text"), gutter = elt("div", [gutterText], "CodeMirror-gutter");
35
+ // Moved around its parent to cover visible view
36
+ var mover = elt("div", [gutter, elt("div", [lineSpace], "CodeMirror-lines")], null, "position: relative");
37
+ // Set to the height of the text, causes scrolling
38
+ var sizer = elt("div", [mover], null, "position: relative");
39
+ // Provides scrolling
40
+ var scroller = elt("div", [sizer], "CodeMirror-scroll");
41
+ scroller.setAttribute("tabIndex", "-1");
42
  // The element in which the editor lives.
43
+ var wrapper = elt("div", [inputDiv, scrollbar, scroller], "CodeMirror" + (options.lineWrapping ? " CodeMirror-wrap" : ""));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44
  if (place.appendChild) place.appendChild(wrapper); else place(wrapper);
45
+
46
+ themeChanged(); keyMapChanged();
 
 
 
 
 
47
  // Needed to hide big blue blinking cursor on Mobile Safari
48
+ if (ios) input.style.width = "0px";
49
+ if (!webkit) scroller.draggable = true;
50
+ lineSpace.style.outline = "none";
51
  if (options.tabindex != null) input.tabIndex = options.tabindex;
52
+ if (options.autofocus) focusInput();
53
  if (!options.gutter && !options.lineNumbers) gutter.style.display = "none";
54
+ // Needed to handle Tab key in KHTML
55
+ if (khtml) inputDiv.style.height = "1px", inputDiv.style.position = "absolute";
56
+
57
+ // Check for OS X >= 10.7. This has transparent scrollbars, so the
58
+ // overlaying of one scrollbar with another won't work. This is a
59
+ // temporary hack to simply turn off the overlay scrollbar. See
60
+ // issue #727.
61
+ if (mac_geLion) { scrollbar.style.zIndex = -2; scrollbar.style.visibility = "hidden"; }
62
+ // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
63
+ else if (ie_lt8) scrollbar.style.minWidth = "18px";
64
 
65
  // Check for problem with IE innerHTML not working when we have a
66
  // P (or similar) parent node.
67
+ try { charWidth(); }
68
  catch (e) {
69
  if (e.message.match(/runtime/i))
70
  e = new Error("A CodeMirror inside a P-style element does not work in Internet Explorer. (innerHTML bug)");
85
  var sel = {from: {line: 0, ch: 0}, to: {line: 0, ch: 0}, inverted: false};
86
  // Selection-related flags. shiftSelecting obviously tracks
87
  // whether the user is holding shift.
88
+ var shiftSelecting, lastClick, lastDoubleClick, lastScrollTop = 0, draggingText,
89
+ overwrite = false, suppressEdits = false;
90
  // Variables used by startOperation/endOperation to track what
91
  // happened during the operation.
92
  var updateInput, userSelChange, changes, textChanged, selectionChanged, leaveInputAlone,
93
  gutterDirty, callbacks;
94
  // Current visible range (may be bigger than the view window).
95
  var displayOffset = 0, showingFrom = 0, showingTo = 0, lastSizeC = 0;
96
+ // bracketHighlighted is used to remember that a bracket has been
97
  // marked.
98
  var bracketHighlighted;
99
  // Tracks the maximum line length so that the horizontal scrollbar
100
  // can be kept static when scrolling.
101
+ var maxLine = getLine(0), updateMaxLine = false, maxLineChanged = true;
102
+ var tabCache = {};
103
+ var pollingFast = false; // Ensures slowPoll doesn't cancel fastPoll
104
+ var goalColumn = null;
105
 
106
  // Initialize the content.
107
  operation(function(){setValue(options.value || ""); updateInput = false;})();
110
  // Register our event handlers.
111
  connect(scroller, "mousedown", operation(onMouseDown));
112
  connect(scroller, "dblclick", operation(onDoubleClick));
 
113
  connect(lineSpace, "selectstart", e_preventDefault);
114
  // Gecko browsers fire contextmenu *after* opening the menu, at
115
  // which point we can't mess with it anymore. Context menu is
116
  // handled in onMouseDown for Gecko.
117
  if (!gecko) connect(scroller, "contextmenu", onContextMenu);
118
+ connect(scroller, "scroll", onScrollMain);
119
+ connect(scrollbar, "scroll", onScrollBar);
120
+ connect(scrollbar, "mousedown", function() {if (focused) setTimeout(focusInput, 0);});
121
+ var resizeHandler = connect(window, "resize", function() {
122
+ if (wrapper.parentNode) updateDisplay(true);
123
+ else resizeHandler();
124
+ }, true);
125
  connect(input, "keyup", operation(onKeyUp));
126
  connect(input, "input", fastPoll);
127
  connect(input, "keydown", operation(onKeyDown));
129
  connect(input, "focus", onFocus);
130
  connect(input, "blur", onBlur);
131
 
132
+ function drag_(e) {
133
+ if (options.onDragEvent && options.onDragEvent(instance, addStop(e))) return;
134
+ e_stop(e);
135
+ }
136
+ if (options.dragDrop) {
137
+ connect(scroller, "dragstart", onDragStart);
138
+ connect(scroller, "dragenter", drag_);
139
+ connect(scroller, "dragover", drag_);
140
+ connect(scroller, "drop", operation(onDrop));
141
+ }
142
  connect(scroller, "paste", function(){focusInput(); fastPoll();});
143
  connect(input, "paste", fastPoll);
144
+ connect(input, "cut", operation(function(){
145
+ if (!options.readOnly) replaceSelection("");
146
+ }));
147
+
148
+ // Needed to handle Tab key in KHTML
149
+ if (khtml) connect(sizer, "mouseup", function() {
150
+ if (document.activeElement == input) input.blur();
151
+ focusInput();
152
+ });
153
 
154
  // IE throws unspecified error in certain cases, when
155
  // trying to access activeElement before onload
156
+ var hasFocus; try { hasFocus = (document.activeElement == input); } catch(e) { }
157
+ if (hasFocus || options.autofocus) setTimeout(onFocus, 20);
158
  else onBlur();
159
 
160
  function isLine(l) {return l >= 0 && l < doc.size;}
168
  setValue: operation(setValue),
169
  getSelection: getSelection,
170
  replaceSelection: operation(replaceSelection),
171
+ focus: function(){window.focus(); focusInput(); onFocus(); fastPoll();},
172
  setOption: function(option, value) {
173
  var oldVal = options[option];
174
  options[option] = value;
175
  if (option == "mode" || option == "indentUnit") loadMode();
176
+ else if (option == "readOnly" && value == "nocursor") {onBlur(); input.blur();}
177
+ else if (option == "readOnly" && !value) {resetInput(true);}
178
  else if (option == "theme") themeChanged();
179
  else if (option == "lineWrapping" && oldVal != value) operation(wrappingChanged)();
180
+ else if (option == "tabSize") updateDisplay(true);
181
+ else if (option == "keyMap") keyMapChanged();
182
+ if (option == "lineNumbers" || option == "gutter" || option == "firstLineNumber" ||
183
+ option == "theme" || option == "lineNumberFormatter") {
184
+ gutterChanged();
185
+ updateDisplay(true);
186
+ }
187
  },
188
  getOption: function(option) {return options[option];},
189
  undo: operation(undo),
190
  redo: operation(redo),
191
  indentLine: operation(function(n, dir) {
192
+ if (typeof dir != "string") {
193
+ if (dir == null) dir = options.smartIndent ? "smart" : "prev";
194
+ else dir = dir ? "add" : "subtract";
195
+ }
196
+ if (isLine(n)) indentLine(n, dir);
197
  }),
198
  indentSelection: operation(indentSelected),
199
  historySize: function() {return {undo: history.done.length, redo: history.undone.length};},
200
  clearHistory: function() {history = new History();},
201
+ setHistory: function(histData) {
202
+ history = new History();
203
+ history.done = histData.done;
204
+ history.undone = histData.undone;
205
+ },
206
+ getHistory: function() {
207
+ history.time = 0;
208
+ return {done: history.done.concat([]), undone: history.undone.concat([])};
209
+ },
210
  matchBrackets: operation(function(){matchBrackets(true);}),
211
  getTokenAt: operation(function(pos) {
212
  pos = clipPos(pos);
213
+ return getLine(pos.line).getTokenAt(mode, getStateBefore(pos.line), options.tabSize, pos.ch);
214
  }),
215
  getStateAfter: function(line) {
216
  line = clipLine(line == null ? doc.size - 1: line);
217
  return getStateBefore(line + 1);
218
  },
219
+ cursorCoords: function(start, mode) {
220
  if (start == null) start = sel.inverted;
221
+ return this.charCoords(start ? sel.from : sel.to, mode);
222
+ },
223
+ charCoords: function(pos, mode) {
224
+ pos = clipPos(pos);
225
+ if (mode == "local") return localCoords(pos, false);
226
+ if (mode == "div") return localCoords(pos, true);
227
+ return pageCoords(pos);
228
  },
 
229
  coordsChar: function(coords) {
230
  var off = eltOffset(lineSpace);
231
  return coordsChar(coords.x - off.left, coords.y - off.top);
232
  },
233
  markText: operation(markText),
234
  setBookmark: setBookmark,
235
+ findMarksAt: findMarksAt,
236
  setMarker: operation(addGutterMarker),
237
  clearMarker: operation(removeGutterMarker),
238
  setLineClass: operation(setLineClass),
247
  return line;
248
  },
249
  lineInfo: lineInfo,
250
+ getViewport: function() { return {from: showingFrom, to: showingTo};},
251
  addWidget: function(pos, node, scroll, vert, horiz) {
252
  pos = localCoords(clipPos(pos));
253
  var top = pos.yBot, left = pos.x;
254
  node.style.position = "absolute";
255
+ sizer.appendChild(node);
256
  if (vert == "over") top = pos.y;
257
  else if (vert == "near") {
258
  var vspace = Math.max(scroller.offsetHeight, doc.height * textHeight()),
259
+ hspace = Math.max(sizer.clientWidth, lineSpace.clientWidth) - paddingLeft();
260
  if (pos.yBot + node.offsetHeight > vspace && pos.y > node.offsetHeight)
261
  top = pos.y - node.offsetHeight;
262
  if (left + node.offsetWidth > hspace)
265
  node.style.top = (top + paddingTop()) + "px";
266
  node.style.left = node.style.right = "";
267
  if (horiz == "right") {
268
+ left = sizer.clientWidth - node.offsetWidth;
269
  node.style.right = "0px";
270
  } else {
271
  if (horiz == "left") left = 0;
272
+ else if (horiz == "middle") left = (sizer.clientWidth - node.offsetWidth) / 2;
273
  node.style.left = (left + paddingLeft()) + "px";
274
  }
275
  if (scroll)
299
  if (isLine(line)) replaceRange("", {line: line, ch: 0}, clipPos({line: line+1, ch: 0}));
300
  }),
301
  replaceRange: operation(replaceRange),
302
+ getRange: function(from, to, lineSep) {return getRange(clipPos(from), clipPos(to), lineSep);},
303
 
304
+ triggerOnKeyDown: operation(onKeyDown),
305
  execCommand: function(cmd) {return commands[cmd](instance);},
306
  // Stuff used by commands, probably not much use to outside code.
307
  moveH: operation(moveH),
308
  deleteH: operation(deleteH),
309
  moveV: operation(moveV),
310
+ toggleOverwrite: function() {
311
+ if(overwrite){
312
+ overwrite = false;
313
+ cursor.className = cursor.className.replace(" CodeMirror-overwrite", "");
314
+ } else {
315
+ overwrite = true;
316
+ cursor.className += " CodeMirror-overwrite";
317
+ }
318
+ },
319
 
320
  posFromIndex: function(off) {
321
  var lineNo = 0, ch;
335
  });
336
  return index;
337
  },
338
+ scrollTo: function(x, y) {
339
+ if (x != null) scroller.scrollLeft = x;
340
+ if (y != null) scrollbar.scrollTop = scroller.scrollTop = y;
341
+ updateDisplay([]);
342
+ },
343
+ getScrollInfo: function() {
344
+ return {x: scroller.scrollLeft, y: scrollbar.scrollTop,
345
+ height: scrollbar.scrollHeight, width: scroller.scrollWidth};
346
+ },
347
+ setSize: function(width, height) {
348
+ function interpret(val) {
349
+ val = String(val);
350
+ return /^\d+$/.test(val) ? val + "px" : val;
351
+ }
352
+ if (width != null) wrapper.style.width = interpret(width);
353
+ if (height != null) scroller.style.height = interpret(height);
354
+ instance.refresh();
355
+ },
356
 
357
  operation: function(f){return operation(f)();},
358
+ compoundChange: function(f){return compoundChange(f);},
359
+ refresh: function(){
360
+ updateDisplay(true, null, lastScrollTop);
361
+ if (scrollbar.scrollHeight > lastScrollTop)
362
+ scrollbar.scrollTop = lastScrollTop;
363
+ },
364
  getInputField: function(){return input;},
365
  getWrapperElement: function(){return wrapper;},
366
  getScrollerElement: function(){return scroller;},
380
  splitLines(code), top, top);
381
  updateInput = true;
382
  }
383
+ function getValue(lineSep) {
384
  var text = [];
385
  doc.iter(0, doc.size, function(line) { text.push(line.text); });
386
+ return text.join(lineSep || "\n");
387
+ }
388
+
389
+ function onScrollBar(e) {
390
+ if (scrollbar.scrollTop != lastScrollTop) {
391
+ lastScrollTop = scroller.scrollTop = scrollbar.scrollTop;
392
+ updateDisplay([]);
393
+ }
394
+ }
395
+
396
+ function onScrollMain(e) {
397
+ if (options.fixedGutter && gutter.style.left != scroller.scrollLeft + "px")
398
+ gutter.style.left = scroller.scrollLeft + "px";
399
+ if (scroller.scrollTop != lastScrollTop) {
400
+ lastScrollTop = scroller.scrollTop;
401
+ if (scrollbar.scrollTop != lastScrollTop)
402
+ scrollbar.scrollTop = lastScrollTop;
403
+ updateDisplay([]);
404
+ }
405
+ if (options.onScroll) options.onScroll(instance);
406
  }
407
 
408
  function onMouseDown(e) {
409
+ setShift(e_prop(e, "shiftKey"));
410
  // Check whether this is a click in a widget
411
  for (var n = e_target(e); n != wrapper; n = n.parentNode)
412
+ if (n.parentNode == sizer && n != mover) return;
413
 
414
  // See if this is a click in the gutter
415
  for (var n = e_target(e); n != wrapper; n = n.parentNode)
423
 
424
  switch (e_button(e)) {
425
  case 3:
426
+ if (gecko) onContextMenu(e);
427
  return;
428
  case 2:
429
  if (start) setCursor(start.line, start.ch, true);
430
+ setTimeout(focusInput, 20);
431
+ e_preventDefault(e);
432
  return;
433
  }
434
  // For button 1, if it was clicked inside the editor
438
 
439
  if (!focused) onFocus();
440
 
441
+ var now = +new Date, type = "single";
442
  if (lastDoubleClick && lastDoubleClick.time > now - 400 && posEq(lastDoubleClick.pos, start)) {
443
+ type = "triple";
444
  e_preventDefault(e);
445
  setTimeout(focusInput, 20);
446
+ selectLine(start.line);
447
  } else if (lastClick && lastClick.time > now - 400 && posEq(lastClick.pos, start)) {
448
+ type = "double";
449
  lastDoubleClick = {time: now, pos: start};
450
  e_preventDefault(e);
451
+ var word = findWordAt(start);
452
+ setSelectionUser(word.from, word.to);
453
  } else { lastClick = {time: now, pos: start}; }
454
 
455
+ function dragEnd(e2) {
456
+ if (webkit) scroller.draggable = false;
457
+ draggingText = false;
458
+ up(); drop();
459
+ if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) {
460
+ e_preventDefault(e2);
461
+ setCursor(start.line, start.ch, true);
462
+ focusInput();
463
+ }
464
+ }
465
  var last = start, going;
466
+ if (options.dragDrop && dragAndDrop && !options.readOnly && !posEq(sel.from, sel.to) &&
467
+ !posLess(start, sel.from) && !posLess(sel.to, start) && type == "single") {
468
  // Let the drag handler handle this.
469
+ if (webkit) scroller.draggable = true;
470
+ var up = connect(document, "mouseup", operation(dragEnd), true);
471
+ var drop = connect(scroller, "drop", operation(dragEnd), true);
 
 
 
 
 
 
 
 
472
  draggingText = true;
473
+ // IE's approach to draggable
474
+ if (scroller.dragDrop) scroller.dragDrop();
475
  return;
476
  }
477
  e_preventDefault(e);
478
+ if (type == "single") setCursor(start.line, start.ch, true);
479
+
480
+ var startstart = sel.from, startend = sel.to;
481
+
482
+ function doSelect(cur) {
483
+ if (type == "single") {
484
+ setSelectionUser(start, cur);
485
+ } else if (type == "double") {
486
+ var word = findWordAt(cur);
487
+ if (posLess(cur, startstart)) setSelectionUser(word.from, startend);
488
+ else setSelectionUser(startstart, word.to);
489
+ } else if (type == "triple") {
490
+ if (posLess(cur, startstart)) setSelectionUser(startend, clipPos({line: cur.line, ch: 0}));
491
+ else setSelectionUser(startstart, clipPos({line: cur.line + 1, ch: 0}));
492
+ }
493
+ }
494
 
495
  function extend(e) {
496
  var cur = posFromMouse(e, true);
497
  if (cur && !posEq(cur, last)) {
498
  if (!focused) onFocus();
499
  last = cur;
500
+ doSelect(cur);
501
  updateInput = false;
502
  var visible = visibleLines();
503
  if (cur.line >= visible.to || cur.line < visible.from)
505
  }
506
  }
507
 
508
+ function done(e) {
 
 
 
 
 
509
  clearTimeout(going);
510
  var cur = posFromMouse(e);
511
+ if (cur) doSelect(cur);
512
  e_preventDefault(e);
513
  focusInput();
514
  updateInput = true;
515
  move(); up();
516
+ }
517
+ var move = connect(document, "mousemove", operation(function(e) {
518
+ clearTimeout(going);
519
+ e_preventDefault(e);
520
+ if (!ie && !e_button(e)) done(e);
521
+ else extend(e);
522
  }), true);
523
+ var up = connect(document, "mouseup", operation(done), true);
524
  }
525
  function onDoubleClick(e) {
526
  for (var n = e_target(e); n != wrapper; n = n.parentNode)
527
  if (n.parentNode == gutterText) return e_preventDefault(e);
 
 
 
528
  e_preventDefault(e);
 
529
  }
530
  function onDrop(e) {
531
+ if (options.onDragEvent && options.onDragEvent(instance, addStop(e))) return;
532
+ e_preventDefault(e);
533
  var pos = posFromMouse(e, true), files = e.dataTransfer.files;
534
  if (!pos || options.readOnly) return;
535
  if (files && files.length && window.FileReader && window.File) {
536
+ var n = files.length, text = Array(n), read = 0;
537
+ var loadFile = function(file, i) {
538
  var reader = new FileReader;
539
  reader.onload = function() {
540
  text[i] = reader.result;
547
  }
548
  };
549
  reader.readAsText(file);
550
+ };
 
551
  for (var i = 0; i < n; ++i) loadFile(files[i], i);
552
+ } else {
553
+ // Don't do a replace if the drop happened inside of the selected text.
554
+ if (draggingText && !(posLess(pos, sel.from) || posLess(sel.to, pos))) return;
555
  try {
556
  var text = e.dataTransfer.getData("Text");
557
  if (text) {
558
+ compoundChange(function() {
559
+ var curFrom = sel.from, curTo = sel.to;
560
+ setSelectionUser(pos, pos);
561
+ if (draggingText) replaceRange("", curFrom, curTo);
562
+ replaceSelection(text);
563
+ focusInput();
564
+ });
565
  }
566
  }
567
  catch(e){}
569
  }
570
  function onDragStart(e) {
571
  var txt = getSelection();
 
 
 
572
  e.dataTransfer.setData("Text", txt);
573
+
574
+ // Use dummy image instead of default browsers image.
575
+ if (gecko || chrome || opera) {
576
+ var img = elt('img');
577
+ img.scr = 'data:image/gif;base64,R0lGODdhAgACAIAAAAAAAP///ywAAAAAAgACAAACAoRRADs='; //1x1 image
578
+ e.dataTransfer.setDragImage(img, 0, 0);
 
 
 
 
 
 
 
 
579
  }
580
+ }
581
+
582
+ function doHandleBinding(bound, dropShift) {
583
  if (typeof bound == "string") {
584
+ bound = commands[bound];
585
+ if (!bound) return false;
586
+ }
587
+ var prevShift = shiftSelecting;
588
+ try {
589
+ if (options.readOnly) suppressEdits = true;
590
+ if (dropShift) shiftSelecting = null;
 
591
  bound(instance);
592
+ } catch(e) {
593
+ if (e != Pass) throw e;
594
+ return false;
595
+ } finally {
596
  shiftSelecting = prevShift;
597
+ suppressEdits = false;
598
+ }
599
  return true;
600
  }
601
+ var maybeTransition;
602
+ function handleKeyBinding(e) {
603
+ // Handle auto keymap transitions
604
+ var startMap = getKeyMap(options.keyMap), next = startMap.auto;
605
+ clearTimeout(maybeTransition);
606
+ if (next && !isModifierKey(e)) maybeTransition = setTimeout(function() {
607
+ if (getKeyMap(options.keyMap) == startMap) {
608
+ options.keyMap = (next.call ? next.call(null, instance) : next);
609
+ }
610
+ }, 50);
611
+
612
+ var name = keyNames[e_prop(e, "keyCode")], handled = false;
613
+ if (name == null || e.altGraphKey) return false;
614
+ if (e_prop(e, "altKey")) name = "Alt-" + name;
615
+ if (e_prop(e, "ctrlKey")) name = "Ctrl-" + name;
616
+ if (e_prop(e, "metaKey")) name = "Cmd-" + name;
617
+
618
+ var stopped = false;
619
+ function stop() { stopped = true; }
620
+
621
+ if (e_prop(e, "shiftKey")) {
622
+ handled = lookupKey("Shift-" + name, options.extraKeys, options.keyMap,
623
+ function(b) {return doHandleBinding(b, true);}, stop)
624
+ || lookupKey(name, options.extraKeys, options.keyMap, function(b) {
625
+ if (typeof b == "string" && /^go[A-Z]/.test(b)) return doHandleBinding(b);
626
+ }, stop);
627
+ } else {
628
+ handled = lookupKey(name, options.extraKeys, options.keyMap, doHandleBinding, stop);
629
+ }
630
+ if (stopped) handled = false;
631
+ if (handled) {
632
+ e_preventDefault(e);
633
+ restartBlink();
634
+ if (ie) { e.oldKeyCode = e.keyCode; e.keyCode = 0; }
635
+ }
636
+ return handled;
637
+ }
638
+ function handleCharBinding(e, ch) {
639
+ var handled = lookupKey("'" + ch + "'", options.extraKeys,
640
+ options.keyMap, function(b) { return doHandleBinding(b, true); });
641
+ if (handled) {
642
+ e_preventDefault(e);
643
+ restartBlink();
644
+ }
645
+ return handled;
646
+ }
647
+
648
  var lastStoppedKey = null;
649
  function onKeyDown(e) {
650
  if (!focused) onFocus();
651
+ if (ie && e.keyCode == 27) { e.returnValue = false; }
652
+ if (pollingFast) { if (readInput()) pollingFast = false; }
653
+ if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return;
654
+ var code = e_prop(e, "keyCode");
655
  // IE does strange things with escape.
656
+ setShift(code == 16 || e_prop(e, "shiftKey"));
 
657
  // First give onKeyEvent option a chance to handle this.
 
658
  var handled = handleKeyBinding(e);
659
+ if (opera) {
660
+ lastStoppedKey = handled ? code : null;
661
  // Opera has no cut event... we try to at least catch the key combo
662
+ if (!handled && code == 88 && e_prop(e, mac ? "metaKey" : "ctrlKey"))
663
  replaceSelection("");
664
  }
665
  }
666
  function onKeyPress(e) {
667
+ if (pollingFast) readInput();
668
  if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return;
669
+ var keyCode = e_prop(e, "keyCode"), charCode = e_prop(e, "charCode");
670
+ if (opera && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;}
671
+ if (((opera && (!e.which || e.which < 10)) || khtml) && handleKeyBinding(e)) return;
672
+ var ch = String.fromCharCode(charCode == null ? keyCode : charCode);
673
+ if (options.electricChars && mode.electricChars && options.smartIndent && !options.readOnly) {
674
  if (mode.electricChars.indexOf(ch) > -1)
675
  setTimeout(operation(function() {indentLine(sel.to.line, "smart");}), 75);
676
  }
677
+ if (handleCharBinding(e, ch)) return;
678
  fastPoll();
679
  }
680
  function onKeyUp(e) {
681
  if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return;
682
+ if (e_prop(e, "keyCode") == 16) shiftSelecting = null;
683
  }
684
 
685
  function onFocus() {
686
+ if (options.readOnly == "nocursor") return;
687
  if (!focused) {
688
  if (options.onFocus) options.onFocus(instance);
689
  focused = true;
690
+ if (scroller.className.search(/\bCodeMirror-focused\b/) == -1)
691
+ scroller.className += " CodeMirror-focused";
692
  if (!leaveInputAlone) resetInput(true);
693
  }
694
  slowPoll();
698
  if (focused) {
699
  if (options.onBlur) options.onBlur(instance);
700
  focused = false;
701
+ if (bracketHighlighted)
702
+ operation(function(){
703
+ if (bracketHighlighted) { bracketHighlighted(); bracketHighlighted = null; }
704
+ })();
705
+ scroller.className = scroller.className.replace(" CodeMirror-focused", "");
706
  }
707
  clearInterval(blinker);
708
  setTimeout(function() {if (!focused) shiftSelecting = null;}, 150);
711
  // Replace the range from from to to by the strings in newText.
712
  // Afterwards, set the selection to selFrom, selTo.
713
  function updateLines(from, to, newText, selFrom, selTo) {
714
+ if (suppressEdits) return;
715
  if (history) {
716
  var old = [];
717
  doc.iter(from.line, to.line + 1, function(line) { old.push(line.text); });
721
  updateLinesNoUndo(from, to, newText, selFrom, selTo);
722
  }
723
  function unredoHelper(from, to) {
724
+ if (!from.length) return;
725
+ var set = from.pop(), out = [];
726
+ for (var i = set.length - 1; i >= 0; i -= 1) {
727
+ var change = set[i];
728
  var replaced = [], end = change.start + change.added;
729
  doc.iter(change.start, end, function(line) { replaced.push(line.text); });
730
+ out.push({start: change.start, added: change.old.length, old: replaced});
731
+ var pos = {line: change.start + change.old.length - 1,
732
+ ch: editEnd(replaced[replaced.length-1], change.old[change.old.length-1])};
733
  updateLinesNoUndo({line: change.start, ch: 0}, {line: end - 1, ch: getLine(end-1).text.length}, change.old, pos, pos);
 
734
  }
735
+ updateInput = true;
736
+ to.push(out);
737
  }
738
  function undo() {unredoHelper(history.done, history.undone);}
739
  function redo() {unredoHelper(history.undone, history.done);}
740
 
741
  function updateLinesNoUndo(from, to, newText, selFrom, selTo) {
742
+ if (suppressEdits) return;
743
+ var recomputeMaxLength = false, maxLineLength = maxLine.text.length;
744
  if (!options.lineWrapping)
745
+ doc.iter(from.line, to.line + 1, function(line) {
746
+ if (!line.hidden && line.text.length == maxLineLength) {recomputeMaxLength = true; return true;}
747
  });
748
  if (from.line != to.line || newText.length > 1) gutterDirty = true;
749
 
790
  doc.insert(from.line + 1, added);
791
  }
792
  if (options.lineWrapping) {
793
+ var perLine = Math.max(5, scroller.clientWidth / charWidth() - 3);
794
  doc.iter(from.line, from.line + newText.length, function(line) {
795
  if (line.hidden) return;
796
  var guess = Math.ceil(line.text.length / perLine) || 1;
797
  if (guess != line.height) updateLineHeight(line, guess);
798
  });
799
  } else {
800
+ doc.iter(from.line, from.line + newText.length, function(line) {
801
  var l = line.text;
802
+ if (!line.hidden && l.length > maxLineLength) {
803
+ maxLine = line; maxLineLength = l.length; maxLineChanged = true;
804
  recomputeMaxLength = false;
805
  }
806
  });
807
+ if (recomputeMaxLength) updateMaxLine = true;
 
 
 
 
 
 
 
 
808
  }
809
 
810
  // Add these lines to the work array, so that they will be
830
 
831
  // Update the selection
832
  function updateLine(n) {return n <= Math.min(to.line, to.line + lendiff) ? n : n + lendiff;}
833
+ setSelection(clipPos(selFrom), clipPos(selTo),
834
+ updateLine(sel.from.line), updateLine(sel.to.line));
835
+ }
836
+
837
+ function needsScrollbar() {
838
+ var realHeight = doc.height * textHeight() + 2 * paddingTop();
839
+ return realHeight * .99 > scroller.offsetHeight ? realHeight : false;
840
+ }
841
+
842
+ function updateVerticalScroll(scrollTop) {
843
+ var scrollHeight = needsScrollbar();
844
+ scrollbar.style.display = scrollHeight ? "block" : "none";
845
+ if (scrollHeight) {
846
+ scrollbarInner.style.height = sizer.style.minHeight = scrollHeight + "px";
847
+ scrollbar.style.height = scroller.clientHeight + "px";
848
+ if (scrollTop != null) {
849
+ scrollbar.scrollTop = scroller.scrollTop = scrollTop;
850
+ // 'Nudge' the scrollbar to work around a Webkit bug where,
851
+ // in some situations, we'd end up with a scrollbar that
852
+ // reported its scrollTop (and looked) as expected, but
853
+ // *behaved* as if it was still in a previous state (i.e.
854
+ // couldn't scroll up, even though it appeared to be at the
855
+ // bottom).
856
+ if (webkit) setTimeout(function() {
857
+ if (scrollbar.scrollTop != scrollTop) return;
858
+ scrollbar.scrollTop = scrollTop + (scrollTop ? -1 : 1);
859
+ scrollbar.scrollTop = scrollTop;
860
+ }, 0);
861
+ }
862
+ } else {
863
+ sizer.style.minHeight = "";
864
+ }
865
+ // Position the mover div to align with the current virtual scroll position
866
+ mover.style.top = displayOffset * textHeight() + "px";
867
+ }
868
 
869
+ function computeMaxLength() {
870
+ maxLine = getLine(0); maxLineChanged = true;
871
+ var maxLineLength = maxLine.text.length;
872
+ doc.iter(1, doc.size, function(line) {
873
+ var l = line.text;
874
+ if (!line.hidden && l.length > maxLineLength) {
875
+ maxLineLength = l.length; maxLine = line;
876
+ }
877
+ });
878
+ updateMaxLine = false;
879
  }
880
 
881
  function replaceRange(code, from, to) {
911
  updateLines(from, to, code, newSel.from, newSel.to);
912
  }
913
 
914
+ function getRange(from, to, lineSep) {
915
  var l1 = from.line, l2 = to.line;
916
  if (l1 == l2) return getLine(l1).text.slice(from.ch, to.ch);
917
  var code = [getLine(l1).text.slice(from.ch)];
918
  doc.iter(l1 + 1, l2, function(line) { code.push(line.text); });
919
  code.push(getLine(l2).text.slice(0, to.ch));
920
+ return code.join(lineSep || "\n");
921
  }
922
+ function getSelection(lineSep) {
923
+ return getRange(sel.from, sel.to, lineSep);
924
  }
925
 
 
926
  function slowPoll() {
927
  if (pollingFast) return;
928
  poll.set(options.pollInterval, function() {
952
  // supported or compatible enough yet to rely on.)
953
  var prevInput = "";
954
  function readInput() {
955
+ if (leaveInputAlone || !focused || hasSelection(input) || options.readOnly) return false;
956
  var text = input.value;
957
  if (text == prevInput) return false;
958
  shiftSelecting = null;
963
  else if (overwrite && posEq(sel.from, sel.to))
964
  sel.to = {line: sel.to.line, ch: Math.min(getLine(sel.to.line).text.length, sel.to.ch + (text.length - same))};
965
  replaceSelection(text.slice(same), "end");
966
+ if (text.length > 1000) { input.value = prevInput = ""; }
967
+ else prevInput = text;
968
  return true;
969
  }
970
  function resetInput(user) {
971
  if (!posEq(sel.from, sel.to)) {
972
  prevInput = "";
973
  input.value = getSelection();
974
+ if (focused) selectInput(input);
975
  } else if (user) prevInput = input.value = "";
976
  }
977
 
978
  function focusInput() {
979
+ if (options.readOnly != "nocursor") input.focus();
980
  }
981
 
 
 
 
 
 
 
 
 
982
  function scrollCursorIntoView() {
983
+ var coords = calculateCursorCoords();
984
+ scrollIntoView(coords.x, coords.y, coords.x, coords.yBot);
985
+ if (!focused) return;
986
+ var box = sizer.getBoundingClientRect(), doScroll = null;
987
+ if (coords.y + box.top < 0) doScroll = true;
988
+ else if (coords.y + box.top + textHeight() > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false;
989
+ if (doScroll != null) {
990
+ var hidden = cursor.style.display == "none";
991
+ if (hidden) {
992
+ cursor.style.display = "";
993
+ cursor.style.left = coords.x + "px";
994
+ cursor.style.top = (coords.y - displayOffset) + "px";
995
+ }
996
+ cursor.scrollIntoView(doScroll);
997
+ if (hidden) cursor.style.display = "none";
998
+ }
999
+ }
1000
+ function calculateCursorCoords() {
1001
  var cursor = localCoords(sel.inverted ? sel.from : sel.to);
1002
  var x = options.lineWrapping ? Math.min(cursor.x, lineSpace.offsetWidth) : cursor.x;
1003
+ return {x: x, y: cursor.y, yBot: cursor.yBot};
1004
  }
1005
  function scrollIntoView(x1, y1, x2, y2) {
1006
+ var scrollPos = calculateScrollPos(x1, y1, x2, y2);
1007
+ if (scrollPos.scrollLeft != null) {scroller.scrollLeft = scrollPos.scrollLeft;}
1008
+ if (scrollPos.scrollTop != null) {scrollbar.scrollTop = scroller.scrollTop = scrollPos.scrollTop;}
1009
+ }
1010
+ function calculateScrollPos(x1, y1, x2, y2) {
1011
+ var pl = paddingLeft(), pt = paddingTop();
1012
  y1 += pt; y2 += pt; x1 += pl; x2 += pl;
1013
+ var screen = scroller.clientHeight, screentop = scrollbar.scrollTop, result = {};
1014
+ var docBottom = needsScrollbar() || Infinity;
1015
+ var atTop = y1 < pt + 10, atBottom = y2 + pt > docBottom - 10;
1016
+ if (y1 < screentop) result.scrollTop = atTop ? 0 : Math.max(0, y1);
1017
+ else if (y2 > screentop + screen) result.scrollTop = (atBottom ? docBottom : y2) - screen;
1018
 
1019
  var screenw = scroller.clientWidth, screenleft = scroller.scrollLeft;
1020
  var gutterw = options.fixedGutter ? gutter.clientWidth : 0;
1021
+ var atLeft = x1 < gutterw + pl + 10;
1022
+ if (x1 < screenleft + gutterw || atLeft) {
1023
+ if (atLeft) x1 = 0;
1024
+ result.scrollLeft = Math.max(0, x1 - 10 - gutterw);
1025
+ } else if (x2 > screenw + screenleft - 3) {
1026
+ result.scrollLeft = x2 + 10 - screenw;
1027
  }
 
 
 
 
 
 
1028
  return result;
1029
  }
1030
 
1031
+ function visibleLines(scrollTop) {
1032
+ var lh = textHeight(), top = (scrollTop != null ? scrollTop : scrollbar.scrollTop) - paddingTop();
1033
+ var fromHeight = Math.max(0, Math.floor(top / lh));
1034
+ var toHeight = Math.ceil((top + scroller.clientHeight) / lh);
1035
+ return {from: lineAtHeight(doc, fromHeight),
1036
+ to: lineAtHeight(doc, toHeight)};
1037
  }
1038
  // Uses a set of changes plus the current scroll position to
1039
  // determine which DOM updates have to be made, and makes the
1040
  // updates.
1041
+ function updateDisplay(changes, suppressCallback, scrollTop) {
1042
  if (!scroller.clientWidth) {
1043
  showingFrom = showingTo = displayOffset = 0;
1044
  return;
1045
  }
1046
  // Compute the new visible window
1047
+ // If scrollTop is specified, use that to determine which lines
1048
+ // to render instead of the current scrollbar position.
1049
+ var visible = visibleLines(scrollTop);
1050
  // Bail out if the visible area is already rendered and nothing changed.
1051
+ if (changes !== true && changes.length == 0 && visible.from > showingFrom && visible.to < showingTo) {
1052
+ updateVerticalScroll(scrollTop);
1053
+ return;
1054
+ }
1055
  var from = Math.max(visible.from - 100, 0), to = Math.min(doc.size, visible.to + 100);
1056
  if (showingFrom < from && from - showingFrom < 20) from = showingFrom;
1057
  if (showingTo > to && showingTo - to < 20) to = Math.min(doc.size, showingTo);
1069
  if (range.from >= range.to) intact.splice(i--, 1);
1070
  else intactLines += range.to - range.from;
1071
  }
1072
+ if (intactLines == to - from && from == showingFrom && to == showingTo) {
1073
+ updateVerticalScroll(scrollTop);
1074
+ return;
1075
+ }
1076
  intact.sort(function(a, b) {return a.domStart - b.domStart;});
1077
 
1078
  var th = textHeight(), gutterDisplay = gutter.style.display;
1079
+ lineDiv.style.display = "none";
1080
  patchDisplay(from, to, intact);
1081
+ lineDiv.style.display = gutter.style.display = "";
1082
 
 
 
1083
  var different = from != showingFrom || to != showingTo || lastSizeC != scroller.clientHeight + th;
1084
  // This is just a bogus formula that detects when the editor is
1085
  // resized or the font size changes.
1086
  if (different) lastSizeC = scroller.clientHeight + th;
1087
+ if (from != showingFrom || to != showingTo && options.onViewportChange)
1088
+ setTimeout(function(){
1089
+ if (options.onViewportChange) options.onViewportChange(instance, from, to);
1090
+ });
1091
  showingFrom = from; showingTo = to;
1092
  displayOffset = heightAtLine(doc, from);
 
 
1093
 
1094
  // Since this is all rather error prone, it is honoured with the
1095
  // only assertion in the whole file.
1097
  throw new Error("BAD PATCH! " + JSON.stringify(intact) + " size=" + (showingTo - showingFrom) +
1098
  " nodes=" + lineDiv.childNodes.length);
1099
 
1100
+ function checkHeights() {
1101
+ var curNode = lineDiv.firstChild, heightChanged = false;
 
1102
  doc.iter(showingFrom, showingTo, function(line) {
1103
+ // Work around bizarro IE7 bug where, sometimes, our curNode
1104
+ // is magically replaced with a new node in the DOM, leaving
1105
+ // us with a reference to an orphan (nextSibling-less) node.
1106
+ if (!curNode) return;
1107
  if (!line.hidden) {
1108
  var height = Math.round(curNode.offsetHeight / th) || 1;
1109
+ if (line.height != height) {
1110
+ updateLineHeight(line, height);
1111
+ gutterDirty = heightChanged = true;
1112
+ }
1113
  }
1114
  curNode = curNode.nextSibling;
1115
  });
1116
+ return heightChanged;
 
 
 
 
 
 
 
 
 
1117
  }
1118
+
1119
+ if (options.lineWrapping) checkHeights();
1120
+
1121
  gutter.style.display = gutterDisplay;
1122
+ if (different || gutterDirty) {
1123
+ // If the gutter grew in size, re-check heights. If those changed, re-draw gutter.
1124
+ updateGutter() && options.lineWrapping && checkHeights() && updateGutter();
1125
+ }
1126
+ updateVerticalScroll(scrollTop);
1127
+ updateSelection();
1128
  if (!suppressCallback && options.onUpdate) options.onUpdate(instance);
1129
  return true;
1130
  }
1153
  }
1154
 
1155
  function patchDisplay(from, to, intact) {
1156
+ function killNode(node) {
1157
+ var tmp = node.nextSibling;
1158
+ node.parentNode.removeChild(node);
1159
+ return tmp;
1160
+ }
1161
  // The first pass removes the DOM nodes that aren't intact.
1162
+ if (!intact.length) removeChildren(lineDiv);
1163
  else {
 
 
 
 
 
1164
  var domPos = 0, curNode = lineDiv.firstChild, n;
1165
  for (var i = 0; i < intact.length; ++i) {
1166
  var cur = intact[i];
1171
  }
1172
  // This pass fills in the lines that actually changed.
1173
  var nextIntact = intact.shift(), curNode = lineDiv.firstChild, j = from;
 
 
1174
  doc.iter(from, to, function(line) {
 
 
 
 
 
 
 
 
1175
  if (nextIntact && nextIntact.to == j) nextIntact = intact.shift();
1176
  if (!nextIntact || nextIntact.from > j) {
1177
+ if (line.hidden) var lineElement = elt("pre");
1178
+ else {
1179
+ var lineElement = line.getElement(makeTab);
1180
+ if (line.className) lineElement.className = line.className;
1181
+ // Kludge to make sure the styled element lies behind the selection (by z-index)
1182
+ if (line.bgClassName) {
1183
+ var pre = elt("pre", "\u00a0", line.bgClassName, "position: absolute; left: 0; right: 0; top: 0; bottom: 0; z-index: -2");
1184
+ lineElement = elt("div", [pre, lineElement], null, "position: relative");
1185
+ }
1186
+ }
1187
+ lineDiv.insertBefore(lineElement, curNode);
1188
  } else {
1189
  curNode = curNode.nextSibling;
1190
  }
1196
  if (!options.gutter && !options.lineNumbers) return;
1197
  var hText = mover.offsetHeight, hEditor = scroller.clientHeight;
1198
  gutter.style.height = (hText - hEditor < 2 ? hEditor : hText) + "px";
1199
+ var fragment = document.createDocumentFragment(), i = showingFrom, normalNode;
1200
  doc.iter(showingFrom, Math.max(showingTo, showingFrom + 1), function(line) {
1201
  if (line.hidden) {
1202
+ fragment.appendChild(elt("pre"));
1203
  } else {
1204
  var marker = line.gutterMarker;
1205
+ var text = options.lineNumbers ? options.lineNumberFormatter(i + options.firstLineNumber) : null;
1206
  if (marker && marker.text)
1207
  text = marker.text.replace("%N%", text != null ? text : "");
1208
  else if (text == null)
1209
  text = "\u00a0";
1210
+ var markerElement = fragment.appendChild(elt("pre", null, marker && marker.style));
1211
+ markerElement.innerHTML = text;
1212
+ for (var j = 1; j < line.height; ++j) {
1213
+ markerElement.appendChild(elt("br"));
1214
+ markerElement.appendChild(document.createTextNode("\u00a0"));
1215
+ }
1216
+ if (!marker) normalNode = i;
1217
  }
1218
  ++i;
1219
  });
1220
  gutter.style.display = "none";
1221
+ removeChildrenAndAdd(gutterText, fragment);
1222
+ // Make sure scrolling doesn't cause number gutter size to pop
1223
+ if (normalNode != null && options.lineNumbers) {
1224
+ var node = gutterText.childNodes[normalNode - showingFrom];
1225
+ var minwidth = String(doc.size).length, val = eltText(node.firstChild), pad = "";
1226
+ while (val.length + pad.length < minwidth) pad += "\u00a0";
1227
+ if (pad) node.insertBefore(document.createTextNode(pad), node.firstChild);
1228
+ }
1229
  gutter.style.display = "";
1230
+ var resized = Math.abs((parseInt(lineSpace.style.marginLeft) || 0) - gutter.offsetWidth) > 2;
1231
  lineSpace.style.marginLeft = gutter.offsetWidth + "px";
1232
  gutterDirty = false;
1233
+ return resized;
1234
  }
1235
+ function updateSelection() {
1236
+ var collapsed = posEq(sel.from, sel.to);
1237
+ var fromPos = localCoords(sel.from, true);
1238
+ var toPos = collapsed ? fromPos : localCoords(sel.to, true);
1239
+ var headPos = sel.inverted ? fromPos : toPos, th = textHeight();
1240
  var wrapOff = eltOffset(wrapper), lineOff = eltOffset(lineDiv);
1241
+ inputDiv.style.top = Math.max(0, Math.min(scroller.offsetHeight, headPos.y + lineOff.top - wrapOff.top)) + "px";
1242
+ inputDiv.style.left = Math.max(0, Math.min(scroller.offsetWidth, headPos.x + lineOff.left - wrapOff.left)) + "px";
1243
+ if (collapsed) {
1244
+ cursor.style.top = headPos.y + "px";
1245
+ cursor.style.left = (options.lineWrapping ? Math.min(headPos.x, lineSpace.offsetWidth) : headPos.x) + "px";
1246
  cursor.style.display = "";
1247
+ selectionDiv.style.display = "none";
1248
+ } else {
1249
+ var sameLine = fromPos.y == toPos.y, fragment = document.createDocumentFragment();
1250
+ var clientWidth = lineSpace.clientWidth || lineSpace.offsetWidth;
1251
+ var clientHeight = lineSpace.clientHeight || lineSpace.offsetHeight;
1252
+ var add = function(left, top, right, height) {
1253
+ var rstyle = quirksMode ? "width: " + (!right ? clientWidth : clientWidth - right - left) + "px"
1254
+ : "right: " + right + "px";
1255
+ fragment.appendChild(elt("div", null, "CodeMirror-selected", "position: absolute; left: " + left +
1256
+ "px; top: " + top + "px; " + rstyle + "; height: " + height + "px"));
1257
+ };
1258
+ if (sel.from.ch && fromPos.y >= 0) {
1259
+ var right = sameLine ? clientWidth - toPos.x : 0;
1260
+ add(fromPos.x, fromPos.y, right, th);
1261
+ }
1262
+ var middleStart = Math.max(0, fromPos.y + (sel.from.ch ? th : 0));
1263
+ var middleHeight = Math.min(toPos.y, clientHeight) - middleStart;
1264
+ if (middleHeight > 0.2 * th)
1265
+ add(0, middleStart, 0, middleHeight);
1266
+ if ((!sameLine || !sel.from.ch) && toPos.y < clientHeight - .5 * th)
1267
+ add(0, toPos.y, clientWidth - toPos.x, th);
1268
+ removeChildrenAndAdd(selectionDiv, fragment);
1269
+ cursor.style.display = "none";
1270
+ selectionDiv.style.display = "";
1271
  }
 
1272
  }
1273
 
1274
  function setShift(val) {
1294
  if (posLess(to, from)) {var tmp = to; to = from; from = tmp;}
1295
 
1296
  // Skip over hidden lines.
1297
+ if (from.line != oldFrom) {
1298
+ var from1 = skipHidden(from, oldFrom, sel.from.ch);
1299
+ // If there is no non-hidden line left, force visibility on current line
1300
+ if (!from1) setLineHidden(from.line, false);
1301
+ else from = from1;
1302
+ }
1303
  if (to.line != oldTo) to = skipHidden(to, oldTo, sel.to.ch);
1304
 
1305
  if (posEq(from, to)) sel.inverted = false;
1306
  else if (posEq(from, sel.to)) sel.inverted = false;
1307
  else if (posEq(to, sel.from)) sel.inverted = true;
1308
 
1309
+ if (options.autoClearEmptyLines && posEq(sel.from, sel.to)) {
1310
+ var head = sel.inverted ? from : to;
1311
+ if (head.line != sel.from.line && sel.from.line < doc.size) {
1312
+ var oldLine = getLine(sel.from.line);
1313
+ if (/^\s+$/.test(oldLine.text))
1314
+ setTimeout(operation(function() {
1315
+ if (oldLine.parent && /^\s+$/.test(oldLine.text)) {
1316
+ var no = lineNo(oldLine);
1317
+ replaceRange("", {line: no, ch: 0}, {line: no, ch: oldLine.text.length});
1318
+ }
1319
+ }, 10));
 
 
 
 
 
 
 
 
 
 
 
1320
  }
1321
  }
1322
+
1323
  sel.from = from; sel.to = to;
1324
  selectionChanged = true;
1325
  }
1330
  var line = getLine(lNo);
1331
  if (!line.hidden) {
1332
  var ch = pos.ch;
1333
+ if (toEnd || ch > oldCh || ch > line.text.length) ch = line.text.length;
1334
  return {line: lNo, ch: ch};
1335
  }
1336
  lNo += dir;
1337
  }
1338
  }
1339
  var line = getLine(pos.line);
1340
+ var toEnd = pos.ch == line.text.length && pos.ch != oldCh;
1341
  if (!line.hidden) return pos;
1342
  if (pos.line >= oldLine) return getNonHidden(1) || getNonHidden(-1);
1343
  else return getNonHidden(-1) || getNonHidden(1);
1397
  else replaceRange("", sel.from, findPosH(dir, unit));
1398
  userSelChange = true;
1399
  }
 
1400
  function moveV(dir, unit) {
1401
  var dist = 0, pos = localCoords(sel.inverted ? sel.from : sel.to, true);
1402
  if (goalColumn != null) pos.x = goalColumn;
1403
+ if (unit == "page") {
1404
+ var screen = Math.min(scroller.clientHeight, window.innerHeight || document.documentElement.clientHeight);
1405
+ var target = coordsChar(pos.x, pos.y + screen * dir);
1406
+ } else if (unit == "line") {
1407
+ var th = textHeight();
1408
+ var target = coordsChar(pos.x, pos.y + .5 * th + dir * th);
1409
+ }
1410
+ if (unit == "page") scrollbar.scrollTop += localCoords(target, true).y - pos.y;
1411
  setCursor(target.line, target.ch, true);
1412
  goalColumn = pos.x;
1413
  }
1414
 
1415
+ function findWordAt(pos) {
1416
  var line = getLine(pos.line).text;
1417
  var start = pos.ch, end = pos.ch;
1418
+ if (line) {
1419
+ if (pos.after === false || end == line.length) --start; else ++end;
1420
+ var startChar = line.charAt(start);
1421
+ var check = isWordChar(startChar) ? isWordChar :
1422
+ /\s/.test(startChar) ? function(ch) {return /\s/.test(ch);} :
1423
+ function(ch) {return !/\s/.test(ch) && !isWordChar(ch);};
1424
+ while (start > 0 && check(line.charAt(start - 1))) --start;
1425
+ while (end < line.length && check(line.charAt(end))) ++end;
1426
+ }
1427
+ return {from: {line: pos.line, ch: start}, to: {line: pos.line, ch: end}};
1428
  }
1429
  function selectLine(line) {
1430
+ setSelectionUser({line: line, ch: 0}, clipPos({line: line + 1, ch: 0}));
1431
  }
1432
  function indentSelected(mode) {
1433
  if (posEq(sel.from, sel.to)) return indentLine(sel.from.line, mode);
1444
 
1445
  var line = getLine(n), curSpace = line.indentation(options.tabSize),
1446
  curSpaceString = line.text.match(/^\s*/)[0], indentation;
1447
+ if (how == "smart") {
1448
+ indentation = mode.indent(state, line.text.slice(curSpaceString.length), line.text);
1449
+ if (indentation == Pass) how = "prev";
1450
+ }
1451
  if (how == "prev") {
1452
  if (n) indentation = getLine(n-1).indentation(options.tabSize);
1453
  else indentation = 0;
1454
  }
 
1455
  else if (how == "add") indentation = curSpace + options.indentUnit;
1456
  else if (how == "subtract") indentation = curSpace - options.indentUnit;
1457
  indentation = Math.max(0, indentation);
1458
  var diff = indentation - curSpace;
1459
 
1460
+ var indentString = "", pos = 0;
1461
+ if (options.indentWithTabs)
1462
+ for (var i = Math.floor(indentation / options.tabSize); i; --i) {pos += options.tabSize; indentString += "\t";}
1463
+ while (pos < indentation) {++pos; indentString += " ";}
 
 
 
 
 
 
1464
 
1465
+ if (indentString != curSpaceString)
1466
+ replaceRange(indentString, {line: n, ch: 0}, {line: n, ch: curSpaceString.length});
1467
  }
1468
 
1469
  function loadMode() {
1487
  var guess = Math.ceil(line.text.length / perLine) || 1;
1488
  if (guess != 1) updateLineHeight(line, guess);
1489
  });
1490
+ lineSpace.style.minWidth = widthForcer.style.left = "";
1491
  } else {
1492
  wrapper.className = wrapper.className.replace(" CodeMirror-wrap", "");
1493
+ computeMaxLength();
1494
  doc.iter(0, doc.size, function(line) {
1495
  if (line.height != 1 && !line.hidden) updateLineHeight(line, 1);
 
1496
  });
1497
  }
1498
  changes.push({from: 0, to: doc.size});
1499
  }
1500
+ function makeTab(col) {
1501
+ var w = options.tabSize - col % options.tabSize, cached = tabCache[w];
1502
+ if (cached) return cached;
1503
+ for (var str = "", i = 0; i < w; ++i) str += " ";
1504
+ var span = elt("span", str, "cm-tab");
1505
+ return (tabCache[w] = {element: span, width: w});
 
1506
  }
1507
  function themeChanged() {
1508
+ scroller.className = scroller.className.replace(/\s*cm-s-\S+/g, "") +
1509
  options.theme.replace(/(^|\s)\s*/g, " cm-s-");
1510
  }
1511
+ function keyMapChanged() {
1512
+ var style = keyMap[options.keyMap].style;
1513
+ wrapper.className = wrapper.className.replace(/\s*cm-keymap-\S+/g, "") +
1514
+ (style ? " cm-keymap-" + style : "");
1515
+ }
1516
 
1517
  function TextMarker() { this.set = []; }
1518
  TextMarker.prototype.clear = operation(function() {
1523
  var lineN = lineNo(line);
1524
  min = Math.min(min, lineN); max = Math.max(max, lineN);
1525
  for (var j = 0; j < mk.length; ++j)
1526
+ if (mk[j].marker == this) mk.splice(j--, 1);
1527
  }
1528
  if (min != Infinity)
1529
  changes.push({from: min, to: max + 1});
1534
  var line = this.set[i], mk = line.marked;
1535
  for (var j = 0; j < mk.length; ++j) {
1536
  var mark = mk[j];
1537
+ if (mark.marker == this) {
1538
  if (mark.from != null || mark.to != null) {
1539
  var found = lineNo(line);
1540
  if (found != null) {
1551
  function markText(from, to, className) {
1552
  from = clipPos(from); to = clipPos(to);
1553
  var tm = new TextMarker();
1554
+ if (!posLess(from, to)) return tm;
1555
  function add(line, from, to, className) {
1556
+ getLine(line).addMark(new MarkedText(from, to, className, tm));
1557
  }
1558
  if (from.line == to.line) add(from.line, from.ch, to.ch, className);
1559
  else {
1573
  return bm;
1574
  }
1575
 
1576
+ function findMarksAt(pos) {
1577
+ pos = clipPos(pos);
1578
+ var markers = [], marked = getLine(pos.line).marked;
1579
+ if (!marked) return markers;
1580
+ for (var i = 0, e = marked.length; i < e; ++i) {
1581
+ var m = marked[i];
1582
+ if ((m.from == null || m.from <= pos.ch) &&
1583
+ (m.to == null || m.to >= pos.ch))
1584
+ markers.push(m.marker || m);
1585
+ }
1586
+ return markers;
1587
+ }
1588
+
1589
  function addGutterMarker(line, text, className) {
1590
  if (typeof line == "number") line = getLine(clipLine(line));
1591
  line.gutterMarker = {text: text, style: className};
1607
  else return null;
1608
  return line;
1609
  }
1610
+ function setLineClass(handle, className, bgClassName) {
1611
  return changeLine(handle, function(line) {
1612
+ if (line.className != className || line.bgClassName != bgClassName) {
1613
  line.className = className;
1614
+ line.bgClassName = bgClassName;
1615
  return true;
1616
  }
1617
  });
1620
  return changeLine(handle, function(line, no) {
1621
  if (line.hidden != hidden) {
1622
  line.hidden = hidden;
1623
+ if (!options.lineWrapping) {
1624
+ if (hidden && line.text.length == maxLine.text.length) {
1625
+ updateMaxLine = true;
1626
+ } else if (!hidden && line.text.length > maxLine.text.length) {
1627
+ maxLine = line; updateMaxLine = false;
1628
+ }
1629
+ }
1630
  updateLineHeight(line, hidden ? 0 : 1);
1631
+ var fline = sel.from.line, tline = sel.to.line;
1632
+ if (hidden && (fline == no || tline == no)) {
1633
+ var from = fline == no ? skipHidden({line: fline, ch: 0}, fline, 0) : sel.from;
1634
+ var to = tline == no ? skipHidden({line: tline, ch: 0}, tline, 0) : sel.to;
1635
+ // Can't hide the last visible line, we'd have no place to put the cursor
1636
+ if (!to) return;
1637
+ setSelection(from, to);
1638
+ }
1639
  return (gutterDirty = true);
1640
  }
1641
  });
1647
  var n = line;
1648
  line = getLine(line);
1649
  if (!line) return null;
1650
+ } else {
 
1651
  var n = lineNo(line);
1652
  if (n == null) return null;
1653
  }
1654
  var marker = line.gutterMarker;
1655
  return {line: n, handle: line, text: line.text, markerText: marker && marker.text,
1656
+ markerClass: marker && marker.style, lineClass: line.className, bgClass: line.bgClassName};
1657
  }
1658
 
 
 
 
 
 
1659
  // These are used to go from pixel positions to character
1660
  // positions, taking varying character widths into account.
1661
  function charFromX(line, x) {
1662
  if (x <= 0) return 0;
1663
  var lineObj = getLine(line), text = lineObj.text;
1664
  function getX(len) {
1665
+ return measureLine(lineObj, len).left;
 
1666
  }
1667
  var from = 0, fromX = 0, to = text.length, toX;
1668
  // Guess a suitable upper bound for our search.
1685
  }
1686
  }
1687
 
 
1688
  function measureLine(line, ch) {
1689
+ if (ch == 0) return {top: 0, left: 0};
1690
+ var wbr = options.lineWrapping && ch < line.text.length &&
1691
+ spanAffectsWrapping.test(line.text.slice(ch - 1, ch + 1));
1692
+ var pre = line.getElement(makeTab, ch, wbr);
1693
+ removeChildrenAndAdd(measure, pre);
1694
+ var anchor = pre.anchor;
1695
+ var top = anchor.offsetTop, left = anchor.offsetLeft;
 
 
 
 
1696
  // Older IEs report zero offsets for spans directly after a wrap
1697
+ if (ie && top == 0 && left == 0) {
1698
+ var backup = elt("span", "x");
1699
+ anchor.parentNode.insertBefore(backup, anchor.nextSibling);
 
1700
  top = backup.offsetTop;
1701
  }
1702
  return {top: top, left: left};
1713
  }
1714
  // Coords must be lineSpace-local
1715
  function coordsChar(x, y) {
 
1716
  var th = textHeight(), cw = charWidth(), heightPos = displayOffset + Math.floor(y / th);
1717
+ if (heightPos < 0) return {line: 0, ch: 0};
1718
  var lineNo = lineAtHeight(doc, heightPos);
1719
  if (lineNo >= doc.size) return {line: doc.size - 1, ch: getLine(doc.size - 1).text.length};
1720
  var lineObj = getLine(lineNo), text = lineObj.text;
1721
  var tw = options.lineWrapping, innerOff = tw ? heightPos - heightAtLine(doc, lineNo) : 0;
1722
  if (x <= 0 && innerOff == 0) return {line: lineNo, ch: 0};
1723
+ var wrongLine = false;
1724
  function getX(len) {
1725
  var sp = measureLine(lineObj, len);
1726
  if (tw) {
1727
  var off = Math.round(sp.top / th);
1728
+ wrongLine = off != innerOff;
1729
  return Math.max(0, sp.left + (off - innerOff) * scroller.clientWidth);
1730
  }
1731
  return sp.left;
1744
  if (estX < x) {from = estimated; fromX = estX;}
1745
  // Do a binary search between these bounds.
1746
  for (;;) {
1747
+ if (to - from <= 1) {
1748
+ var after = x - fromX < toX - x;
1749
+ return {line: lineNo, ch: after ? from : to, after: after};
1750
+ }
1751
  var middle = Math.ceil((from + to) / 2), middleX = getX(middle);
1752
+ if (middleX > x) {to = middle; toX = middleX; if (wrongLine) toX += 1000; }
1753
  else {from = middle; fromX = middleX;}
1754
  }
1755
  }
1758
  return {x: off.left + local.x, y: off.top + local.y, yBot: off.top + local.yBot};
1759
  }
1760
 
1761
+ var cachedHeight, cachedHeightFor, measurePre;
1762
  function textHeight() {
1763
+ if (measurePre == null) {
1764
+ measurePre = elt("pre");
1765
+ for (var i = 0; i < 49; ++i) {
1766
+ measurePre.appendChild(document.createTextNode("x"));
1767
+ measurePre.appendChild(elt("br"));
1768
+ }
1769
+ measurePre.appendChild(document.createTextNode("x"));
1770
  }
1771
  var offsetHeight = lineDiv.clientHeight;
1772
  if (offsetHeight == cachedHeightFor) return cachedHeight;
1773
  cachedHeightFor = offsetHeight;
1774
+ removeChildrenAndAdd(measure, measurePre.cloneNode(true));
1775
  cachedHeight = measure.firstChild.offsetHeight / 50 || 1;
1776
+ removeChildren(measure);
1777
  return cachedHeight;
1778
  }
1779
  var cachedWidth, cachedWidthFor = 0;
1780
  function charWidth() {
1781
  if (scroller.clientWidth == cachedWidthFor) return cachedWidth;
1782
  cachedWidthFor = scroller.clientWidth;
1783
+ var anchor = elt("span", "x");
1784
+ var pre = elt("pre", [anchor]);
1785
+ removeChildrenAndAdd(measure, pre);
1786
+ return (cachedWidth = anchor.offsetWidth || 10);
1787
  }
1788
  function paddingTop() {return lineSpace.offsetTop;}
1789
  function paddingLeft() {return lineSpace.offsetLeft;}
1801
  return coordsChar(x - offL.left, y - offL.top);
1802
  }
1803
  function onContextMenu(e) {
1804
+ var pos = posFromMouse(e), scrollPos = scrollbar.scrollTop;
1805
+ if (!pos || opera) return; // Opera is difficult.
1806
  if (posEq(sel.from, sel.to) || posLess(pos, sel.from) || !posLess(pos, sel.to))
1807
  operation(setCursor)(pos.line, pos.ch);
1808
 
1814
  leaveInputAlone = true;
1815
  var val = input.value = getSelection();
1816
  focusInput();
1817
+ selectInput(input);
1818
  function rehide() {
1819
  var newVal = splitLines(input.value).join("\n");
1820
+ if (newVal != val && !options.readOnly) operation(replaceSelection)(newVal, "end");
1821
  inputDiv.style.position = "relative";
1822
  input.style.cssText = oldCSS;
1823
+ if (ie_lt9) scrollbar.scrollTop = scrollPos;
1824
  leaveInputAlone = false;
1825
  resetInput(true);
1826
  slowPoll();
1832
  mouseup();
1833
  setTimeout(rehide, 20);
1834
  }, true);
1835
+ } else {
 
1836
  setTimeout(rehide, 50);
1837
  }
1838
  }
1844
  cursor.style.visibility = "";
1845
  blinker = setInterval(function() {
1846
  cursor.style.visibility = (on = !on) ? "" : "hidden";
1847
+ }, options.cursorBlinkRate);
1848
  }
1849
 
1850
  var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<"};
1862
  var st = line.styles, pos = forward ? 0 : line.text.length - 1, cur;
1863
  for (var i = forward ? 0 : st.length - 2, e = forward ? st.length : -2; i != e; i += 2*d) {
1864
  var text = st[i];
1865
+ if (st[i+1] != style) {pos += d * text.length; continue;}
1866
  for (var j = forward ? 0 : text.length - 1, te = forward ? text.length : -1; j != te; j += d, pos+=d) {
1867
  if (pos >= from && pos < to && re.test(cur = text.charAt(j))) {
1868
  var match = matching[cur];
1949
  var changed = line.highlight(mode, state, options.tabSize);
1950
  if (changed) realChange = true;
1951
  line.stateAfter = copyState(mode, state);
1952
+ var done = null;
1953
  if (compare) {
1954
+ var same = hadState && compare(hadState, state);
1955
+ if (same != Pass) done = !!same;
1956
+ }
1957
+ if (done == null) {
1958
  if (changed !== false || !hadState) unchanged = 0;
1959
  else if (++unchanged > 3 && (!mode.indent || mode.indent(hadState, "") == mode.indent(state, "")))
1960
+ done = true;
1961
  }
1962
+ if (done) return true;
1963
  ++i;
1964
  });
1965
  if (bail) return;
1982
  changes = []; selectionChanged = false; callbacks = [];
1983
  }
1984
  function endOperation() {
1985
+ if (updateMaxLine) computeMaxLength();
1986
+ if (maxLineChanged && !options.lineWrapping) {
1987
+ var cursorWidth = widthForcer.offsetWidth, left = measureLine(maxLine, maxLine.text.length).left;
1988
+ if (!ie_lt8) {
1989
+ widthForcer.style.left = left + "px";
1990
+ lineSpace.style.minWidth = (left + cursorWidth) + "px";
1991
+ }
1992
+ maxLineChanged = false;
1993
+ }
1994
+ var newScrollPos, updated;
1995
+ if (selectionChanged) {
1996
+ var coords = calculateCursorCoords();
1997
+ newScrollPos = calculateScrollPos(coords.x, coords.y, coords.x, coords.yBot);
1998
+ }
1999
+ if (changes.length || newScrollPos && newScrollPos.scrollTop != null)
2000
+ updated = updateDisplay(changes, true, newScrollPos && newScrollPos.scrollTop);
2001
+ if (!updated) {
2002
+ if (selectionChanged) updateSelection();
2003
  if (gutterDirty) updateGutter();
2004
  }
2005
+ if (newScrollPos) scrollCursorIntoView();
2006
+ if (selectionChanged) restartBlink();
2007
 
2008
  if (focused && !leaveInputAlone &&
2009
  (updateInput === true || (updateInput !== false && selectionChanged)))
2014
  if (bracketHighlighted) {bracketHighlighted(); bracketHighlighted = null;}
2015
  if (posEq(sel.from, sel.to)) matchBrackets(false);
2016
  }), 20);
2017
+ var sc = selectionChanged, cbs = callbacks; // these can be reset by callbacks
2018
+ if (textChanged && options.onChange && instance)
2019
+ options.onChange(instance, textChanged);
2020
+ if (sc && options.onCursorActivity)
2021
  options.onCursorActivity(instance);
 
 
2022
  for (var i = 0; i < cbs.length; ++i) cbs[i](instance);
2023
  if (updated && options.onUpdate) options.onUpdate(instance);
2024
  }
2032
  };
2033
  }
2034
 
2035
+ function compoundChange(f) {
2036
+ history.startCompound();
2037
+ try { return f(); } finally { history.endCompound(); }
2038
+ }
2039
+
2040
  for (var ext in extensions)
2041
  if (extensions.propertyIsEnumerable(ext) &&
2042
  !instance.propertyIsEnumerable(ext))
2051
  theme: "default",
2052
  indentUnit: 2,
2053
  indentWithTabs: false,
2054
+ smartIndent: true,
2055
  tabSize: 4,
2056
  keyMap: "default",
2057
  extraKeys: null,
2058
  electricChars: true,
2059
+ autoClearEmptyLines: false,
2060
  onKeyEvent: null,
2061
+ onDragEvent: null,
2062
  lineWrapping: false,
2063
  lineNumbers: false,
2064
  gutter: false,
2065
  fixedGutter: false,
2066
  firstLineNumber: 1,
2067
  readOnly: false,
2068
+ dragDrop: true,
2069
  onChange: null,
2070
  onCursorActivity: null,
2071
+ onViewportChange: null,
2072
  onGutterClick: null,
2073
  onHighlightComplete: null,
2074
  onUpdate: null,
2075
  onFocus: null, onBlur: null, onScroll: null,
2076
  matchBrackets: false,
2077
+ cursorBlinkRate: 530,
2078
  workTime: 100,
2079
  workDelay: 200,
2080
  pollInterval: 100,
2081
  undoDepth: 40,
2082
  tabindex: null,
2083
+ autofocus: null,
2084
+ lineNumberFormatter: function(integer) { return integer; }
2085
  };
2086
 
2087
+ var ios = /AppleWebKit/.test(navigator.userAgent) && /Mobile\/\w+/.test(navigator.userAgent);
2088
+ var mac = ios || /Mac/.test(navigator.platform);
2089
  var win = /Win/.test(navigator.platform);
2090
 
2091
  // Known modes, by name and by MIME
2092
+ var modes = CodeMirror.modes = {}, mimeModes = CodeMirror.mimeModes = {};
2093
  CodeMirror.defineMode = function(name, mode) {
2094
  if (!CodeMirror.defaults.mode && name != "null") CodeMirror.defaults.mode = name;
2095
+ if (arguments.length > 2) {
2096
+ mode.dependencies = [];
2097
+ for (var i = 2; i < arguments.length; ++i) mode.dependencies.push(arguments[i]);
2098
+ }
2099
  modes[name] = mode;
2100
  };
2101
  CodeMirror.defineMIME = function(mime, spec) {
2102
  mimeModes[mime] = spec;
2103
  };
2104
+ CodeMirror.resolveMode = function(spec) {
2105
  if (typeof spec == "string" && mimeModes.hasOwnProperty(spec))
2106
  spec = mimeModes[spec];
2107
+ else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec))
2108
+ return CodeMirror.resolveMode("application/xml");
2109
+ if (typeof spec == "string") return {name: spec};
2110
+ else return spec || {name: "null"};
2111
+ };
2112
+ CodeMirror.getMode = function(options, spec) {
2113
+ var spec = CodeMirror.resolveMode(spec);
2114
+ var mfactory = modes[spec.name];
2115
+ if (!mfactory) return CodeMirror.getMode(options, "text/plain");
2116
+ return mfactory(options, spec);
2117
  };
2118
  CodeMirror.listModes = function() {
2119
  var list = [];
2170
  indentMore: function(cm) {cm.indentSelection("add");},
2171
  indentLess: function(cm) {cm.indentSelection("subtract");},
2172
  insertTab: function(cm) {cm.replaceSelection("\t", "end");},
2173
+ defaultTab: function(cm) {
2174
+ if (cm.somethingSelected()) cm.indentSelection("add");
2175
+ else cm.replaceSelection("\t", "end");
2176
+ },
2177
  transposeChars: function(cm) {
2178
  var cur = cm.getCursor(), line = cm.getLine(cur.line);
2179
  if (cur.ch > 0 && cur.ch < line.length - 1)
2191
  keyMap.basic = {
2192
  "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
2193
  "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
2194
+ "Delete": "delCharRight", "Backspace": "delCharLeft", "Tab": "defaultTab", "Shift-Tab": "indentAuto",
2195
  "Enter": "newlineAndIndent", "Insert": "toggleOverwrite"
2196
  };
2197
  // Note that the save and find-related commands aren't defined by
2202
  "Ctrl-Left": "goWordLeft", "Ctrl-Right": "goWordRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
2203
  "Ctrl-Backspace": "delWordLeft", "Ctrl-Delete": "delWordRight", "Ctrl-S": "save", "Ctrl-F": "find",
2204
  "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
2205
+ "Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
2206
  fallthrough: "basic"
2207
  };
2208
  keyMap.macDefault = {
2211
  "Alt-Right": "goWordRight", "Cmd-Left": "goLineStart", "Cmd-Right": "goLineEnd", "Alt-Backspace": "delWordLeft",
2212
  "Ctrl-Alt-Backspace": "delWordRight", "Alt-Delete": "delWordRight", "Cmd-S": "save", "Cmd-F": "find",
2213
  "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
2214
+ "Cmd-[": "indentLess", "Cmd-]": "indentMore",
2215
  fallthrough: ["basic", "emacsy"]
2216
  };
2217
  keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault;
2222
  "Alt-D": "delWordRight", "Alt-Backspace": "delWordLeft", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars"
2223
  };
2224
 
2225
+ function getKeyMap(val) {
2226
+ if (typeof val == "string") return keyMap[val];
2227
+ else return val;
2228
+ }
2229
+ function lookupKey(name, extraMap, map, handle, stop) {
2230
+ function lookup(map) {
2231
+ map = getKeyMap(map);
2232
  var found = map[name];
2233
+ if (found === false) {
2234
+ if (stop) stop();
2235
+ return true;
2236
+ }
2237
+ if (found != null && handle(found)) return true;
2238
+ if (map.nofallthrough) {
2239
+ if (stop) stop();
2240
+ return true;
2241
+ }
2242
+ var fallthrough = map.fallthrough;
2243
+ if (fallthrough == null) return false;
2244
+ if (Object.prototype.toString.call(fallthrough) != "[object Array]")
2245
+ return lookup(fallthrough);
2246
+ for (var i = 0, e = fallthrough.length; i < e; ++i) {
2247
+ if (lookup(fallthrough[i])) return true;
2248
  }
2249
+ return false;
2250
  }
2251
+ if (extraMap && lookup(extraMap)) return true;
2252
+ return lookup(map);
2253
  }
2254
  function isModifierKey(event) {
2255
+ var name = keyNames[e_prop(event, "keyCode")];
2256
  return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod";
2257
  }
2258
 
2261
  options.value = textarea.value;
2262
  if (!options.tabindex && textarea.tabindex)
2263
  options.tabindex = textarea.tabindex;
2264
+ // Set autofocus to true if this textarea is focused, or if it has
2265
+ // autofocus and no other element is focused.
2266
+ if (options.autofocus == null) {
2267
+ var hasFocus = document.body;
2268
+ // doc.activeElement occasionally throws on IE
2269
+ try { hasFocus = document.activeElement; } catch(e) {}
2270
+ options.autofocus = hasFocus == textarea ||
2271
+ textarea.getAttribute("autofocus") != null && hasFocus == document.body;
2272
+ }
2273
 
2274
  function save() {textarea.value = instance.getValue();}
2275
  if (textarea.form) {
2277
  var rmSubmit = connect(textarea.form, "submit", save, true);
2278
  if (typeof textarea.form.submit == "function") {
2279
  var realSubmit = textarea.form.submit;
2280
+ textarea.form.submit = function wrappedSubmit() {
2281
  save();
2282
  textarea.form.submit = realSubmit;
2283
  textarea.form.submit();
2284
  textarea.form.submit = wrappedSubmit;
2285
+ };
 
2286
  }
2287
  }
2288
 
2305
  return instance;
2306
  };
2307
 
2308
+ var gecko = /gecko\/\d{7}/i.test(navigator.userAgent);
2309
+ var ie = /MSIE \d/.test(navigator.userAgent);
2310
+ var ie_lt8 = /MSIE [1-7]\b/.test(navigator.userAgent);
2311
+ var ie_lt9 = /MSIE [1-8]\b/.test(navigator.userAgent);
2312
+ var quirksMode = ie && document.documentMode == 5;
2313
+ var webkit = /WebKit\//.test(navigator.userAgent);
2314
+ var chrome = /Chrome\//.test(navigator.userAgent);
2315
+ var opera = /Opera\//.test(navigator.userAgent);
2316
+ var safari = /Apple Computer/.test(navigator.vendor);
2317
+ var khtml = /KHTML\//.test(navigator.userAgent);
2318
+ var mac_geLion = /Mac OS X 10\D([7-9]|\d\d)\D/.test(navigator.userAgent);
2319
+
2320
  // Utility functions for working with state. Exported because modes
2321
  // sometimes need to do this.
2322
  function copyState(mode, state) {
2345
  StringStream.prototype = {
2346
  eol: function() {return this.pos >= this.string.length;},
2347
  sol: function() {return this.pos == 0;},
2348
+ peek: function() {return this.string.charAt(this.pos) || undefined;},
2349
  next: function() {
2350
  if (this.pos < this.string.length)
2351
  return this.string.charAt(this.pos++);
2376
  indentation: function() {return countColumn(this.string, null, this.tabSize);},
2377
  match: function(pattern, consume, caseInsensitive) {
2378
  if (typeof pattern == "string") {
2379
+ var cased = function(str) {return caseInsensitive ? str.toLowerCase() : str;};
2380
  if (cased(this.string).indexOf(cased(pattern), this.pos) == this.pos) {
2381
  if (consume !== false) this.pos += pattern.length;
2382
  return true;
2383
  }
2384
+ } else {
 
2385
  var match = this.string.slice(this.pos).match(pattern);
2386
  if (match && consume !== false) this.pos += match[0].length;
2387
  return match;
2391
  };
2392
  CodeMirror.StringStream = StringStream;
2393
 
2394
+ function MarkedText(from, to, className, marker) {
2395
+ this.from = from; this.to = to; this.style = className; this.marker = marker;
2396
  }
2397
  MarkedText.prototype = {
2398
+ attach: function(line) { this.marker.set.push(line); },
2399
  detach: function(line) {
2400
+ var ix = indexOf(this.marker.set, line);
2401
+ if (ix > -1) this.marker.set.splice(ix, 1);
2402
  },
2403
  split: function(pos, lenBefore) {
2404
  if (this.to <= pos && this.to != null) return null;
2405
  var from = this.from < pos || this.from == null ? null : this.from - pos + lenBefore;
2406
  var to = this.to == null ? null : this.to - pos + lenBefore;
2407
+ return new MarkedText(from, to, this.style, this.marker);
2408
  },
2409
+ dup: function() { return new MarkedText(null, null, this.style, this.marker); },
2410
  clipTo: function(fromOpen, from, toOpen, to, diff) {
 
 
 
 
2411
  if (fromOpen && to > this.from && (to < this.to || this.to == null))
2412
  this.from = null;
2413
+ else if (this.from != null && this.from >= from)
2414
+ this.from = Math.max(to, this.from) + diff;
2415
  if (toOpen && (from < this.to || this.to == null) && (from > this.from || this.from == null))
2416
  this.to = null;
2417
+ else if (this.to != null && this.to > from)
2418
+ this.to = to < this.to ? this.to + diff : from;
2419
  },
2420
  isDead: function() { return this.from != null && this.to != null && this.from >= this.to; },
2421
+ sameSet: function(x) { return this.marker == x.marker; }
2422
  };
2423
 
2424
  function Bookmark(pos) {
2455
  }
2456
  };
2457
 
2458
+ // When measuring the position of the end of a line, different
2459
+ // browsers require different approaches. If an empty span is added,
2460
+ // many browsers report bogus offsets. Of those, some (Webkit,
2461
+ // recent IE) will accept a space without moving the whole span to
2462
+ // the next line when wrapping it, others work with a zero-width
2463
+ // space.
2464
+ var eolSpanContent = " ";
2465
+ if (gecko || (ie && !ie_lt8)) eolSpanContent = "\u200b";
2466
+ else if (opera) eolSpanContent = "";
2467
+
2468
  // Line objects. These hold state related to a line, including
2469
  // highlighting info (the styles array).
2470
  function Line(text, styles) {
2471
  this.styles = styles || [text, null];
2472
  this.text = text;
2473
  this.height = 1;
 
 
2474
  }
2475
  Line.inheritMarks = function(text, orig) {
2476
  var ln = new Line(text), mk = orig && orig.marked;
2483
  }
2484
  }
2485
  return ln;
2486
+ };
2487
  Line.prototype = {
2488
  // Replace a piece of a line, keeping the styles around it intact.
2489
  replace: function(from, to_, text) {
2496
  this.stateAfter = null;
2497
  if (mk) {
2498
  var diff = text.length - (to - from);
2499
+ for (var i = 0; i < mk.length; ++i) {
2500
+ var mark = mk[i];
2501
  mark.clipTo(from == null, from || 0, to_ == null, to, diff);
2502
  if (mark.isDead()) {mark.detach(this); mk.splice(i--, 1);}
2503
  }
2515
  if (newmark) {
2516
  if (!taken.marked) taken.marked = [];
2517
  taken.marked.push(newmark); newmark.attach(taken);
2518
+ if (newmark == mark) mk.splice(i--, 1);
2519
  }
2520
  }
2521
  }
2556
  fixMarkEnds: function(other) {
2557
  var mk = this.marked, omk = other.marked;
2558
  if (!mk) return;
2559
+ outer: for (var i = 0; i < mk.length; ++i) {
2560
  var mark = mk[i], close = mark.to == null;
2561
  if (close && omk) {
2562
+ for (var j = 0; j < omk.length; ++j) {
2563
+ var om = omk[j];
2564
+ if (!om.sameSet(mark) || om.from != null) continue;
2565
+ if (mark.from == this.text.length && om.to == 0) {
2566
+ omk.splice(j, 1);
2567
+ mk.splice(i--, 1);
2568
+ continue outer;
2569
+ } else {
2570
+ close = false; break;
2571
+ }
2572
+ }
2573
  }
2574
  if (close) mark.to = this.text.length;
2575
  }
2619
  },
2620
  // Fetch the parser token for a given character. Useful for hacks
2621
  // that want to inspect the mode state (say, for completion).
2622
+ getTokenAt: function(mode, state, tabSize, ch) {
2623
+ var txt = this.text, stream = new StringStream(txt, tabSize);
2624
  while (stream.pos < ch && !stream.eol()) {
2625
  stream.start = stream.pos;
2626
  var style = mode.token(stream, state);
2634
  indentation: function(tabSize) {return countColumn(this.text, null, tabSize);},
2635
  // Produces an HTML fragment for the line, taking selection,
2636
  // marking, and highlighting into account.
2637
+ getElement: function(makeTab, wrapAt, wrapWBR) {
2638
+ var first = true, col = 0, specials = /[\t\u0000-\u0019\u200b\u2028\u2029\uFEFF]/g;
2639
+ var pre = elt("pre");
2640
+ function span_(html, text, style) {
 
2641
  if (!text) return;
2642
  // Work around a bug where, in some compat modes, IE ignores leading spaces
2643
  if (first && ie && text.charAt(0) == " ") text = "\u00a0" + text.slice(1);
2644
  first = false;
2645
+ if (!specials.test(text)) {
2646
+ col += text.length;
2647
+ var content = document.createTextNode(text);
2648
+ } else {
2649
+ var content = document.createDocumentFragment(), pos = 0;
2650
+ while (true) {
2651
+ specials.lastIndex = pos;
2652
+ var m = specials.exec(text);
2653
+ var skipped = m ? m.index - pos : text.length - pos;
2654
+ if (skipped) {
2655
+ content.appendChild(document.createTextNode(text.slice(pos, pos + skipped)));
2656
+ col += skipped;
2657
+ }
2658
+ if (!m) break;
2659
+ pos += skipped + 1;
2660
+ if (m[0] == "\t") {
2661
+ var tab = makeTab(col);
2662
+ content.appendChild(tab.element.cloneNode(true));
2663
+ col += tab.width;
2664
+ } else {
2665
+ var token = elt("span", "\u2022", "cm-invalidchar");
2666
+ token.title = "\\u" + m[0].charCodeAt(0).toString(16);
2667
+ content.appendChild(token);
2668
+ col += 1;
2669
+ }
2670
+ }
2671
+ }
2672
+ if (style) html.appendChild(elt("span", [content], style));
2673
+ else html.appendChild(content);
2674
+ }
2675
+ var span = span_;
2676
+ if (wrapAt != null) {
2677
+ var outPos = 0, anchor = pre.anchor = elt("span");
2678
+ span = function(html, text, style) {
2679
+ var l = text.length;
2680
+ if (wrapAt >= outPos && wrapAt < outPos + l) {
2681
+ if (wrapAt > outPos) {
2682
+ span_(html, text.slice(0, wrapAt - outPos), style);
2683
+ // See comment at the definition of spanAffectsWrapping
2684
+ if (wrapWBR) html.appendChild(elt("wbr"));
2685
+ }
2686
+ html.appendChild(anchor);
2687
+ var cut = wrapAt - outPos;
2688
+ span_(anchor, opera ? text.slice(cut, cut + 1) : text.slice(cut), style);
2689
+ if (opera) span_(html, text.slice(cut + 1), style);
2690
+ wrapAt--;
2691
+ outPos += l;
2692
+ } else {
2693
+ outPos += l;
2694
+ span_(html, text, style);
2695
+ if (outPos == wrapAt && outPos == len) {
2696
+ setTextContent(anchor, eolSpanContent);
2697
+ html.appendChild(anchor);
2698
+ }
2699
+ // Stop outputting HTML when gone sufficiently far beyond measure
2700
+ else if (outPos > wrapAt + 10 && /\s/.test(text)) span = function(){};
2701
+ }
2702
+ };
2703
  }
2704
+
2705
  var st = this.styles, allText = this.text, marked = this.marked;
 
2706
  var len = allText.length;
2707
+ function styleToClass(style) {
2708
+ if (!style) return null;
2709
+ return "cm-" + style.replace(/ +/g, " cm-");
2710
+ }
2711
+ if (!allText && wrapAt == null) {
2712
+ span(pre, " ");
2713
+ } else if (!marked || !marked.length) {
2714
  for (var i = 0, ch = 0; ch < len; i+=2) {
2715
  var str = st[i], style = st[i+1], l = str.length;
2716
  if (ch + l > len) str = str.slice(0, len - ch);
2717
  ch += l;
2718
+ span(pre, str, styleToClass(style));
2719
  }
2720
+ } else {
2721
  var pos = 0, i = 0, text = "", style, sg = 0;
2722
+ var nextChange = marked[0].from || 0, marks = [], markpos = 0;
2723
+ var advanceMarks = function() {
2724
+ var m;
2725
+ while (markpos < marked.length &&
2726
+ ((m = marked[markpos]).from == pos || m.from == null)) {
2727
+ if (m.style != null) marks.push(m);
2728
+ ++markpos;
2729
  }
2730
+ nextChange = markpos < marked.length ? marked[markpos].from : Infinity;
2731
+ for (var i = 0; i < marks.length; ++i) {
2732
+ var to = marks[i].to;
2733
+ if (to == null) to = Infinity;
2734
+ if (to == pos) marks.splice(i--, 1);
2735
+ else nextChange = Math.min(to, nextChange);
 
 
 
 
 
2736
  }
2737
+ };
2738
+ var m = 0;
2739
+ while (pos < len) {
2740
+ if (nextChange == pos) advanceMarks();
2741
+ var upto = Math.min(len, nextChange);
2742
+ while (true) {
2743
+ if (text) {
2744
+ var end = pos + text.length;
2745
+ var appliedStyle = style;
2746
+ for (var j = 0; j < marks.length; ++j)
2747
+ appliedStyle = (appliedStyle ? appliedStyle + " " : "") + marks[j].style;
2748
+ span(pre, end > upto ? text.slice(0, upto - pos) : text, appliedStyle);
2749
+ if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;}
2750
+ pos = end;
2751
  }
2752
+ text = st[i++]; style = styleToClass(st[i++]);
 
 
 
 
 
 
 
 
2753
  }
2754
  }
 
2755
  }
2756
+ return pre;
 
2757
  },
2758
  cleanUp: function() {
2759
  this.parent = null;
2768
  if (state == 0) {
2769
  if (end > from) dest.push(part.slice(from - pos, Math.min(part.length, to - pos)), source[i+1]);
2770
  if (end >= from) state = 1;
2771
+ } else if (state == 1) {
 
2772
  if (end > to) dest.push(part.slice(0, to - pos), source[i+1]);
2773
  else dest.push(part, source[i+1]);
2774
  }
2803
  },
2804
  insertHeight: function(at, lines, height) {
2805
  this.height += height;
2806
+ this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at));
2807
  for (var i = 0, e = lines.length; i < e; ++i) lines[i].parent = this;
2808
  },
2809
  iterN: function(at, n, op) {
2842
  var lines = [];
2843
  this.collapse(lines);
2844
  this.children = [new LeafChunk(lines)];
2845
+ this.children[0].parent = this;
2846
  }
2847
  },
2848
  collapse: function(lines) {
2969
  function History() {
2970
  this.time = 0;
2971
  this.done = []; this.undone = [];
2972
+ this.compound = 0;
2973
+ this.closed = false;
2974
  }
2975
  History.prototype = {
2976
  addChange: function(start, added, old) {
2977
  this.undone.length = 0;
2978
+ var time = +new Date, cur = this.done[this.done.length - 1], last = cur && cur[cur.length - 1];
2979
+ var dtime = time - this.time;
2980
+
2981
+ if (this.compound && cur && !this.closed) {
2982
+ cur.push({start: start, added: added, old: old});
2983
+ } else if (dtime > 400 || !last || this.closed ||
2984
+ last.start > start + old.length || last.start + last.added < start) {
2985
+ this.done.push([{start: start, added: added, old: old}]);
2986
+ this.closed = false;
2987
+ } else {
2988
+ var startBefore = Math.max(0, last.start - start),
2989
+ endAfter = Math.max(0, (start + old.length) - (last.start + last.added));
2990
+ for (var i = startBefore; i > 0; --i) last.old.unshift(old[i - 1]);
2991
+ for (var i = endAfter; i > 0; --i) last.old.push(old[old.length - i]);
2992
+ if (startBefore) last.start = start;
2993
+ last.added += added - (old.length - startBefore - endAfter);
 
 
 
2994
  }
2995
  this.time = time;
2996
+ },
2997
+ startCompound: function() {
2998
+ if (!this.compound++) this.closed = true;
2999
+ },
3000
+ endCompound: function() {
3001
+ if (!--this.compound) this.closed = true;
3002
  }
3003
  };
3004
 
3024
 
3025
  function e_target(e) {return e.target || e.srcElement;}
3026
  function e_button(e) {
3027
+ var b = e.which;
3028
+ if (b == null) {
3029
+ if (e.button & 1) b = 1;
3030
+ else if (e.button & 2) b = 3;
3031
+ else if (e.button & 4) b = 2;
3032
+ }
3033
+ if (mac && e.ctrlKey && b == 1) b = 3;
3034
+ return b;
3035
+ }
3036
+
3037
+ // Allow 3rd-party code to override event properties by adding an override
3038
+ // object to an event object.
3039
+ function e_prop(e, prop) {
3040
+ var overridden = e.override && e.override.hasOwnProperty(prop);
3041
+ return overridden ? e.override[prop] : e[prop];
3042
  }
3043
 
3044
  // Event handler registration. If disconnect is true, it'll return a
3047
  if (typeof node.addEventListener == "function") {
3048
  node.addEventListener(type, handler, false);
3049
  if (disconnect) return function() {node.removeEventListener(type, handler, false);};
3050
+ } else {
 
3051
  var wrapHandler = function(event) {handler(event || window.event);};
3052
  node.attachEvent("on" + type, wrapHandler);
3053
  if (disconnect) return function() {node.detachEvent("on" + type, wrapHandler);};
3058
  function Delayed() {this.id = null;}
3059
  Delayed.prototype = {set: function(ms, f) {clearTimeout(this.id); this.id = setTimeout(f, ms);}};
3060
 
3061
+ var Pass = CodeMirror.Pass = {toString: function(){return "CodeMirror.Pass";}};
3062
+
3063
  // Detect drag-and-drop
3064
  var dragAndDrop = function() {
3065
+ // There is *some* kind of drag-and-drop support in IE6-8, but I
3066
+ // couldn't get it to work yet.
3067
+ if (ie_lt9) return false;
3068
+ var div = elt('div');
3069
+ return "draggable" in div || "dragDrop" in div;
3070
  }();
3071
 
 
 
 
 
 
3072
  // Feature-detect whether newlines in textareas are converted to \r\n
3073
+ var lineSep = function () {
3074
+ var te = elt("textarea");
3075
  te.value = "foo\nbar";
3076
+ if (te.value.indexOf("\r") > -1) return "\r\n";
3077
+ return "\n";
3078
+ }();
3079
+
3080
+ // For a reason I have yet to figure out, some browsers disallow
3081
+ // word wrapping between certain characters *only* if a new inline
3082
+ // element is started between them. This makes it hard to reliably
3083
+ // measure the position of things, since that requires inserting an
3084
+ // extra span. This terribly fragile set of regexps matches the
3085
+ // character combinations that suffer from this phenomenon on the
3086
+ // various browsers.
3087
+ var spanAffectsWrapping = /^$/; // Won't match any two-character string
3088
+ if (gecko) spanAffectsWrapping = /$'/;
3089
+ else if (safari) spanAffectsWrapping = /\-[^ \-?]|\?[^ !'\"\),.\-\/:;\?\]\}]/;
3090
+ else if (chrome) spanAffectsWrapping = /\-[^ \-\.?]|\?[^ \-\.?\]\}:;!'\"\),\/]|[\.!\"#&%\)*+,:;=>\]|\}~][\(\{\[<]|\$'/;
3091
 
3092
  // Counts the column offset in a string, taking tabs into account.
3093
  // Used mostly to find indentation.
3103
  return n;
3104
  }
3105
 
 
 
 
 
 
 
 
3106
  function eltOffset(node, screen) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3107
  // Take the parts of bounding client rect that we are interested in so we are able to edit if need be,
3108
  // since the returned value cannot be changed externally (they are kept in sync as the element moves within the page)
3109
  try { var box = node.getBoundingClientRect(); box = { top: box.top, left: box.left }; }
3119
  }
3120
  }
3121
  return box;
3122
+ }
3123
 
3124
  // Get a node's text content.
3125
  function eltText(node) {
3126
  return node.textContent || node.innerText || node.nodeValue || "";
3127
  }
3128
+ function selectInput(node) {
3129
+ if (ios) { // Mobile Safari apparently has a bug where select() is broken.
3130
+ node.selectionStart = 0;
3131
+ node.selectionEnd = node.value.length;
3132
+ } else node.select();
3133
+ }
3134
 
3135
  // Operations on {line, ch} objects.
3136
  function posEq(a, b) {return a.line == b.line && a.ch == b.ch;}
3137
  function posLess(a, b) {return a.line < b.line || (a.line == b.line && a.ch < b.ch);}
3138
  function copyPos(x) {return {line: x.line, ch: x.ch};}
3139
 
3140
+ function elt(tag, content, className, style) {
3141
+ var e = document.createElement(tag);
3142
+ if (className) e.className = className;
3143
+ if (style) e.style.cssText = style;
3144
+ if (typeof content == "string") setTextContent(e, content);
3145
+ else if (content) for (var i = 0; i < content.length; ++i) e.appendChild(content[i]);
3146
+ return e;
3147
  }
3148
+ function removeChildren(e) {
3149
+ e.innerHTML = "";
3150
+ return e;
3151
+ }
3152
+ function removeChildrenAndAdd(parent, e) {
3153
+ removeChildren(parent).appendChild(e);
3154
+ }
3155
+ function setTextContent(e, str) {
3156
+ if (ie_lt9) {
3157
+ e.innerHTML = "";
3158
+ e.appendChild(document.createTextNode(str));
3159
+ } else e.textContent = str;
3160
+ }
3161
+ CodeMirror.setTextContent = setTextContent;
 
3162
 
3163
  // Used to position the cursor after an undo/redo by finding the
3164
  // last edited character.
3165
  function editEnd(from, to) {
3166
+ if (!to) return 0;
3167
  if (!from) return to.length;
3168
  for (var i = from.length, j = to.length; i >= 0 && j >= 0; --i, --j)
3169
  if (from.charAt(i) != to.charAt(j)) break;
3183
  // See if "".split is the broken IE version, if so, provide an
3184
  // alternative way to split lines.
3185
  var splitLines = "\n\nb".split(/\n/).length != 3 ? function(string) {
3186
+ var pos = 0, result = [], l = string.length;
3187
+ while (pos <= l) {
3188
+ var nl = string.indexOf("\n", pos);
3189
+ if (nl == -1) nl = string.length;
3190
+ var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl);
3191
+ var rt = line.indexOf("\r");
3192
+ if (rt != -1) {
3193
+ result.push(line.slice(0, rt));
3194
+ pos += rt + 1;
3195
+ } else {
3196
+ result.push(line);
3197
+ pos = nl + 1;
3198
+ }
3199
  }
 
3200
  return result;
3201
+ } : function(string){return string.split(/\r\n?|\n/);};
3202
  CodeMirror.splitLines = splitLines;
3203
 
3204
  var hasSelection = window.getSelection ? function(te) {
3219
  var keyNames = {3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
3220
  19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
3221
  36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
3222
+ 46: "Delete", 59: ";", 91: "Mod", 92: "Mod", 93: "Mod", 109: "-", 107: "=", 127: "Delete",
3223
+ 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\",
3224
+ 221: "]", 222: "'", 63276: "PageUp", 63277: "PageDown", 63275: "End", 63273: "Home",
3225
+ 63234: "Left", 63232: "Up", 63235: "Right", 63233: "Down", 63302: "Insert", 63272: "Delete"};
3226
  CodeMirror.keyNames = keyNames;
3227
  (function() {
3228
  // Number keys
js/posts-jquery.js CHANGED
@@ -116,9 +116,6 @@ tags = {};
116
  $('#wpe_qt_content_block').live("click", function() {
117
  insertOpenCloseTag('block', 'b-quote', '\n\n<blockquote>', '</blockquote>\n\n');
118
  })
119
- $('#wpe_qt_content_block').live("click", function() {
120
- insertOpenCloseTag('block', 'b-quote', '\n\n<blockquote>', '</blockquote>\n\n');
121
- })
122
  $('#wpe_qt_content_del').live("click", function() {
123
  insertOpenCloseTag('del', 'del', '<del datetime="' + _datetime() + '">', '</del>');
124
  })
@@ -210,7 +207,7 @@ tags = {};
210
  this.focus();
211
  this.setSelectionRange(start, end);
212
  }
213
- else if (this.createTextRange) {
214
  var range = this.createTextRange();
215
  range.collapse(true);
216
  range.moveEnd('character', end);
@@ -221,19 +218,26 @@ tags = {};
221
  };
222
  window.original_send_to_editor = window.send_to_editor;
223
  window.send_to_editor = function(html){
224
- editor.toTextArea();
225
- var cursor = editor.getCursor(true);
226
- var lines = $('#content').val().substr(0, this.selectionStart).split("\n");
227
- var newLength = 0, line = 1, lineArray = [];
228
- lineArray[0] = 0;
229
- $.each(lines, function(key, value) {
230
- newLength = newLength + value.length + 1;
231
- lineArray[line] = newLength;
232
- line++;
233
- })
234
- $('#content').setContentCursor(lineArray[cursor.line] + cursor.ch, lineArray[cursor.line] + cursor.ch);
235
- window.original_send_to_editor(html);
236
- postCodeMirror('content');
 
 
 
 
 
 
 
237
  };
238
  function postCodeMirror(element) {
239
  var activeLine = WPEPosts.activeLine;
@@ -242,6 +246,8 @@ tags = {};
242
  theme: WPEPosts.theme,
243
  lineNumbers: WPEPosts.lineNumbers,
244
  lineWrapping: WPEPosts.lineWrapping,
 
 
245
  onCursorActivity: function() {
246
  editor.setLineClass(hlLine, null);
247
  hlLine = editor.setLineClass(editor.getCursor().line, activeLine);
@@ -276,6 +282,11 @@ tags = {};
276
  }
277
  });
278
  var hlLine = editor.setLineClass(0, activeLine);
 
 
 
 
 
279
  if(!$('.CodeMirror .quicktags-toolbar').length) {
280
  $('.CodeMirror').prepend('<div class="quicktags-toolbar">' +
281
  '<input type="button" id="wpe_qt_content_strong" class="wpe_ed_button" title="" value="b">' +
@@ -295,6 +306,7 @@ tags = {};
295
  '<input type="button" id="wpe_qt_content_fullscreen" class="ed_button" title="" value="fullscreen">' +
296
  '</div>'
297
  ).height($('.CodeMirror').height() + 33);
 
298
  }
299
  }
300
  })(jQuery);
116
  $('#wpe_qt_content_block').live("click", function() {
117
  insertOpenCloseTag('block', 'b-quote', '\n\n<blockquote>', '</blockquote>\n\n');
118
  })
 
 
 
119
  $('#wpe_qt_content_del').live("click", function() {
120
  insertOpenCloseTag('del', 'del', '<del datetime="' + _datetime() + '">', '</del>');
121
  })
207
  this.focus();
208
  this.setSelectionRange(start, end);
209
  }
210
+ else if(this.createTextRange) {
211
  var range = this.createTextRange();
212
  range.collapse(true);
213
  range.moveEnd('character', end);
218
  };
219
  window.original_send_to_editor = window.send_to_editor;
220
  window.send_to_editor = function(html){
221
+ if(editor_status == 'html') {
222
+ var cursor = editor.getCursor(true);
223
+ var lines = $('#content').val().substr(0, this.selectionStart).split("\n");
224
+ var newLength = 0, line = 1, lineArray = [];
225
+ lineArray[0] = 0;
226
+ $.each(lines, function(key, value) {
227
+ newLength = newLength + value.length + 1;
228
+ lineArray[line] = newLength;
229
+ line++;
230
+ });
231
+ editor.toTextArea();
232
+ $('#content').setContentCursor(lineArray[cursor.line] + cursor.ch, lineArray[cursor.line] + cursor.ch);
233
+ window.original_send_to_editor(html);
234
+ postCodeMirror('content');
235
+ editor.setCursor(cursor.line, cursor.ch + html.length)
236
+ editor.focus();
237
+ }
238
+ else {
239
+ window.original_send_to_editor(html);
240
+ }
241
  };
242
  function postCodeMirror(element) {
243
  var activeLine = WPEPosts.activeLine;
246
  theme: WPEPosts.theme,
247
  lineNumbers: WPEPosts.lineNumbers,
248
  lineWrapping: WPEPosts.lineWrapping,
249
+ indentWithTabs: WPEPosts.indentWithTabs,
250
+ tabSize: WPEPosts.tabSize,
251
  onCursorActivity: function() {
252
  editor.setLineClass(hlLine, null);
253
  hlLine = editor.setLineClass(editor.getCursor().line, activeLine);
282
  }
283
  });
284
  var hlLine = editor.setLineClass(0, activeLine);
285
+ if(WPEPosts.editorHeight) {
286
+ $('.CodeMirror-scroll, .CodeMirror, .CodeMirror-gutter').height(WPEPosts.editorHeight + 'px');
287
+ var scrollDivHeight = $('.CodeMirror-scroll div:first-child').height();
288
+ $('.CodeMirror-gutter').height(scrollDivHeight);
289
+ }
290
  if(!$('.CodeMirror .quicktags-toolbar').length) {
291
  $('.CodeMirror').prepend('<div class="quicktags-toolbar">' +
292
  '<input type="button" id="wpe_qt_content_strong" class="wpe_ed_button" title="" value="b">' +
306
  '<input type="button" id="wpe_qt_content_fullscreen" class="ed_button" title="" value="fullscreen">' +
307
  '</div>'
308
  ).height($('.CodeMirror').height() + 33);
309
+ editor.focus();
310
  }
311
  }
312
  })(jQuery);
js/wpeditor.js CHANGED
@@ -53,7 +53,7 @@ function toggleFullscreenEditing() {
53
  else {
54
  editorDiv.removeClass('CodeMirror-fullscreen');
55
  editorDiv.height(toggleFullscreenEditing.beforeFullscreen.height);
56
- $jq('.CodeMirror-scroll').height('450px');
57
  editorDiv.width('97%');
58
  editor.refresh();
59
  }
53
  else {
54
  editorDiv.removeClass('CodeMirror-fullscreen');
55
  editorDiv.height(toggleFullscreenEditing.beforeFullscreen.height);
56
+ $jq('.CodeMirror-scroll').height(toggleFullscreenEditing.beforeFullscreen.height);
57
  editorDiv.width('97%');
58
  editor.refresh();
59
  }
readme.txt CHANGED
@@ -4,7 +4,7 @@ Donate link: http://wpeditor.net/
4
  Tags: plugin editor, theme editor, page editor, post editor, pages, posts, html, codemirror, plugins, themes, editor, fancybox, post.php, post-new.php, ajax, syntax highlighting, html syntax highlighting
5
  Requires at least: 3.0
6
  Tested up to: 3.4.1
7
- Stable tag: 1.1.0.1
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
@@ -69,6 +69,14 @@ Yes! While we don't have a need for further developers at this time, any financi
69
 
70
  == Changelog ==
71
 
 
 
 
 
 
 
 
 
72
  = 1.1.0.1 =
73
  * Fixed Upload/Insert media buttons not working in page/post editor
74
  * Removed legacy CSS theme files
@@ -103,6 +111,11 @@ Yes! While we don't have a need for further developers at this time, any financi
103
 
104
  == Upgrade Notice ==
105
 
 
 
 
 
 
106
  = 1.1.0.1 =
107
  Fixed media buttons not working in Page/Post editor
108
 
4
  Tags: plugin editor, theme editor, page editor, post editor, pages, posts, html, codemirror, plugins, themes, editor, fancybox, post.php, post-new.php, ajax, syntax highlighting, html syntax highlighting
5
  Requires at least: 3.0
6
  Tested up to: 3.4.1
7
+ Stable tag: 1.1.0.2
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
69
 
70
  == Changelog ==
71
 
72
+ = 1.1.0.2 =
73
+ * Added ability to customize tab characters and size for all editors
74
+ * Added ability to set custom editor heights for all editors
75
+ * Updated CodeMirror library to 2.33
76
+ * Updated CSS to work with new version of CodeMirror
77
+ * Fixed issue with media button toolbar not inserting shortcodes/content when in visual mode
78
+ * Fixed issue with blockquote QuickTag inserting twice
79
+
80
  = 1.1.0.1 =
81
  * Fixed Upload/Insert media buttons not working in page/post editor
82
  * Removed legacy CSS theme files
111
 
112
  == Upgrade Notice ==
113
 
114
+ = 1.1.0.2 =
115
+ CodeMirror 2.33
116
+ Fixed issue with media button toolbar not inserting shortcodes/content when in visual mode
117
+ Fixed issue with blockquote QuickTag inserting twice
118
+
119
  = 1.1.0.1 =
120
  Fixed media buttons not working in Page/Post editor
121
 
views/plugin-editor.php CHANGED
@@ -168,6 +168,11 @@
168
  });
169
  return false;
170
  });
 
 
 
 
 
171
  })
172
  })(jQuery);
173
  function runCodeMirror(extension) {
@@ -205,6 +210,14 @@
205
  <?php }
206
  if(WPEditorSetting::getValue('enable_plugin_line_wrapping')) { ?>
207
  lineWrapping: true,
 
 
 
 
 
 
 
 
208
  <?php } ?>
209
  onCursorActivity: function() {
210
  editor.setLineClass(hlLine, null);
168
  });
169
  return false;
170
  });
171
+ <?php if(WPEditorSetting::getValue('enable_plugin_editor_height')) { ?>
172
+ $('.CodeMirror-scroll, .CodeMirror').height('<?php echo WPEditorSetting::getValue("enable_plugin_editor_height"); ?>px');
173
+ var scrollDivHeight = $('.CodeMirror-scroll div:first-child').height();
174
+ $('.CodeMirror-gutter').height(scrollDivHeight);
175
+ <?php } ?>
176
  })
177
  })(jQuery);
178
  function runCodeMirror(extension) {
210
  <?php }
211
  if(WPEditorSetting::getValue('enable_plugin_line_wrapping')) { ?>
212
  lineWrapping: true,
213
+ <?php }
214
+ if(WPEditorSetting::getValue('enable_plugin_tab_characters') && WPEditorSetting::getValue('enable_plugin_tab_characters') == 'tabs') { ?>
215
+ indentWithTabs: true,
216
+ <?php }
217
+ if(WPEditorSetting::getValue('enable_plugin_tab_size')) { ?>
218
+ tabSize: <?php echo WPEditorSetting::getValue('enable_plugin_tab_size'); ?>,
219
+ <?php } else { ?>
220
+ tabSize: 2,
221
  <?php } ?>
222
  onCursorActivity: function() {
223
  editor.setLineClass(hlLine, null);
views/settings.php CHANGED
@@ -256,6 +256,54 @@
256
  </ul>
257
  </div>
258
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
259
  <div id="enable-theme-file-upload" class="section">
260
  <div class="section-header">
261
  <h3><?php _e('File Upload', 'wpeditor'); ?></h3>
@@ -429,6 +477,54 @@
429
  </ul>
430
  </div>
431
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
432
  <div id="enable-plugin-file-upload" class="section">
433
  <div class="section-header">
434
  <h3><?php _e('File Upload', 'wpeditor'); ?></h3>
@@ -569,6 +665,54 @@
569
  </ul>
570
  </div>
571
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
572
  <div id="save-settings">
573
  <ul>
574
  <li>
256
  </ul>
257
  </div>
258
  </div>
259
+ <div id="enable-theme-tab-characters" class="section">
260
+ <div class="section-header">
261
+ <h3><?php _e('Tab Characters', 'wpeditor'); ?></h3>
262
+ </div>
263
+ <div class="section-body">
264
+ <ul>
265
+ <li>
266
+ <label for="enable_theme_tab_characters"><?php _e('Tab Characters:', 'wpeditor'); ?></label>
267
+ </li>
268
+ <li class="indent">
269
+ <select name="enable_theme_tab_characters">
270
+ <option value="spaces"<?php echo WPEditorSetting::getValue('enable_theme_tab_characters') == 'spaces' ? ' selected="selected"' : ''; ?>><?php _e('Spaces', 'wpeditor'); ?></option>
271
+ <option value="tabs"<?php echo WPEditorSetting::getValue('enable_theme_tab_characters') == 'tabs' ? ' selected="selected"' : ''; ?>><?php _e('Tabs', 'wpeditor'); ?></option>
272
+ </select>
273
+ </li>
274
+ <li class="indent description">
275
+ <p><?php _e("This will set the tab character for the theme editor.<br />Default: Spaces", 'wpeditor'); ?></p>
276
+ </li>
277
+ <li>
278
+ <label for="enable_theme_tab_size"><?php _e('Tab Size:', 'wpeditor'); ?></label>
279
+ </li>
280
+ <li class="indent">
281
+ <input class="small-text" name="enable_theme_tab_size" value="<?php echo WPEditorSetting::getValue('enable_theme_tab_size') ? WPEditorSetting::getValue('enable_theme_tab_size') : 2; ?>" />
282
+ </li>
283
+ <li class="indent description">
284
+ <p><?php _e("This will set the tab size for the theme editor.<br />Default: 2", 'wpeditor'); ?></p>
285
+ </li>
286
+ </ul>
287
+ </div>
288
+ </div>
289
+ <div id="enable-theme-editor-height" class="section">
290
+ <div class="section-header">
291
+ <h3><?php _e('Tab Characters', 'wpeditor'); ?></h3>
292
+ </div>
293
+ <div class="section-body">
294
+ <ul>
295
+ <li>
296
+ <label for="enable_theme_editor_height"><?php _e('Editor Height:', 'wpeditor'); ?></label>
297
+ </li>
298
+ <li class="indent">
299
+ <input class="small-text" name="enable_theme_editor_height" value="<?php echo WPEditorSetting::getValue('enable_theme_editor_height') ? WPEditorSetting::getValue('enable_theme_editor_height') : 450; ?>" />
300
+ </li>
301
+ <li class="indent description">
302
+ <p><?php _e("This will set the height in pixels for the theme editor.<br />Default: 450", 'wpeditor'); ?></p>
303
+ </li>
304
+ </ul>
305
+ </div>
306
+ </div>
307
  <div id="enable-theme-file-upload" class="section">
308
  <div class="section-header">
309
  <h3><?php _e('File Upload', 'wpeditor'); ?></h3>
477
  </ul>
478
  </div>
479
  </div>
480
+ <div id="enable-plugin-tab-characters" class="section">
481
+ <div class="section-header">
482
+ <h3><?php _e('Tab Characters', 'wpeditor'); ?></h3>
483
+ </div>
484
+ <div class="section-body">
485
+ <ul>
486
+ <li>
487
+ <label for="enable_plugin_tab_characters"><?php _e('Tab Characters:', 'wpeditor'); ?></label>
488
+ </li>
489
+ <li class="indent">
490
+ <select name="enable_plugin_tab_characters">
491
+ <option value="spaces"<?php echo WPEditorSetting::getValue('enable_plugin_tab_characters') == 'spaces' ? ' selected="selected"' : ''; ?>><?php _e('Spaces', 'wpeditor'); ?></option>
492
+ <option value="tabs"<?php echo WPEditorSetting::getValue('enable_plugin_tab_characters') == 'tabs' ? ' selected="selected"' : ''; ?>><?php _e('Tabs', 'wpeditor'); ?></option>
493
+ </select>
494
+ </li>
495
+ <li class="indent description">
496
+ <p><?php _e("This will set the tab character for the plugin editor.<br />Default: Spaces", 'wpeditor'); ?></p>
497
+ </li>
498
+ <li>
499
+ <label for="enable_plugin_tab_size"><?php _e('Tab Size:', 'wpeditor'); ?></label>
500
+ </li>
501
+ <li class="indent">
502
+ <input class="small-text" name="enable_plugin_tab_size" value="<?php echo WPEditorSetting::getValue('enable_plugin_tab_size') ? WPEditorSetting::getValue('enable_plugin_tab_size') : 2; ?>" />
503
+ </li>
504
+ <li class="indent description">
505
+ <p><?php _e("This will set the tab size for the plugin editor.<br />Default: 2", 'wpeditor'); ?></p>
506
+ </li>
507
+ </ul>
508
+ </div>
509
+ </div>
510
+ <div id="enable-plugin-editor-height" class="section">
511
+ <div class="section-header">
512
+ <h3><?php _e('Tab Characters', 'wpeditor'); ?></h3>
513
+ </div>
514
+ <div class="section-body">
515
+ <ul>
516
+ <li>
517
+ <label for="enable_plugin_editor_height"><?php _e('Editor Height:', 'wpeditor'); ?></label>
518
+ </li>
519
+ <li class="indent">
520
+ <input class="small-text" name="enable_plugin_editor_height" value="<?php echo WPEditorSetting::getValue('enable_plugin_editor_height') ? WPEditorSetting::getValue('enable_plugin_editor_height') : 450; ?>" />
521
+ </li>
522
+ <li class="indent description">
523
+ <p><?php _e("This will set the height in pixels for the plugin editor.<br />Default: 450", 'wpeditor'); ?></p>
524
+ </li>
525
+ </ul>
526
+ </div>
527
+ </div>
528
  <div id="enable-plugin-file-upload" class="section">
529
  <div class="section-header">
530
  <h3><?php _e('File Upload', 'wpeditor'); ?></h3>
665
  </ul>
666
  </div>
667
  </div>
668
+ <div id="enable-post-tab-characters" class="section">
669
+ <div class="section-header">
670
+ <h3><?php _e('Tab Characters', 'wpeditor'); ?></h3>
671
+ </div>
672
+ <div class="section-body">
673
+ <ul>
674
+ <li>
675
+ <label for="enable_post_tab_characters"><?php _e('Tab Characters:', 'wpeditor'); ?></label>
676
+ </li>
677
+ <li class="indent">
678
+ <select name="enable_post_tab_characters">
679
+ <option value="spaces"<?php echo WPEditorSetting::getValue('enable_post_tab_characters') == 'spaces' ? ' selected="selected"' : ''; ?>><?php _e('Spaces', 'wpeditor'); ?></option>
680
+ <option value="tabs"<?php echo WPEditorSetting::getValue('enable_post_tab_characters') == 'tabs' ? ' selected="selected"' : ''; ?>><?php _e('Tabs', 'wpeditor'); ?></option>
681
+ </select>
682
+ </li>
683
+ <li class="indent description">
684
+ <p><?php _e("This will set the tab character for the post editor.<br />Default: Spaces", 'wpeditor'); ?></p>
685
+ </li>
686
+ <li>
687
+ <label for="enable_post_tab_size"><?php _e('Tab Size:', 'wpeditor'); ?></label>
688
+ </li>
689
+ <li class="indent">
690
+ <input class="small-text" name="enable_post_tab_size" value="<?php echo WPEditorSetting::getValue('enable_post_tab_size') ? WPEditorSetting::getValue('enable_post_tab_size') : 2; ?>" />
691
+ </li>
692
+ <li class="indent description">
693
+ <p><?php _e("This will set the tab size for the post editor.<br />Default: 2", 'wpeditor'); ?></p>
694
+ </li>
695
+ </ul>
696
+ </div>
697
+ </div>
698
+ <div id="enable-post-editor-height" class="section">
699
+ <div class="section-header">
700
+ <h3><?php _e('Tab Characters', 'wpeditor'); ?></h3>
701
+ </div>
702
+ <div class="section-body">
703
+ <ul>
704
+ <li>
705
+ <label for="enable_post_editor_height"><?php _e('Editor Height:', 'wpeditor'); ?></label>
706
+ </li>
707
+ <li class="indent">
708
+ <input class="small-text" name="enable_post_editor_height" value="<?php echo WPEditorSetting::getValue('enable_post_editor_height') ? WPEditorSetting::getValue('enable_post_editor_height') : 450; ?>" />
709
+ </li>
710
+ <li class="indent description">
711
+ <p><?php _e("This will set the height in pixels for the post editor.<br />Default: 450", 'wpeditor'); ?></p>
712
+ </li>
713
+ </ul>
714
+ </div>
715
+ </div>
716
  <div id="save-settings">
717
  <ul>
718
  <li>
views/theme-editor.php CHANGED
@@ -175,7 +175,11 @@
175
  });
176
  return false;
177
  });
178
-
 
 
 
 
179
  })
180
  })(jQuery);
181
  function runCodeMirror(extension) {
@@ -213,6 +217,14 @@
213
  <?php }
214
  if(WPEditorSetting::getValue('enable_theme_line_wrapping')) { ?>
215
  lineWrapping: true,
 
 
 
 
 
 
 
 
216
  <?php } ?>
217
  onCursorActivity: function() {
218
  editor.setLineClass(hlLine, null);
175
  });
176
  return false;
177
  });
178
+ <?php if(WPEditorSetting::getValue('enable_theme_editor_height')) { ?>
179
+ $('.CodeMirror-scroll, .CodeMirror').height('<?php echo WPEditorSetting::getValue("enable_theme_editor_height"); ?>px');
180
+ var scrollDivHeight = $('.CodeMirror-scroll div:first-child').height();
181
+ $('.CodeMirror-gutter').height(scrollDivHeight);
182
+ <?php } ?>
183
  })
184
  })(jQuery);
185
  function runCodeMirror(extension) {
217
  <?php }
218
  if(WPEditorSetting::getValue('enable_theme_line_wrapping')) { ?>
219
  lineWrapping: true,
220
+ <?php }
221
+ if(WPEditorSetting::getValue('enable_theme_tab_characters') && WPEditorSetting::getValue('enable_theme_tab_characters') == 'tabs') { ?>
222
+ indentWithTabs: true,
223
+ <?php }
224
+ if(WPEditorSetting::getValue('enable_theme_tab_size')) { ?>
225
+ tabSize: <?php echo WPEditorSetting::getValue('enable_theme_tab_size'); ?>,
226
+ <?php } else { ?>
227
+ tabSize: 2,
228
  <?php } ?>
229
  onCursorActivity: function() {
230
  editor.setLineClass(hlLine, null);
wpeditor.php CHANGED
@@ -3,7 +3,7 @@
3
  Plugin Name: WP Editor
4
  Plugin URI: http://wpeditor.net
5
  Description: This plugin modifies the default behavior of the WordPress plugin and theme editors.
6
- Version: 1.1.0.1
7
  Author: Benjamin Rojas
8
  Author URI: http://benjaminrojas.net
9
  Text Domain: wpeditor
@@ -31,7 +31,7 @@ if(!class_exists('WPEditor')) {
31
  ob_start();
32
 
33
  // Define the WP Editor version number
34
- define('WPEDITOR_VERSION_NUMBER', '1.1');
35
 
36
  $wp_34 = false;
37
  if(version_compare(get_bloginfo('version'), '3.4', '>=')) {
3
  Plugin Name: WP Editor
4
  Plugin URI: http://wpeditor.net
5
  Description: This plugin modifies the default behavior of the WordPress plugin and theme editors.
6
+ Version: 1.1.0.2
7
  Author: Benjamin Rojas
8
  Author URI: http://benjaminrojas.net
9
  Text Domain: wpeditor
31
  ob_start();
32
 
33
  // Define the WP Editor version number
34
+ define('WPEDITOR_VERSION_NUMBER', '1.1.0.2');
35
 
36
  $wp_34 = false;
37
  if(version_compare(get_bloginfo('version'), '3.4', '>=')) {