##// END OF EJS Templates
bumped codemirror to latest version
marcink -
r1606:a30689fc beta
parent child Browse files
Show More
This diff has been collapsed as it changes many lines, (1093 lines changed) Show them Hide them
@@ -13,32 +13,45 b' var CodeMirror = (function() {'
13 if (defaults.hasOwnProperty(opt))
13 if (defaults.hasOwnProperty(opt))
14 options[opt] = (givenOptions && givenOptions.hasOwnProperty(opt) ? givenOptions : defaults)[opt];
14 options[opt] = (givenOptions && givenOptions.hasOwnProperty(opt) ? givenOptions : defaults)[opt];
15
15
16 // The element in which the editor lives. Takes care of scrolling
16 var targetDocument = options["document"];
17 // (if enabled).
17 // The element in which the editor lives.
18 var wrapper = document.createElement("div");
18 var wrapper = targetDocument.createElement("div");
19 wrapper.className = "CodeMirror";
19 wrapper.className = "CodeMirror";
20 // This mess creates the base DOM structure for the editor.
20 // This mess creates the base DOM structure for the editor.
21 wrapper.innerHTML =
21 wrapper.innerHTML =
22 '<div style="position: relative">' + // Set to the height of the text, causes scrolling
22 '<div style="overflow: hidden; position: relative; width: 1px; height: 0px;">' + // Wraps and hides input textarea
23 '<pre style="position: relative; height: 0; visibility: hidden; overflow: hidden;">' + // To measure line/char size
23 '<textarea style="position: absolute; width: 10000px;" wrap="off" ' +
24 '<span>xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx</span></pre>' +
24 'autocorrect="off" autocapitalize="off"></textarea></div>' +
25 '<div style="position: relative">' + // Moved around its parent to cover visible view
25 '<div class="CodeMirror-scroll cm-s-' + options.theme + '">' +
26 '<div class="CodeMirror-gutter"><div class="CodeMirror-gutter-text"></div></div>' +
26 '<div style="position: relative">' + // Set to the height of the text, causes scrolling
27 '<div style="overflow: hidden; position: absolute; width: 0; left: 0">' + // Wraps and hides input textarea
27 '<div style="position: absolute; height: 0; width: 0; overflow: hidden;"></div>' +
28 '<textarea style="height: 1px; position: absolute; width: 1px;" wrap="off"></textarea></div>' +
28 '<div style="position: relative">' + // Moved around its parent to cover visible view
29 // Provides positioning relative to (visible) text origin
29 '<div class="CodeMirror-gutter"><div class="CodeMirror-gutter-text"></div></div>' +
30 '<div class="CodeMirror-lines"><div style="position: relative">' +
30 // Provides positioning relative to (visible) text origin
31 '<pre class="CodeMirror-cursor">&#160;</pre>' + // Absolutely positioned blinky cursor
31 '<div class="CodeMirror-lines"><div style="position: relative" draggable="true">' +
32 '<div></div></div></div></div></div>'; // This DIV contains the actual code
32 '<pre class="CodeMirror-cursor">&#160;</pre>' + // Absolutely positioned blinky cursor
33 '<div></div>' + // This DIV contains the actual code
34 '</div></div></div></div></div>';
33 if (place.appendChild) place.appendChild(wrapper); else place(wrapper);
35 if (place.appendChild) place.appendChild(wrapper); else place(wrapper);
34 // I've never seen more elegant code in my life.
36 // I've never seen more elegant code in my life.
35 var code = wrapper.firstChild, measure = code.firstChild, mover = measure.nextSibling,
37 var inputDiv = wrapper.firstChild, input = inputDiv.firstChild,
38 scroller = wrapper.lastChild, code = scroller.firstChild,
39 measure = code.firstChild, mover = measure.nextSibling,
36 gutter = mover.firstChild, gutterText = gutter.firstChild,
40 gutter = mover.firstChild, gutterText = gutter.firstChild,
37 inputDiv = gutter.nextSibling, input = inputDiv.firstChild,
41 lineSpace = gutter.nextSibling.firstChild,
38 lineSpace = inputDiv.nextSibling.firstChild, cursor = lineSpace.firstChild, lineDiv = cursor.nextSibling;
42 cursor = lineSpace.firstChild, lineDiv = cursor.nextSibling;
39 if (options.tabindex != null) input.tabindex = options.tabindex;
43 if (options.tabindex != null) input.tabindex = options.tabindex;
40 if (!options.gutter && !options.lineNumbers) gutter.style.display = "none";
44 if (!options.gutter && !options.lineNumbers) gutter.style.display = "none";
41
45
46 // Check for problem with IE innerHTML not working when we have a
47 // P (or similar) parent node.
48 try { stringWidth("x"); }
49 catch (e) {
50 if (e.message.match(/unknown runtime/i))
51 e = new Error("A CodeMirror inside a P-style element does not work in Internet Explorer. (innerHTML bug)");
52 throw e;
53 }
54
42 // Delayed object wrap timeouts, making sure only one is active. blinker holds an interval.
55 // Delayed object wrap timeouts, making sure only one is active. blinker holds an interval.
43 var poll = new Delayed(), highlight = new Delayed(), blinker;
56 var poll = new Delayed(), highlight = new Delayed(), blinker;
44
57
@@ -46,7 +59,7 b' var CodeMirror = (function() {'
46 // (see Line constructor), work an array of lines that should be
59 // (see Line constructor), work an array of lines that should be
47 // parsed, and history the undo history (instance of History
60 // parsed, and history the undo history (instance of History
48 // constructor).
61 // constructor).
49 var mode, lines = [new Line("")], work, history = new History(), focused;
62 var mode, lines = [new Line("")], work, focused;
50 loadMode();
63 loadMode();
51 // The selection. These are always maintained to point at valid
64 // The selection. These are always maintained to point at valid
52 // positions. Inverted is used to remember that the user is
65 // positions. Inverted is used to remember that the user is
@@ -56,10 +69,10 b' var CodeMirror = (function() {'
56 // whether the user is holding shift. reducedSelection is a hack
69 // whether the user is holding shift. reducedSelection is a hack
57 // to get around the fact that we can't create inverted
70 // to get around the fact that we can't create inverted
58 // selections. See below.
71 // selections. See below.
59 var shiftSelecting, reducedSelection;
72 var shiftSelecting, reducedSelection, lastClick, lastDoubleClick, draggingText;
60 // Variables used by startOperation/endOperation to track what
73 // Variables used by startOperation/endOperation to track what
61 // happened during the operation.
74 // happened during the operation.
62 var updateInput, changes, textChanged, selectionChanged, leaveInputAlone;
75 var updateInput, changes, textChanged, selectionChanged, leaveInputAlone, gutterDirty;
63 // Current visible range (may be bigger than the view window).
76 // Current visible range (may be bigger than the view window).
64 var showingFrom = 0, showingTo = 0, lastHeight = 0, curKeyId = null;
77 var showingFrom = 0, showingTo = 0, lastHeight = 0, curKeyId = null;
65 // editing will hold an object describing the things we put in the
78 // editing will hold an object describing the things we put in the
@@ -67,35 +80,46 b' var CodeMirror = (function() {'
67 // bracketHighlighted is used to remember that a backet has been
80 // bracketHighlighted is used to remember that a backet has been
68 // marked.
81 // marked.
69 var editing, bracketHighlighted;
82 var editing, bracketHighlighted;
83 // Tracks the maximum line length so that the horizontal scrollbar
84 // can be kept static when scrolling.
85 var maxLine = "", maxWidth;
70
86
71 // Initialize the content. Somewhat hacky (delayed prepareInput)
87 // Initialize the content.
72 // to work around browser issues.
73 operation(function(){setValue(options.value || ""); updateInput = false;})();
88 operation(function(){setValue(options.value || ""); updateInput = false;})();
74 setTimeout(prepareInput, 20);
89 var history = new History();
75
90
76 // Register our event handlers.
91 // Register our event handlers.
77 connect(wrapper, "mousedown", operation(onMouseDown));
92 connect(scroller, "mousedown", operation(onMouseDown));
93 connect(scroller, "dblclick", operation(onDoubleClick));
94 connect(lineSpace, "dragstart", onDragStart);
78 // Gecko browsers fire contextmenu *after* opening the menu, at
95 // Gecko browsers fire contextmenu *after* opening the menu, at
79 // which point we can't mess with it anymore. Context menu is
96 // which point we can't mess with it anymore. Context menu is
80 // handled in onMouseDown for Gecko.
97 // handled in onMouseDown for Gecko.
81 if (!gecko) connect(wrapper, "contextmenu", operation(onContextMenu));
98 if (!gecko) connect(scroller, "contextmenu", onContextMenu);
82 connect(code, "dblclick", operation(onDblClick));
99 connect(scroller, "scroll", function() {
83 connect(wrapper, "scroll", function() {updateDisplay([]); if (options.onScroll) options.onScroll(instance);});
100 updateDisplay([]);
101 if (options.fixedGutter) gutter.style.left = scroller.scrollLeft + "px";
102 if (options.onScroll) options.onScroll(instance);
103 });
84 connect(window, "resize", function() {updateDisplay(true);});
104 connect(window, "resize", function() {updateDisplay(true);});
85 connect(input, "keyup", operation(onKeyUp));
105 connect(input, "keyup", operation(onKeyUp));
106 connect(input, "input", function() {fastPoll(curKeyId);});
86 connect(input, "keydown", operation(onKeyDown));
107 connect(input, "keydown", operation(onKeyDown));
87 connect(input, "keypress", operation(onKeyPress));
108 connect(input, "keypress", operation(onKeyPress));
88 connect(input, "focus", onFocus);
109 connect(input, "focus", onFocus);
89 connect(input, "blur", onBlur);
110 connect(input, "blur", onBlur);
90
111
91 connect(wrapper, "dragenter", function(e){e.stop();});
112 connect(scroller, "dragenter", e_stop);
92 connect(wrapper, "dragover", function(e){e.stop();});
113 connect(scroller, "dragover", e_stop);
93 connect(wrapper, "drop", operation(onDrop));
114 connect(scroller, "drop", operation(onDrop));
94 connect(wrapper, "paste", function(){input.focus(); fastPoll();});
115 connect(scroller, "paste", function(){focusInput(); fastPoll();});
95 connect(input, "paste", function(){fastPoll();});
116 connect(input, "paste", function(){fastPoll();});
96 connect(input, "cut", function(){fastPoll();});
117 connect(input, "cut", function(){fastPoll();});
97
118
98 if (document.activeElement == input) onFocus();
119 // IE throws unspecified error in certain cases, when
120 // trying to access activeElement before onload
121 var hasFocus; try { hasFocus = (targetDocument.activeElement == input); } catch(e) { }
122 if (hasFocus) setTimeout(onFocus, 20);
99 else onBlur();
123 else onBlur();
100
124
101 function isLine(l) {return l >= 0 && l < lines.length;}
125 function isLine(l) {return l >= 0 && l < lines.length;}
@@ -104,27 +128,37 b' var CodeMirror = (function() {'
104 // range checking and/or clipping. operation is used to wrap the
128 // range checking and/or clipping. operation is used to wrap the
105 // call so that changes it makes are tracked, and the display is
129 // call so that changes it makes are tracked, and the display is
106 // updated afterwards.
130 // updated afterwards.
107 var instance = {
131 var instance = wrapper.CodeMirror = {
108 getValue: getValue,
132 getValue: getValue,
109 setValue: operation(setValue),
133 setValue: operation(setValue),
110 getSelection: getSelection,
134 getSelection: getSelection,
111 replaceSelection: operation(replaceSelection),
135 replaceSelection: operation(replaceSelection),
112 focus: function(){input.focus(); onFocus(); fastPoll();},
136 focus: function(){focusInput(); onFocus(); fastPoll();},
113 setOption: function(option, value) {
137 setOption: function(option, value) {
114 options[option] = value;
138 options[option] = value;
115 if (option == "lineNumbers" || option == "gutter") gutterChanged();
139 if (option == "lineNumbers" || option == "gutter" || option == "firstLineNumber")
140 operation(gutterChanged)();
116 else if (option == "mode" || option == "indentUnit") loadMode();
141 else if (option == "mode" || option == "indentUnit") loadMode();
142 else if (option == "readOnly" && value == "nocursor") input.blur();
143 else if (option == "theme") scroller.className = scroller.className.replace(/cm-s-\w+/, "cm-s-" + value);
117 },
144 },
118 getOption: function(option) {return options[option];},
145 getOption: function(option) {return options[option];},
119 undo: operation(undo),
146 undo: operation(undo),
120 redo: operation(redo),
147 redo: operation(redo),
121 indentLine: operation(function(n) {if (isLine(n)) indentLine(n, "smart");}),
148 indentLine: operation(function(n, dir) {
149 if (isLine(n)) indentLine(n, dir == null ? "smart" : dir ? "add" : "subtract");
150 }),
122 historySize: function() {return {undo: history.done.length, redo: history.undone.length};},
151 historySize: function() {return {undo: history.done.length, redo: history.undone.length};},
152 clearHistory: function() {history = new History();},
123 matchBrackets: operation(function(){matchBrackets(true);}),
153 matchBrackets: operation(function(){matchBrackets(true);}),
124 getTokenAt: function(pos) {
154 getTokenAt: function(pos) {
125 pos = clipPos(pos);
155 pos = clipPos(pos);
126 return lines[pos.line].getTokenAt(mode, getStateBefore(pos.line), pos.ch);
156 return lines[pos.line].getTokenAt(mode, getStateBefore(pos.line), pos.ch);
127 },
157 },
158 getStateAfter: function(line) {
159 line = clipLine(line == null ? lines.length - 1: line);
160 return getStateBefore(line + 1);
161 },
128 cursorCoords: function(start){
162 cursorCoords: function(start){
129 if (start == null) start = sel.inverted;
163 if (start == null) start = sel.inverted;
130 return pageCoords(start ? sel.from : sel.to);
164 return pageCoords(start ? sel.from : sel.to);
@@ -132,22 +166,41 b' var CodeMirror = (function() {'
132 charCoords: function(pos){return pageCoords(clipPos(pos));},
166 charCoords: function(pos){return pageCoords(clipPos(pos));},
133 coordsChar: function(coords) {
167 coordsChar: function(coords) {
134 var off = eltOffset(lineSpace);
168 var off = eltOffset(lineSpace);
135 var line = Math.min(showingTo - 1, showingFrom + Math.floor(coords.y / lineHeight()));
169 var line = clipLine(Math.min(lines.length - 1, showingFrom + Math.floor((coords.y - off.top) / lineHeight())));
136 return clipPos({line: line, ch: charFromX(clipLine(line), coords.x)});
170 return clipPos({line: line, ch: charFromX(clipLine(line), coords.x - off.left)});
137 },
171 },
138 getSearchCursor: function(query, pos, caseFold) {return new SearchCursor(query, pos, caseFold);},
172 getSearchCursor: function(query, pos, caseFold) {return new SearchCursor(query, pos, caseFold);},
139 markText: operation(function(a, b, c){return operation(markText(a, b, c));}),
173 markText: operation(markText),
140 setMarker: addGutterMarker,
174 setMarker: operation(addGutterMarker),
141 clearMarker: removeGutterMarker,
175 clearMarker: operation(removeGutterMarker),
142 setLineClass: operation(setLineClass),
176 setLineClass: operation(setLineClass),
143 lineInfo: lineInfo,
177 lineInfo: lineInfo,
144 addWidget: function(pos, node, scroll) {
178 addWidget: function(pos, node, scroll, vert, horiz) {
145 var pos = localCoords(clipPos(pos), true);
179 pos = localCoords(clipPos(pos));
146 node.style.top = (showingFrom * lineHeight() + pos.yBot + paddingTop()) + "px";
180 var top = pos.yBot, left = pos.x;
147 node.style.left = (pos.x + paddingLeft()) + "px";
181 node.style.position = "absolute";
148 code.appendChild(node);
182 code.appendChild(node);
183 if (vert == "over") top = pos.y;
184 else if (vert == "near") {
185 var vspace = Math.max(scroller.offsetHeight, lines.length * lineHeight()),
186 hspace = Math.max(code.clientWidth, lineSpace.clientWidth) - paddingLeft();
187 if (pos.yBot + node.offsetHeight > vspace && pos.y > node.offsetHeight)
188 top = pos.y - node.offsetHeight;
189 if (left + node.offsetWidth > hspace)
190 left = hspace - node.offsetWidth;
191 }
192 node.style.top = (top + paddingTop()) + "px";
193 node.style.left = node.style.right = "";
194 if (horiz == "right") {
195 left = code.clientWidth - node.offsetWidth;
196 node.style.right = "0px";
197 } else {
198 if (horiz == "left") left = 0;
199 else if (horiz == "middle") left = (code.clientWidth - node.offsetWidth) / 2;
200 node.style.left = (left + paddingLeft()) + "px";
201 }
149 if (scroll)
202 if (scroll)
150 scrollIntoView(pos.x, pos.yBot, pos.x + node.offsetWidth, pos.yBot + node.offsetHeight);
203 scrollIntoView(left, top, left + node.offsetWidth, top + node.offsetHeight);
151 },
204 },
152
205
153 lineCount: function() {return lines.length;},
206 lineCount: function() {return lines.length;},
@@ -171,18 +224,30 b' var CodeMirror = (function() {'
171 replaceRange: operation(replaceRange),
224 replaceRange: operation(replaceRange),
172 getRange: function(from, to) {return getRange(clipPos(from), clipPos(to));},
225 getRange: function(from, to) {return getRange(clipPos(from), clipPos(to));},
173
226
227 coordsFromIndex: function(index) {
228 var total = lines.length, pos = 0, line, ch, len;
229
230 for (line = 0; line < total; line++) {
231 len = lines[line].text.length + 1;
232 if (pos + len > index) { ch = index - pos; break; }
233 pos += len;
234 }
235 return clipPos({line: line, ch: ch});
236 },
237
174 operation: function(f){return operation(f)();},
238 operation: function(f){return operation(f)();},
175 refresh: function(){updateDisplay(true);},
239 refresh: function(){updateDisplay(true);},
176 getInputField: function(){return input;},
240 getInputField: function(){return input;},
177 getWrapperElement: function(){return wrapper;}
241 getWrapperElement: function(){return wrapper;},
242 getScrollerElement: function(){return scroller;},
243 getGutterElement: function(){return gutter;}
178 };
244 };
179
245
180 function setValue(code) {
246 function setValue(code) {
181 history = null;
182 var top = {line: 0, ch: 0};
247 var top = {line: 0, ch: 0};
183 updateLines(top, {line: lines.length - 1, ch: lines[lines.length-1].text.length},
248 updateLines(top, {line: lines.length - 1, ch: lines[lines.length-1].text.length},
184 splitLines(code), top, top);
249 splitLines(code), top, top);
185 history = new History();
250 updateInput = true;
186 }
251 }
187 function getValue(code) {
252 function getValue(code) {
188 var text = [];
253 var text = [];
@@ -192,38 +257,70 b' var CodeMirror = (function() {'
192 }
257 }
193
258
194 function onMouseDown(e) {
259 function onMouseDown(e) {
260 // Check whether this is a click in a widget
261 for (var n = e_target(e); n != wrapper; n = n.parentNode)
262 if (n.parentNode == code && n != mover) return;
263
195 // First, see if this is a click in the gutter
264 // First, see if this is a click in the gutter
196 for (var n = e.target(); n != wrapper; n = n.parentNode)
265 for (var n = e_target(e); n != wrapper; n = n.parentNode)
197 if (n.parentNode == gutterText) {
266 if (n.parentNode == gutterText) {
198 if (options.onGutterClick)
267 if (options.onGutterClick)
199 options.onGutterClick(instance, indexOf(gutterText.childNodes, n) + showingFrom);
268 options.onGutterClick(instance, indexOf(gutterText.childNodes, n) + showingFrom, e);
200 return e.stop();
269 return e_preventDefault(e);
201 }
270 }
202
271
203 if (gecko && e.button() == 3) onContextMenu(e);
272 var start = posFromMouse(e);
204 if (e.button() != 1) return;
273
274 switch (e_button(e)) {
275 case 3:
276 if (gecko && !mac) onContextMenu(e);
277 return;
278 case 2:
279 if (start) setCursor(start.line, start.ch, true);
280 return;
281 }
205 // For button 1, if it was clicked inside the editor
282 // For button 1, if it was clicked inside the editor
206 // (posFromMouse returning non-null), we have to adjust the
283 // (posFromMouse returning non-null), we have to adjust the
207 // selection.
284 // selection.
208 var start = posFromMouse(e), last = start, going;
285 if (!start) {if (e_target(e) == scroller) e_preventDefault(e); return;}
209 if (!start) {if (e.target() == wrapper) e.stop(); return;}
210 setCursor(start.line, start.ch, false);
211
286
212 if (!focused) onFocus();
287 if (!focused) onFocus();
213 e.stop();
288
214 // And then we have to see if it's a drag event, in which case
289 var now = +new Date;
215 // the dragged-over text must be selected.
290 if (lastDoubleClick > now - 400) {
216 function end() {
291 e_preventDefault(e);
217 input.focus();
292 return selectLine(start.line);
218 updateInput = true;
293 } else if (lastClick > now - 400) {
219 move(); up();
294 lastDoubleClick = now;
295 e_preventDefault(e);
296 return selectWordAt(start);
297 } else { lastClick = now; }
298
299 var last = start, going;
300 if (dragAndDrop && !posEq(sel.from, sel.to) &&
301 !posLess(start, sel.from) && !posLess(sel.to, start)) {
302 // Let the drag handler handle this.
303 var up = connect(targetDocument, "mouseup", operation(function(e2) {
304 draggingText = false;
305 up();
306 if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) {
307 e_preventDefault(e2);
308 setCursor(start.line, start.ch, true);
309 focusInput();
310 }
311 }), true);
312 draggingText = true;
313 return;
220 }
314 }
315 e_preventDefault(e);
316 setCursor(start.line, start.ch, true);
317
221 function extend(e) {
318 function extend(e) {
222 var cur = posFromMouse(e, true);
319 var cur = posFromMouse(e, true);
223 if (cur && !posEq(cur, last)) {
320 if (cur && !posEq(cur, last)) {
224 if (!focused) onFocus();
321 if (!focused) onFocus();
225 last = cur;
322 last = cur;
226 setSelection(start, cur);
323 setSelectionUser(start, cur);
227 updateInput = false;
324 updateInput = false;
228 var visible = visibleLines();
325 var visible = visibleLines();
229 if (cur.line >= visible.to || cur.line < visible.from)
326 if (cur.line >= visible.to || cur.line < visible.from)
@@ -231,68 +328,95 b' var CodeMirror = (function() {'
231 }
328 }
232 }
329 }
233
330
234 var move = connect(document, "mousemove", operation(function(e) {
331 var move = connect(targetDocument, "mousemove", operation(function(e) {
235 clearTimeout(going);
332 clearTimeout(going);
236 e.stop();
333 e_preventDefault(e);
237 extend(e);
334 extend(e);
238 }), true);
335 }), true);
239 var up = connect(document, "mouseup", operation(function(e) {
336 var up = connect(targetDocument, "mouseup", operation(function(e) {
240 clearTimeout(going);
337 clearTimeout(going);
241 var cur = posFromMouse(e);
338 var cur = posFromMouse(e);
242 if (cur) setSelection(start, cur);
339 if (cur) setSelectionUser(start, cur);
243 e.stop();
340 e_preventDefault(e);
244 end();
341 focusInput();
342 updateInput = true;
343 move(); up();
245 }), true);
344 }), true);
246 }
345 }
247 function onDblClick(e) {
346 function onDoubleClick(e) {
248 var pos = posFromMouse(e);
347 var start = posFromMouse(e);
249 if (!pos) return;
348 if (!start) return;
250 selectWordAt(pos);
349 lastDoubleClick = +new Date;
251 e.stop();
350 e_preventDefault(e);
351 selectWordAt(start);
252 }
352 }
253 function onDrop(e) {
353 function onDrop(e) {
254 var pos = posFromMouse(e, true), files = e.e.dataTransfer.files;
354 e.preventDefault();
355 var pos = posFromMouse(e, true), files = e.dataTransfer.files;
255 if (!pos || options.readOnly) return;
356 if (!pos || options.readOnly) return;
256 if (files && files.length && window.FileReader && window.File) {
357 if (files && files.length && window.FileReader && window.File) {
257 var n = files.length, text = Array(n), read = 0;
258 for (var i = 0; i < n; ++i) loadFile(files[i], i);
259 function loadFile(file, i) {
358 function loadFile(file, i) {
260 var reader = new FileReader;
359 var reader = new FileReader;
261 reader.onload = function() {
360 reader.onload = function() {
262 text[i] = reader.result;
361 text[i] = reader.result;
263 if (++read == n) replaceRange(text.join(""), clipPos(pos), clipPos(pos));
362 if (++read == n) {
363 pos = clipPos(pos);
364 var end = replaceRange(text.join(""), pos, pos);
365 setSelectionUser(pos, end);
366 }
264 };
367 };
265 reader.readAsText(file);
368 reader.readAsText(file);
266 }
369 }
370 var n = files.length, text = Array(n), read = 0;
371 for (var i = 0; i < n; ++i) loadFile(files[i], i);
267 }
372 }
268 else {
373 else {
269 try {
374 try {
270 var text = e.e.dataTransfer.getData("Text");
375 var text = e.dataTransfer.getData("Text");
271 if (text) replaceRange(text, pos, pos);
376 if (text) {
377 var end = replaceRange(text, pos, pos);
378 var curFrom = sel.from, curTo = sel.to;
379 setSelectionUser(pos, end);
380 if (draggingText) replaceRange("", curFrom, curTo);
381 focusInput();
382 }
272 }
383 }
273 catch(e){}
384 catch(e){}
274 }
385 }
275 }
386 }
387 function onDragStart(e) {
388 var txt = getSelection();
389 // This will reset escapeElement
390 htmlEscape(txt);
391 e.dataTransfer.setDragImage(escapeElement, 0, 0);
392 e.dataTransfer.setData("Text", txt);
393 }
276 function onKeyDown(e) {
394 function onKeyDown(e) {
277 if (!focused) onFocus();
395 if (!focused) onFocus();
278
396
279 var code = e.e.keyCode;
397 var code = e.keyCode;
398 // IE does strange things with escape.
399 if (ie && code == 27) { e.returnValue = false; }
280 // Tries to detect ctrl on non-mac, cmd on mac.
400 // Tries to detect ctrl on non-mac, cmd on mac.
281 var mod = (mac ? e.e.metaKey : e.e.ctrlKey) && !e.e.altKey, anyMod = e.e.ctrlKey || e.e.altKey || e.e.metaKey;
401 var mod = (mac ? e.metaKey : e.ctrlKey) && !e.altKey, anyMod = e.ctrlKey || e.altKey || e.metaKey;
282 if (code == 16 || e.e.shiftKey) shiftSelecting = shiftSelecting || (sel.inverted ? sel.to : sel.from);
402 if (code == 16 || e.shiftKey) shiftSelecting = shiftSelecting || (sel.inverted ? sel.to : sel.from);
283 else shiftSelecting = null;
403 else shiftSelecting = null;
284 // First give onKeyEvent option a chance to handle this.
404 // First give onKeyEvent option a chance to handle this.
285 if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e.e))) return;
405 if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return;
286
406
287 if (code == 33 || code == 34) {scrollPage(code == 34); return e.stop();} // page up/down
407 if (code == 33 || code == 34) {scrollPage(code == 34); return e_preventDefault(e);} // page up/down
288 if (mod && (code == 36 || code == 35)) {scrollEnd(code == 36); return e.stop();} // ctrl-home/end
408 if (mod && ((code == 36 || code == 35) || // ctrl-home/end
289 if (mod && code == 65) {selectAll(); return e.stop();} // ctrl-a
409 mac && (code == 38 || code == 40))) { // cmd-up/down
410 scrollEnd(code == 36 || code == 38); return e_preventDefault(e);
411 }
412 if (mod && code == 65) {selectAll(); return e_preventDefault(e);} // ctrl-a
290 if (!options.readOnly) {
413 if (!options.readOnly) {
291 if (!anyMod && code == 13) {return;} // enter
414 if (!anyMod && code == 13) {return;} // enter
292 if (!anyMod && code == 9 && handleTab(e.e.shiftKey)) return e.stop(); // tab
415 if (!anyMod && code == 9 && handleTab(e.shiftKey)) return e_preventDefault(e); // tab
293 if (mod && code == 90) {undo(); return e.stop();} // ctrl-z
416 if (mod && code == 90) {undo(); return e_preventDefault(e);} // ctrl-z
294 if (mod && ((e.e.shiftKey && code == 90) || code == 89)) {redo(); return e.stop();} // ctrl-shift-z, ctrl-y
417 if (mod && ((e.shiftKey && code == 90) || code == 89)) {redo(); return e_preventDefault(e);} // ctrl-shift-z, ctrl-y
295 }
418 }
419 if (code == 36) { if (options.smartHome) { smartHome(); return e_preventDefault(e); } }
296
420
297 // Key id to use in the movementKeys map. We also pass it to
421 // Key id to use in the movementKeys map. We also pass it to
298 // fastPoll in order to 'self learn'. We need this because
422 // fastPoll in order to 'self learn'. We need this because
@@ -300,51 +424,60 b' var CodeMirror = (function() {'
300 // its start when it is inverted and a movement key is pressed
424 // its start when it is inverted and a movement key is pressed
301 // (and later restore it again), shouldn't be used for
425 // (and later restore it again), shouldn't be used for
302 // non-movement keys.
426 // non-movement keys.
303 curKeyId = (mod ? "c" : "") + code;
427 curKeyId = (mod ? "c" : "") + (e.altKey ? "a" : "") + code;
304 if (sel.inverted && movementKeys.hasOwnProperty(curKeyId)) {
428 if (sel.inverted && movementKeys[curKeyId] === true) {
305 var range = selRange(input);
429 var range = selRange(input);
306 if (range) {
430 if (range) {
307 reducedSelection = {anchor: range.start};
431 reducedSelection = {anchor: range.start};
308 setSelRange(input, range.start, range.start);
432 setSelRange(input, range.start, range.start);
309 }
433 }
310 }
434 }
435 // Don't save the key as a movementkey unless it had a modifier
436 if (!mod && !e.altKey) curKeyId = null;
311 fastPoll(curKeyId);
437 fastPoll(curKeyId);
312 }
438 }
313 function onKeyUp(e) {
439 function onKeyUp(e) {
440 if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return;
314 if (reducedSelection) {
441 if (reducedSelection) {
315 reducedSelection = null;
442 reducedSelection = null;
316 updateInput = true;
443 updateInput = true;
317 }
444 }
318 if (e.e.keyCode == 16) shiftSelecting = null;
445 if (e.keyCode == 16) shiftSelecting = null;
319 }
446 }
320 function onKeyPress(e) {
447 function onKeyPress(e) {
321 if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e.e))) return;
448 if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return;
322 if (options.electricChars && mode.electricChars) {
449 if (options.electricChars && mode.electricChars) {
323 var ch = String.fromCharCode(e.e.charCode == null ? e.e.keyCode : e.e.charCode);
450 var ch = String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode);
324 if (mode.electricChars.indexOf(ch) > -1)
451 if (mode.electricChars.indexOf(ch) > -1)
325 setTimeout(operation(function() {indentLine(sel.to.line, "smart");}), 50);
452 setTimeout(operation(function() {indentLine(sel.to.line, "smart");}), 50);
326 }
453 }
327 var code = e.e.keyCode;
454 var code = e.keyCode;
328 // Re-stop tab and enter. Necessary on some browsers.
455 // Re-stop tab and enter. Necessary on some browsers.
329 if (code == 13) {handleEnter(); e.stop();}
456 if (code == 13) {if (!options.readOnly) handleEnter(); e_preventDefault(e);}
330 else if (code == 9 && options.tabMode != "default") e.stop();
457 else if (!e.ctrlKey && !e.altKey && !e.metaKey && code == 9 && options.tabMode != "default") e_preventDefault(e);
331 else fastPoll(curKeyId);
458 else fastPoll(curKeyId);
332 }
459 }
333
460
334 function onFocus() {
461 function onFocus() {
335 if (!focused && options.onFocus) options.onFocus(instance);
462 if (options.readOnly == "nocursor") return;
336 focused = true;
463 if (!focused) {
464 if (options.onFocus) options.onFocus(instance);
465 focused = true;
466 if (wrapper.className.search(/\bCodeMirror-focused\b/) == -1)
467 wrapper.className += " CodeMirror-focused";
468 if (!leaveInputAlone) prepareInput();
469 }
337 slowPoll();
470 slowPoll();
338 if (wrapper.className.search(/\bCodeMirror-focused\b/) == -1)
339 wrapper.className += " CodeMirror-focused";
340 restartBlink();
471 restartBlink();
341 }
472 }
342 function onBlur() {
473 function onBlur() {
343 if (focused && options.onBlur) options.onBlur(instance);
474 if (focused) {
475 if (options.onBlur) options.onBlur(instance);
476 focused = false;
477 wrapper.className = wrapper.className.replace(" CodeMirror-focused", "");
478 }
344 clearInterval(blinker);
479 clearInterval(blinker);
345 shiftSelecting = null;
480 setTimeout(function() {if (!focused) shiftSelecting = null;}, 150);
346 focused = false;
347 wrapper.className = wrapper.className.replace(" CodeMirror-focused", "");
348 }
481 }
349
482
350 // Replace the range from from to to by the strings in newText.
483 // Replace the range from from to to by the strings in newText.
@@ -367,12 +500,18 b' var CodeMirror = (function() {'
367 var pos = clipPos({line: change.start + change.old.length - 1,
500 var pos = clipPos({line: change.start + change.old.length - 1,
368 ch: editEnd(replaced[replaced.length-1], change.old[change.old.length-1])});
501 ch: editEnd(replaced[replaced.length-1], change.old[change.old.length-1])});
369 updateLinesNoUndo({line: change.start, ch: 0}, {line: end - 1, ch: lines[end-1].text.length}, change.old, pos, pos);
502 updateLinesNoUndo({line: change.start, ch: 0}, {line: end - 1, ch: lines[end-1].text.length}, change.old, pos, pos);
503 updateInput = true;
370 }
504 }
371 }
505 }
372 function undo() {unredoHelper(history.done, history.undone);}
506 function undo() {unredoHelper(history.done, history.undone);}
373 function redo() {unredoHelper(history.undone, history.done);}
507 function redo() {unredoHelper(history.undone, history.done);}
374
508
375 function updateLinesNoUndo(from, to, newText, selFrom, selTo) {
509 function updateLinesNoUndo(from, to, newText, selFrom, selTo) {
510 var recomputeMaxLength = false, maxLineLength = maxLine.length;
511 for (var i = from.line; i <= to.line; ++i) {
512 if (lines[i].text.length == maxLineLength) {recomputeMaxLength = true; break;}
513 }
514
376 var nlines = to.line - from.line, firstLine = lines[from.line], lastLine = lines[to.line];
515 var nlines = to.line - from.line, firstLine = lines[from.line], lastLine = lines[to.line];
377 // First adjust the line structure, taking some care to leave highlighting intact.
516 // First adjust the line structure, taking some care to leave highlighting intact.
378 if (firstLine == lastLine) {
517 if (firstLine == lastLine) {
@@ -381,24 +520,46 b' var CodeMirror = (function() {'
381 else {
520 else {
382 lastLine = firstLine.split(to.ch, newText[newText.length-1]);
521 lastLine = firstLine.split(to.ch, newText[newText.length-1]);
383 var spliceargs = [from.line + 1, nlines];
522 var spliceargs = [from.line + 1, nlines];
384 firstLine.replace(from.ch, firstLine.text.length, newText[0]);
523 firstLine.replace(from.ch, null, newText[0]);
385 for (var i = 1, e = newText.length - 1; i < e; ++i) spliceargs.push(new Line(newText[i]));
524 for (var i = 1, e = newText.length - 1; i < e; ++i)
525 spliceargs.push(Line.inheritMarks(newText[i], firstLine));
386 spliceargs.push(lastLine);
526 spliceargs.push(lastLine);
387 lines.splice.apply(lines, spliceargs);
527 lines.splice.apply(lines, spliceargs);
388 }
528 }
389 }
529 }
390 else if (newText.length == 1) {
530 else if (newText.length == 1) {
391 firstLine.replace(from.ch, firstLine.text.length, newText[0] + lastLine.text.slice(to.ch));
531 firstLine.replace(from.ch, null, newText[0]);
532 lastLine.replace(null, to.ch, "");
533 firstLine.append(lastLine);
392 lines.splice(from.line + 1, nlines);
534 lines.splice(from.line + 1, nlines);
393 }
535 }
394 else {
536 else {
395 var spliceargs = [from.line + 1, nlines - 1];
537 var spliceargs = [from.line + 1, nlines - 1];
396 firstLine.replace(from.ch, firstLine.text.length, newText[0]);
538 firstLine.replace(from.ch, null, newText[0]);
397 lastLine.replace(0, to.ch, newText[newText.length-1]);
539 lastLine.replace(null, to.ch, newText[newText.length-1]);
398 for (var i = 1, e = newText.length - 1; i < e; ++i) spliceargs.push(new Line(newText[i]));
540 for (var i = 1, e = newText.length - 1; i < e; ++i)
541 spliceargs.push(Line.inheritMarks(newText[i], firstLine));
399 lines.splice.apply(lines, spliceargs);
542 lines.splice.apply(lines, spliceargs);
400 }
543 }
401
544
545
546 for (var i = from.line, e = i + newText.length; i < e; ++i) {
547 var l = lines[i].text;
548 if (l.length > maxLineLength) {
549 maxLine = l; maxLineLength = l.length; maxWidth = null;
550 recomputeMaxLength = false;
551 }
552 }
553 if (recomputeMaxLength) {
554 maxLineLength = 0; maxLine = ""; maxWidth = null;
555 for (var i = 0, e = lines.length; i < e; ++i) {
556 var l = lines[i].text;
557 if (l.length > maxLineLength) {
558 maxLineLength = l.length; maxLine = l;
559 }
560 }
561 }
562
402 // Add these lines to the work array, so that they will be
563 // Add these lines to the work array, so that they will be
403 // highlighted. Adjust work lines if lines were added/removed.
564 // highlighted. Adjust work lines if lines were added/removed.
404 var newWork = [], lendiff = newText.length - nlines - 1;
565 var newWork = [], lendiff = newText.length - nlines - 1;
@@ -407,12 +568,17 b' var CodeMirror = (function() {'
407 if (task < from.line) newWork.push(task);
568 if (task < from.line) newWork.push(task);
408 else if (task > to.line) newWork.push(task + lendiff);
569 else if (task > to.line) newWork.push(task + lendiff);
409 }
570 }
410 if (newText.length) newWork.push(from.line);
571 if (newText.length < 5) {
572 highlightLines(from.line, from.line + newText.length);
573 newWork.push(from.line + newText.length);
574 } else {
575 newWork.push(from.line);
576 }
411 work = newWork;
577 work = newWork;
412 startWorker(100);
578 startWorker(100);
413 // Remember that these lines changed, for updating the display
579 // Remember that these lines changed, for updating the display
414 changes.push({from: from.line, to: to.line + 1, diff: lendiff});
580 changes.push({from: from.line, to: to.line + 1, diff: lendiff});
415 textChanged = true;
581 textChanged = {from: from, to: to, text: newText};
416
582
417 // Update the selection
583 // Update the selection
418 function updateLine(n) {return n <= Math.min(to.line, to.line + lendiff) ? n : n + lendiff;}
584 function updateLine(n) {return n <= Math.min(to.line, to.line + lendiff) ? n : n + lendiff;}
@@ -483,7 +649,10 b' var CodeMirror = (function() {'
483 function p() {
649 function p() {
484 startOperation();
650 startOperation();
485 var changed = readInput();
651 var changed = readInput();
486 if (changed == "moved" && keyId) movementKeys[keyId] = true;
652 if (changed && keyId) {
653 if (changed == "moved" && movementKeys[keyId] == null) movementKeys[keyId] = true;
654 if (changed == "changed") movementKeys[keyId] = false;
655 }
487 if (!changed && !missed) {missed = true; poll.set(80, p);}
656 if (!changed && !missed) {missed = true; poll.set(80, p);}
488 else {pollingFast = false; slowPoll();}
657 else {pollingFast = false; slowPoll();}
489 endOperation();
658 endOperation();
@@ -495,13 +664,12 b' var CodeMirror = (function() {'
495 // to the data in the editing variable, and updates the editor
664 // to the data in the editing variable, and updates the editor
496 // content or cursor if something changed.
665 // content or cursor if something changed.
497 function readInput() {
666 function readInput() {
667 if (leaveInputAlone || !focused) return;
498 var changed = false, text = input.value, sr = selRange(input);
668 var changed = false, text = input.value, sr = selRange(input);
499 if (!sr) return false;
669 if (!sr) return false;
500 var changed = editing.text != text, rs = reducedSelection;
670 var changed = editing.text != text, rs = reducedSelection;
501 var moved = changed || sr.start != editing.start || sr.end != (rs ? editing.start : editing.end);
671 var moved = changed || sr.start != editing.start || sr.end != (rs ? editing.start : editing.end);
502 if (reducedSelection && !moved && sel.from.line == 0 && sel.from.ch == 0)
672 if (!moved && !rs) return false;
503 reducedSelection = null;
504 else if (!moved) return false;
505 if (changed) {
673 if (changed) {
506 shiftSelecting = reducedSelection = null;
674 shiftSelecting = reducedSelection = null;
507 if (options.readOnly) {updateInput = true; return "changed";}
675 if (options.readOnly) {updateInput = true; return "changed";}
@@ -524,13 +692,10 b' var CodeMirror = (function() {'
524 // so that you can, for example, press shift-up at the start of
692 // so that you can, for example, press shift-up at the start of
525 // your selection and have the right thing happen.
693 // your selection and have the right thing happen.
526 if (rs) {
694 if (rs) {
527 from = sr.start == rs.anchor ? to : from;
695 var head = sr.start == rs.anchor ? to : from;
528 to = shiftSelecting ? sel.to : sr.start == rs.anchor ? from : to;
696 var tail = shiftSelecting ? sel.to : sr.start == rs.anchor ? from : to;
529 if (!posLess(from, to)) {
697 if (sel.inverted = posLess(head, tail)) { from = head; to = tail; }
530 reducedSelection = null;
698 else { reducedSelection = null; from = tail; to = head; }
531 sel.inverted = false;
532 var tmp = from; from = to; to = tmp;
533 }
534 }
699 }
535
700
536 // In some cases (cursor on same line as before), we don't have
701 // In some cases (cursor on same line as before), we don't have
@@ -550,8 +715,8 b' var CodeMirror = (function() {'
550 var ch = nl > -1 ? start - nl : start, endline = editing.to - 1, edend = editing.text.length;
715 var ch = nl > -1 ? start - nl : start, endline = editing.to - 1, edend = editing.text.length;
551 for (;;) {
716 for (;;) {
552 c = editing.text.charAt(edend);
717 c = editing.text.charAt(edend);
718 if (text.charAt(end) != c) {++end; ++edend; break;}
553 if (c == "\n") endline--;
719 if (c == "\n") endline--;
554 if (text.charAt(end) != c) {++end; ++edend; break;}
555 if (edend <= start || end <= start) break;
720 if (edend <= start || end <= start) break;
556 --end; --edend;
721 --end; --edend;
557 }
722 }
@@ -580,22 +745,36 b' var CodeMirror = (function() {'
580 editing = {text: text, from: from, to: to, start: startch, end: endch};
745 editing = {text: text, from: from, to: to, start: startch, end: endch};
581 setSelRange(input, startch, reducedSelection ? startch : endch);
746 setSelRange(input, startch, reducedSelection ? startch : endch);
582 }
747 }
748 function focusInput() {
749 if (options.readOnly != "nocursor") input.focus();
750 }
583
751
752 function scrollEditorIntoView() {
753 if (!cursor.getBoundingClientRect) return;
754 var rect = cursor.getBoundingClientRect();
755 var winH = window.innerHeight || Math.max(document.body.offsetHeight, document.documentElement.offsetHeight);
756 if (rect.top < 0 || rect.bottom > winH) cursor.scrollIntoView();
757 }
584 function scrollCursorIntoView() {
758 function scrollCursorIntoView() {
585 var cursor = localCoords(sel.inverted ? sel.from : sel.to);
759 var cursor = localCoords(sel.inverted ? sel.from : sel.to);
586 return scrollIntoView(cursor.x, cursor.y, cursor.x, cursor.yBot);
760 return scrollIntoView(cursor.x, cursor.y, cursor.x, cursor.yBot);
587 }
761 }
588 function scrollIntoView(x1, y1, x2, y2) {
762 function scrollIntoView(x1, y1, x2, y2) {
589 var pl = paddingLeft(), pt = paddingTop();
763 var pl = paddingLeft(), pt = paddingTop(), lh = lineHeight();
590 y1 += pt; y2 += pt; x1 += pl; x2 += pl;
764 y1 += pt; y2 += pt; x1 += pl; x2 += pl;
591 var screen = wrapper.clientHeight, screentop = wrapper.scrollTop, scrolled = false, result = true;
765 var screen = scroller.clientHeight, screentop = scroller.scrollTop, scrolled = false, result = true;
592 if (y1 < screentop) {wrapper.scrollTop = Math.max(0, y1 - 10); scrolled = true;}
766 if (y1 < screentop) {scroller.scrollTop = Math.max(0, y1 - 2*lh); scrolled = true;}
593 else if (y2 > screentop + screen) {wrapper.scrollTop = y2 + 10 - screen; scrolled = true;}
767 else if (y2 > screentop + screen) {scroller.scrollTop = y2 + lh - screen; scrolled = true;}
594
768
595 var screenw = wrapper.clientWidth, screenleft = wrapper.scrollLeft;
769 var screenw = scroller.clientWidth, screenleft = scroller.scrollLeft;
596 if (x1 < screenleft) {wrapper.scrollLeft = Math.max(0, x1 - 10); scrolled = true;}
770 var gutterw = options.fixedGutter ? gutter.clientWidth : 0;
771 if (x1 < screenleft + gutterw) {
772 if (x1 < 50) x1 = 0;
773 scroller.scrollLeft = Math.max(0, x1 - 10 - gutterw);
774 scrolled = true;
775 }
597 else if (x2 > screenw + screenleft) {
776 else if (x2 > screenw + screenleft) {
598 wrapper.scrollLeft = x2 + 10 - screenw;
777 scroller.scrollLeft = x2 + 10 - screenw;
599 scrolled = true;
778 scrolled = true;
600 if (x2 > code.clientWidth) result = false;
779 if (x2 > code.clientWidth) result = false;
601 }
780 }
@@ -604,15 +783,15 b' var CodeMirror = (function() {'
604 }
783 }
605
784
606 function visibleLines() {
785 function visibleLines() {
607 var lh = lineHeight(), top = wrapper.scrollTop - paddingTop();
786 var lh = lineHeight(), top = scroller.scrollTop - paddingTop();
608 return {from: Math.min(lines.length, Math.max(0, Math.floor(top / lh))),
787 return {from: Math.min(lines.length, Math.max(0, Math.floor(top / lh))),
609 to: Math.min(lines.length, Math.ceil((top + wrapper.clientHeight) / lh))};
788 to: Math.min(lines.length, Math.ceil((top + scroller.clientHeight) / lh))};
610 }
789 }
611 // Uses a set of changes plus the current scroll position to
790 // Uses a set of changes plus the current scroll position to
612 // determine which DOM updates have to be made, and makes the
791 // determine which DOM updates have to be made, and makes the
613 // updates.
792 // updates.
614 function updateDisplay(changes) {
793 function updateDisplay(changes) {
615 if (!wrapper.clientWidth) {
794 if (!scroller.clientWidth) {
616 showingFrom = showingTo = 0;
795 showingFrom = showingTo = 0;
617 return;
796 return;
618 }
797 }
@@ -629,7 +808,7 b' var CodeMirror = (function() {'
629 intact2.push(range);
808 intact2.push(range);
630 else {
809 else {
631 if (change.from > range.from)
810 if (change.from > range.from)
632 intact2.push({from: range.from, to: change.from, domStart: range.domStart})
811 intact2.push({from: range.from, to: change.from, domStart: range.domStart});
633 if (change.to < range.to)
812 if (change.to < range.to)
634 intact2.push({from: change.to + diff, to: range.to + diff,
813 intact2.push({from: change.to + diff, to: range.to + diff,
635 domStart: range.domStart + (change.to - range.from)});
814 domStart: range.domStart + (change.to - range.from)});
@@ -659,6 +838,7 b' var CodeMirror = (function() {'
659 if (domPos != domEnd || pos != to) {
838 if (domPos != domEnd || pos != to) {
660 changedLines += Math.abs(to - pos);
839 changedLines += Math.abs(to - pos);
661 updates.push({from: pos, to: to, domSize: domEnd - domPos, domStart: domPos});
840 updates.push({from: pos, to: to, domSize: domEnd - domPos, domStart: domPos});
841 if (to - pos != domEnd - domPos) gutterDirty = true;
662 }
842 }
663
843
664 if (!updates.length) return;
844 if (!updates.length) return;
@@ -674,13 +854,23 b' var CodeMirror = (function() {'
674
854
675 // Position the mover div to align with the lines it's supposed
855 // Position the mover div to align with the lines it's supposed
676 // to be showing (which will cover the visible display)
856 // to be showing (which will cover the visible display)
677 var different = from != showingFrom || to != showingTo || lastHeight != wrapper.clientHeight;
857 var different = from != showingFrom || to != showingTo || lastHeight != scroller.clientHeight;
678 showingFrom = from; showingTo = to;
858 showingFrom = from; showingTo = to;
679 mover.style.top = (from * lineHeight()) + "px";
859 mover.style.top = (from * lineHeight()) + "px";
680 if (different) {
860 if (different) {
681 lastHeight = wrapper.clientHeight;
861 lastHeight = scroller.clientHeight;
682 code.style.height = (lines.length * lineHeight() + 2 * paddingTop()) + "px";
862 code.style.height = (lines.length * lineHeight() + 2 * paddingTop()) + "px";
683 updateGutter();
863 }
864 if (different || gutterDirty) updateGutter();
865
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 = "";
684 }
874 }
685
875
686 // Since this is all rather error prone, it is honoured with the
876 // Since this is all rather error prone, it is honoured with the
@@ -712,7 +902,7 b' var CodeMirror = (function() {'
712 // there .innerHTML on PRE nodes is dumb, and discards
902 // there .innerHTML on PRE nodes is dumb, and discards
713 // whitespace.
903 // whitespace.
714 var sfrom = sel.from.line, sto = sel.to.line, off = 0,
904 var sfrom = sel.from.line, sto = sel.to.line, off = 0,
715 scratch = badInnerHTML && document.createElement("div");
905 scratch = badInnerHTML && targetDocument.createElement("div");
716 for (var i = 0, e = updates.length; i < e; ++i) {
906 for (var i = 0, e = updates.length; i < e; ++i) {
717 var rec = updates[i];
907 var rec = updates[i];
718 var extra = (rec.to - rec.from) - rec.domSize;
908 var extra = (rec.to - rec.from) - rec.domSize;
@@ -722,7 +912,7 b' var CodeMirror = (function() {'
722 lineDiv.removeChild(nodeAfter ? nodeAfter.previousSibling : lineDiv.lastChild);
912 lineDiv.removeChild(nodeAfter ? nodeAfter.previousSibling : lineDiv.lastChild);
723 else if (extra) {
913 else if (extra) {
724 for (var j = Math.max(0, extra); j > 0; --j)
914 for (var j = Math.max(0, extra); j > 0; --j)
725 lineDiv.insertBefore(document.createElement("pre"), nodeAfter);
915 lineDiv.insertBefore(targetDocument.createElement("pre"), nodeAfter);
726 for (var j = Math.max(0, -extra); j > 0; --j)
916 for (var j = Math.max(0, -extra); j > 0; --j)
727 lineDiv.removeChild(nodeAfter ? nodeAfter.previousSibling : lineDiv.lastChild);
917 lineDiv.removeChild(nodeAfter ? nodeAfter.previousSibling : lineDiv.lastChild);
728 }
918 }
@@ -753,10 +943,10 b' var CodeMirror = (function() {'
753
943
754 function updateGutter() {
944 function updateGutter() {
755 if (!options.gutter && !options.lineNumbers) return;
945 if (!options.gutter && !options.lineNumbers) return;
756 var hText = mover.offsetHeight, hEditor = wrapper.clientHeight;
946 var hText = mover.offsetHeight, hEditor = scroller.clientHeight;
757 gutter.style.height = (hText - hEditor < 2 ? hEditor : hText) + "px";
947 gutter.style.height = (hText - hEditor < 2 ? hEditor : hText) + "px";
758 var html = [];
948 var html = [];
759 for (var i = showingFrom; i < showingTo; ++i) {
949 for (var i = showingFrom; i < Math.max(showingTo, showingFrom + 1); ++i) {
760 var marker = lines[i].gutterMarker;
950 var marker = lines[i].gutterMarker;
761 var text = options.lineNumbers ? i + options.firstLineNumber : null;
951 var text = options.lineNumbers ? i + options.firstLineNumber : null;
762 if (marker && marker.text)
952 if (marker && marker.text)
@@ -769,37 +959,43 b' var CodeMirror = (function() {'
769 gutterText.innerHTML = html.join("");
959 gutterText.innerHTML = html.join("");
770 var minwidth = String(lines.length).length, firstNode = gutterText.firstChild, val = eltText(firstNode), pad = "";
960 var minwidth = String(lines.length).length, firstNode = gutterText.firstChild, val = eltText(firstNode), pad = "";
771 while (val.length + pad.length < minwidth) pad += "\u00a0";
961 while (val.length + pad.length < minwidth) pad += "\u00a0";
772 if (pad) firstNode.insertBefore(document.createTextNode(pad), firstNode.firstChild);
962 if (pad) firstNode.insertBefore(targetDocument.createTextNode(pad), firstNode.firstChild);
773 gutter.style.display = "";
963 gutter.style.display = "";
774 lineSpace.style.marginLeft = gutter.offsetWidth + "px";
964 lineSpace.style.marginLeft = gutter.offsetWidth + "px";
965 gutterDirty = false;
775 }
966 }
776 function updateCursor() {
967 function updateCursor() {
777 var head = sel.inverted ? sel.from : sel.to;
968 var head = sel.inverted ? sel.from : sel.to, lh = lineHeight();
778 var x = charX(head.line, head.ch) + "px", y = (head.line - showingFrom) * lineHeight() + "px";
969 var x = charX(head.line, head.ch);
779 inputDiv.style.top = y; inputDiv.style.left = x;
970 var top = head.line * lh - scroller.scrollTop;
971 inputDiv.style.top = Math.max(Math.min(top, scroller.offsetHeight), 0) + "px";
972 inputDiv.style.left = (x - scroller.scrollLeft) + "px";
780 if (posEq(sel.from, sel.to)) {
973 if (posEq(sel.from, sel.to)) {
781 cursor.style.top = y; cursor.style.left = x;
974 cursor.style.top = (head.line - showingFrom) * lh + "px";
975 cursor.style.left = x + "px";
782 cursor.style.display = "";
976 cursor.style.display = "";
783 }
977 }
784 else cursor.style.display = "none";
978 else cursor.style.display = "none";
785 }
979 }
786
980
981 function setSelectionUser(from, to) {
982 var sh = shiftSelecting && clipPos(shiftSelecting);
983 if (sh) {
984 if (posLess(sh, from)) from = sh;
985 else if (posLess(to, sh)) to = sh;
986 }
987 setSelection(from, to);
988 }
787 // Update the selection. Last two args are only used by
989 // Update the selection. Last two args are only used by
788 // updateLines, since they have to be expressed in the line
990 // updateLines, since they have to be expressed in the line
789 // numbers before the update.
991 // numbers before the update.
790 function setSelection(from, to, oldFrom, oldTo) {
992 function setSelection(from, to, oldFrom, oldTo) {
791 if (posEq(sel.from, from) && posEq(sel.to, to)) return;
993 if (posEq(sel.from, from) && posEq(sel.to, to)) return;
792 var sh = shiftSelecting && clipPos(shiftSelecting);
793 if (posLess(to, from)) {var tmp = to; to = from; from = tmp;}
994 if (posLess(to, from)) {var tmp = to; to = from; from = tmp;}
794 if (sh) {
795 if (posLess(sh, from)) from = sh;
796 else if (posLess(to, sh)) to = sh;
797 }
798
995
799 var startEq = posEq(sel.to, to), endEq = posEq(sel.from, from);
800 if (posEq(from, to)) sel.inverted = false;
996 if (posEq(from, to)) sel.inverted = false;
801 else if (startEq && !endEq) sel.inverted = true;
997 else if (posEq(from, sel.to)) sel.inverted = false;
802 else if (endEq && !startEq) sel.inverted = false;
998 else if (posEq(to, sel.from)) sel.inverted = true;
803
999
804 // Some ugly logic used to only mark the lines that actually did
1000 // Some ugly logic used to only mark the lines that actually did
805 // see a change in selection as changed, rather than the whole
1001 // see a change in selection as changed, rather than the whole
@@ -829,9 +1025,9 b' var CodeMirror = (function() {'
829 sel.from = from; sel.to = to;
1025 sel.from = from; sel.to = to;
830 selectionChanged = true;
1026 selectionChanged = true;
831 }
1027 }
832 function setCursor(line, ch) {
1028 function setCursor(line, ch, user) {
833 var pos = clipPos({line: line, ch: ch || 0});
1029 var pos = clipPos({line: line, ch: ch || 0});
834 setSelection(pos, pos);
1030 (user ? setSelectionUser : setSelection)(pos, pos);
835 }
1031 }
836
1032
837 function clipLine(n) {return Math.max(0, Math.min(n, lines.length-1));}
1033 function clipLine(n) {return Math.max(0, Math.min(n, lines.length-1));}
@@ -845,11 +1041,12 b' var CodeMirror = (function() {'
845 }
1041 }
846
1042
847 function scrollPage(down) {
1043 function scrollPage(down) {
848 var linesPerPage = Math.floor(wrapper.clientHeight / lineHeight()), head = sel.inverted ? sel.from : sel.to;
1044 var linesPerPage = Math.floor(scroller.clientHeight / lineHeight()), head = sel.inverted ? sel.from : sel.to;
849 setCursor(head.line + (Math.max(linesPerPage - 1, 1) * (down ? 1 : -1)), head.ch);
1045 setCursor(head.line + (Math.max(linesPerPage - 1, 1) * (down ? 1 : -1)), head.ch, true);
850 }
1046 }
851 function scrollEnd(top) {
1047 function scrollEnd(top) {
852 setCursor(top ? 0 : lines.length - 1);
1048 var pos = top ? {line: 0, ch: 0} : {line: lines.length - 1, ch: lines[lines.length-1].text.length};
1049 setSelectionUser(pos, pos);
853 }
1050 }
854 function selectAll() {
1051 function selectAll() {
855 var endLine = lines.length - 1;
1052 var endLine = lines.length - 1;
@@ -859,8 +1056,11 b' var CodeMirror = (function() {'
859 var line = lines[pos.line].text;
1056 var line = lines[pos.line].text;
860 var start = pos.ch, end = pos.ch;
1057 var start = pos.ch, end = pos.ch;
861 while (start > 0 && /\w/.test(line.charAt(start - 1))) --start;
1058 while (start > 0 && /\w/.test(line.charAt(start - 1))) --start;
862 while (end < line.length - 1 && /\w/.test(line.charAt(end))) ++end;
1059 while (end < line.length && /\w/.test(line.charAt(end))) ++end;
863 setSelection({line: pos.line, ch: start}, {line: pos.line, ch: end});
1060 setSelectionUser({line: pos.line, ch: start}, {line: pos.line, ch: end});
1061 }
1062 function selectLine(line) {
1063 setSelectionUser({line: line, ch: 0}, {line: line, ch: lines[line].text.length});
864 }
1064 }
865 function handleEnter() {
1065 function handleEnter() {
866 replaceSelection("\n", "end");
1066 replaceSelection("\n", "end");
@@ -868,12 +1068,17 b' var CodeMirror = (function() {'
868 indentLine(sel.from.line, options.enterMode == "keep" ? "prev" : "smart");
1068 indentLine(sel.from.line, options.enterMode == "keep" ? "prev" : "smart");
869 }
1069 }
870 function handleTab(shift) {
1070 function handleTab(shift) {
1071 function indentSelected(mode) {
1072 if (posEq(sel.from, sel.to)) return indentLine(sel.from.line, mode);
1073 var e = sel.to.line - (sel.to.ch ? 0 : 1);
1074 for (var i = sel.from.line; i <= e; ++i) indentLine(i, mode);
1075 }
871 shiftSelecting = null;
1076 shiftSelecting = null;
872 switch (options.tabMode) {
1077 switch (options.tabMode) {
873 case "default":
1078 case "default":
874 return false;
1079 return false;
875 case "indent":
1080 case "indent":
876 for (var i = sel.from.line, e = sel.to.line; i <= e; ++i) indentLine(i, "smart");
1081 indentSelected("smart");
877 break;
1082 break;
878 case "classic":
1083 case "classic":
879 if (posEq(sel.from, sel.to)) {
1084 if (posEq(sel.from, sel.to)) {
@@ -882,11 +1087,15 b' var CodeMirror = (function() {'
882 break;
1087 break;
883 }
1088 }
884 case "shift":
1089 case "shift":
885 for (var i = sel.from.line, e = sel.to.line; i <= e; ++i) indentLine(i, shift ? "subtract" : "add");
1090 indentSelected(shift ? "subtract" : "add");
886 break;
1091 break;
887 }
1092 }
888 return true;
1093 return true;
889 }
1094 }
1095 function smartHome() {
1096 var firstNonWS = Math.max(0, lines[sel.from.line].text.search(/\S/));
1097 setCursor(sel.from.line, sel.from.ch <= firstNonWS && sel.from.ch ? 0 : firstNonWS, true);
1098 }
890
1099
891 function indentLine(n, how) {
1100 function indentLine(n, how) {
892 if (how == "smart") {
1101 if (how == "smart") {
@@ -924,21 +1133,20 b' var CodeMirror = (function() {'
924 for (var i = 0, l = lines.length; i < l; ++i)
1133 for (var i = 0, l = lines.length; i < l; ++i)
925 lines[i].stateAfter = null;
1134 lines[i].stateAfter = null;
926 work = [0];
1135 work = [0];
1136 startWorker();
927 }
1137 }
928 function gutterChanged() {
1138 function gutterChanged() {
929 var visible = options.gutter || options.lineNumbers;
1139 var visible = options.gutter || options.lineNumbers;
930 gutter.style.display = visible ? "" : "none";
1140 gutter.style.display = visible ? "" : "none";
931 if (visible) updateGutter();
1141 if (visible) gutterDirty = true;
932 else lineDiv.parentNode.style.marginLeft = 0;
1142 else lineDiv.parentNode.style.marginLeft = 0;
933 }
1143 }
934
1144
935 function markText(from, to, className) {
1145 function markText(from, to, className) {
936 from = clipPos(from); to = clipPos(to);
1146 from = clipPos(from); to = clipPos(to);
937 var accum = [];
1147 var set = [];
938 function add(line, from, to, className) {
1148 function add(line, from, to, className) {
939 var line = lines[line], mark = line.addMark(from, to, className);
1149 mark = lines[line].addMark(from, to, className, set);
940 mark.line = line;
941 accum.push(mark);
942 }
1150 }
943 if (from.line == to.line) add(from.line, from.ch, to.ch, className);
1151 if (from.line == to.line) add(from.line, from.ch, to.ch, className);
944 else {
1152 else {
@@ -948,30 +1156,51 b' var CodeMirror = (function() {'
948 add(to.line, 0, to.ch, className);
1156 add(to.line, 0, to.ch, className);
949 }
1157 }
950 changes.push({from: from.line, to: to.line + 1});
1158 changes.push({from: from.line, to: to.line + 1});
951 return function() {
1159 return new TextMarker(set);
952 var start, end;
1160 }
953 for (var i = 0; i < accum.length; ++i) {
1161
954 var mark = accum[i], found = indexOf(lines, mark.line);
1162 function TextMarker(set) { this.set = set; }
955 mark.line.removeMark(mark);
1163 TextMarker.prototype.clear = operation(function() {
956 if (found > -1) {
1164 for (var i = 0, e = this.set.length; i < e; ++i) {
957 if (start == null) start = found;
1165 var mk = this.set[i].marked;
958 end = found;
1166 for (var j = 0; j < mk.length; ++j) {
1167 if (mk[j].set == this.set) mk.splice(j--, 1);
1168 }
1169 }
1170 // We don't know the exact lines that changed. Refreshing is
1171 // cheaper than finding them.
1172 changes.push({from: 0, to: lines.length});
1173 });
1174 TextMarker.prototype.find = function() {
1175 var from, to;
1176 for (var i = 0, e = this.set.length; i < e; ++i) {
1177 var line = this.set[i], mk = line.marked;
1178 for (var j = 0; j < mk.length; ++j) {
1179 var mark = mk[j];
1180 if (mark.set == this.set) {
1181 if (mark.from != null || mark.to != null) {
1182 var found = indexOf(lines, line);
1183 if (found > -1) {
1184 if (mark.from != null) from = {line: found, ch: mark.from};
1185 if (mark.to != null) to = {line: found, ch: mark.to};
1186 }
1187 }
959 }
1188 }
960 }
1189 }
961 if (start != null) changes.push({from: start, to: end + 1});
1190 }
962 };
1191 return {from: from, to: to};
963 }
1192 };
964
1193
965 function addGutterMarker(line, text, className) {
1194 function addGutterMarker(line, text, className) {
966 if (typeof line == "number") line = lines[clipLine(line)];
1195 if (typeof line == "number") line = lines[clipLine(line)];
967 line.gutterMarker = {text: text, style: className};
1196 line.gutterMarker = {text: text, style: className};
968 updateGutter();
1197 gutterDirty = true;
969 return line;
1198 return line;
970 }
1199 }
971 function removeGutterMarker(line) {
1200 function removeGutterMarker(line) {
972 if (typeof line == "number") line = lines[clipLine(line)];
1201 if (typeof line == "number") line = lines[clipLine(line)];
973 line.gutterMarker = null;
1202 line.gutterMarker = null;
974 updateGutter();
1203 gutterDirty = true;
975 }
1204 }
976 function setLineClass(line, className) {
1205 function setLineClass(line, className) {
977 if (typeof line == "number") {
1206 if (typeof line == "number") {
@@ -982,8 +1211,10 b' var CodeMirror = (function() {'
982 var no = indexOf(lines, line);
1211 var no = indexOf(lines, line);
983 if (no == -1) return null;
1212 if (no == -1) return null;
984 }
1213 }
985 line.className = className;
1214 if (line.className != className) {
986 changes.push({from: no, to: no + 1});
1215 line.className = className;
1216 changes.push({from: no, to: no + 1});
1217 }
987 return line;
1218 return line;
988 }
1219 }
989
1220
@@ -1001,35 +1232,44 b' var CodeMirror = (function() {'
1001 return {line: n, text: line.text, markerText: marker && marker.text, markerClass: marker && marker.style};
1232 return {line: n, text: line.text, markerText: marker && marker.text, markerClass: marker && marker.style};
1002 }
1233 }
1003
1234
1235 function stringWidth(str) {
1236 measure.innerHTML = "<pre><span>x</span></pre>";
1237 measure.firstChild.firstChild.firstChild.nodeValue = str;
1238 return measure.firstChild.firstChild.offsetWidth || 10;
1239 }
1004 // These are used to go from pixel positions to character
1240 // These are used to go from pixel positions to character
1005 // positions, taking tabs into account.
1241 // positions, taking varying character widths into account.
1006 function charX(line, pos) {
1242 function charX(line, pos) {
1007 var text = lines[line].text, span = measure.firstChild;
1243 if (pos == 0) return 0;
1008 if (text.lastIndexOf("\t", pos) == -1) return pos * charWidth();
1244 measure.innerHTML = "<pre><span>" + lines[line].getHTML(null, null, false, pos) + "</span></pre>";
1009 var old = span.firstChild.nodeValue;
1245 return measure.firstChild.firstChild.offsetWidth;
1010 try {
1011 span.firstChild.nodeValue = text.slice(0, pos);
1012 return span.offsetWidth;
1013 } finally {span.firstChild.nodeValue = old;}
1014 }
1246 }
1015 function charFromX(line, x) {
1247 function charFromX(line, x) {
1016 var text = lines[line].text, cw = charWidth();
1017 if (x <= 0) return 0;
1248 if (x <= 0) return 0;
1018 if (text.indexOf("\t") == -1) return Math.min(text.length, Math.round(x / cw));
1249 var lineObj = lines[line], text = lineObj.text;
1019 var mspan = measure.firstChild, mtext = mspan.firstChild, old = mtext.nodeValue;
1250 function getX(len) {
1020 try {
1251 measure.innerHTML = "<pre><span>" + lineObj.getHTML(null, null, false, len) + "</span></pre>";
1021 mtext.nodeValue = text;
1252 return measure.firstChild.firstChild.offsetWidth;
1022 var from = 0, fromX = 0, to = text.length, toX = mspan.offsetWidth;
1253 }
1023 if (x > toX) return to;
1254 var from = 0, fromX = 0, to = text.length, toX;
1024 for (;;) {
1255 // Guess a suitable upper bound for our search.
1025 if (to - from <= 1) return (toX - x > x - fromX) ? from : to;
1256 var estimated = Math.min(to, Math.ceil(x / stringWidth("x")));
1026 var middle = Math.ceil((from + to) / 2);
1257 for (;;) {
1027 mtext.nodeValue = text.slice(0, middle);
1258 var estX = getX(estimated);
1028 var curX = mspan.offsetWidth;
1259 if (estX <= x && estimated < to) estimated = Math.min(to, Math.ceil(estimated * 1.2));
1029 if (curX > x) {to = middle; toX = curX;}
1260 else {toX = estX; to = estimated; break;}
1030 else {from = middle; fromX = curX;}
1261 }
1031 }
1262 if (x > toX) return to;
1032 } finally {mtext.nodeValue = old;}
1263 // Try to guess a suitable lower bound as well.
1264 estimated = Math.floor(to * 0.8); estX = getX(estimated);
1265 if (estX < x) {from = estimated; fromX = estX;}
1266 // Do a binary search between these bounds.
1267 for (;;) {
1268 if (to - from <= 1) return (toX - x > x - fromX) ? from : to;
1269 var middle = Math.ceil((from + to) / 2), middleX = getX(middle);
1270 if (middleX > x) {to = middle; toX = middleX;}
1271 else {from = middle; fromX = middleX;}
1272 }
1033 }
1273 }
1034
1274
1035 function localCoords(pos, inLineWrap) {
1275 function localCoords(pos, inLineWrap) {
@@ -1043,45 +1283,61 b' var CodeMirror = (function() {'
1043
1283
1044 function lineHeight() {
1284 function lineHeight() {
1045 var nlines = lineDiv.childNodes.length;
1285 var nlines = lineDiv.childNodes.length;
1046 if (nlines) return lineDiv.offsetHeight / nlines;
1286 if (nlines) return (lineDiv.offsetHeight / nlines) || 1;
1047 else return measure.firstChild.offsetHeight || 1;
1287 measure.innerHTML = "<pre>x</pre>";
1288 return measure.firstChild.offsetHeight || 1;
1048 }
1289 }
1049 function charWidth() {return (measure.firstChild.offsetWidth || 320) / 40;}
1050 function paddingTop() {return lineSpace.offsetTop;}
1290 function paddingTop() {return lineSpace.offsetTop;}
1051 function paddingLeft() {return lineSpace.offsetLeft;}
1291 function paddingLeft() {return lineSpace.offsetLeft;}
1052
1292
1053 function posFromMouse(e, liberal) {
1293 function posFromMouse(e, liberal) {
1054 var off = eltOffset(lineSpace),
1294 var offW = eltOffset(scroller, true), x, y;
1055 x = e.pageX() - off.left,
1295 // Fails unpredictably on IE[67] when mouse is dragged around quickly.
1056 y = e.pageY() - off.top;
1296 try { x = e.clientX; y = e.clientY; } catch (e) { return null; }
1057 if (!liberal && e.target() != lineSpace.parentNode && !(e.target() == wrapper && y > (lines.length * lineHeight())))
1297 // This is a mess of a heuristic to try and determine whether a
1058 for (var n = e.target(); n != lineDiv && n != cursor; n = n.parentNode)
1298 // scroll-bar was clicked or not, and to return null if one was
1059 if (!n || n == wrapper) return null;
1299 // (and !liberal).
1060 var line = showingFrom + Math.floor(y / lineHeight());
1300 if (!liberal && (x - offW.left > scroller.clientWidth || y - offW.top > scroller.clientHeight))
1061 return clipPos({line: line, ch: charFromX(clipLine(line), x)});
1301 return null;
1302 var offL = eltOffset(lineSpace, true);
1303 var line = showingFrom + Math.floor((y - offL.top) / lineHeight());
1304 return clipPos({line: line, ch: charFromX(clipLine(line), x - offL.left)});
1062 }
1305 }
1063 function onContextMenu(e) {
1306 function onContextMenu(e) {
1064 var pos = posFromMouse(e);
1307 var pos = posFromMouse(e);
1065 if (!pos || window.opera) return; // Opera is difficult.
1308 if (!pos || window.opera) return; // Opera is difficult.
1066 if (posEq(sel.from, sel.to) || posLess(pos, sel.from) || !posLess(pos, sel.to))
1309 if (posEq(sel.from, sel.to) || posLess(pos, sel.from) || !posLess(pos, sel.to))
1067 setCursor(pos.line, pos.ch);
1310 operation(setCursor)(pos.line, pos.ch);
1068
1311
1069 var oldCSS = input.style.cssText;
1312 var oldCSS = input.style.cssText;
1070 input.style.cssText = "position: fixed; width: 30px; height: 30px; top: " + (e.pageY() - 1) +
1313 inputDiv.style.position = "absolute";
1071 "px; left: " + (e.pageX() - 1) + "px; z-index: 1000; background: white; " +
1314 input.style.cssText = "position: fixed; width: 30px; height: 30px; top: " + (e.clientY - 5) +
1072 "border-width: 0; outline: none; overflow: hidden;";
1315 "px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: white; " +
1316 "border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);";
1317 leaveInputAlone = true;
1073 var val = input.value = getSelection();
1318 var val = input.value = getSelection();
1074 input.focus();
1319 focusInput();
1075 setSelRange(input, 0, val.length);
1320 setSelRange(input, 0, input.value.length);
1076 if (gecko) e.stop();
1321 function rehide() {
1077 leaveInputAlone = true;
1322 var newVal = splitLines(input.value).join("\n");
1078 setTimeout(function() {
1323 if (newVal != val) operation(replaceSelection)(newVal, "end");
1079 if (input.value != val) operation(replaceSelection)(input.value, "end");
1324 inputDiv.style.position = "relative";
1080 input.style.cssText = oldCSS;
1325 input.style.cssText = oldCSS;
1081 leaveInputAlone = false;
1326 leaveInputAlone = false;
1082 prepareInput();
1327 prepareInput();
1083 slowPoll();
1328 slowPoll();
1084 }, 50);
1329 }
1330
1331 if (gecko) {
1332 e_stop(e);
1333 var mouseup = connect(window, "mouseup", function() {
1334 mouseup();
1335 setTimeout(rehide, 20);
1336 }, true);
1337 }
1338 else {
1339 setTimeout(rehide, 50);
1340 }
1085 }
1341 }
1086
1342
1087 // Cursor-blinking
1343 // Cursor-blinking
@@ -1120,19 +1376,18 b' var CodeMirror = (function() {'
1120 }
1376 }
1121 }
1377 }
1122 }
1378 }
1123 for (var i = head.line, e = forward ? Math.min(i + 50, lines.length) : Math.max(0, i - 50); i != e; i+=d) {
1379 for (var i = head.line, e = forward ? Math.min(i + 100, lines.length) : Math.max(-1, i - 100); i != e; i+=d) {
1124 var line = lines[i], first = i == head.line;
1380 var line = lines[i], first = i == head.line;
1125 var found = scan(line, first && forward ? pos + 1 : 0, first && !forward ? pos : line.text.length);
1381 var found = scan(line, first && forward ? pos + 1 : 0, first && !forward ? pos : line.text.length);
1126 if (found) {
1382 if (found) break;
1127 var style = found.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket";
1128 var one = markText({line: head.line, ch: pos}, {line: head.line, ch: pos+1}, style),
1129 two = markText({line: i, ch: found.pos}, {line: i, ch: found.pos + 1}, style);
1130 var clear = operation(function(){one(); two();});
1131 if (autoclear) setTimeout(clear, 800);
1132 else bracketHighlighted = clear;
1133 break;
1134 }
1135 }
1383 }
1384 if (!found) found = {pos: null, match: false};
1385 var style = found.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket";
1386 var one = markText({line: head.line, ch: pos}, {line: head.line, ch: pos+1}, style),
1387 two = found.pos != null && markText({line: i, ch: found.pos}, {line: i, ch: found.pos + 1}, style);
1388 var clear = operation(function(){one.clear(); two && two.clear();});
1389 if (autoclear) setTimeout(clear, 800);
1390 else bracketHighlighted = clear;
1136 }
1391 }
1137
1392
1138 // Finds the line to start with when starting a parse. Tries to
1393 // Finds the line to start with when starting a parse. Tries to
@@ -1148,7 +1403,7 b' var CodeMirror = (function() {'
1148 if (line.stateAfter) return search;
1403 if (line.stateAfter) return search;
1149 var indented = line.indentation();
1404 var indented = line.indentation();
1150 if (minline == null || minindent > indented) {
1405 if (minline == null || minindent > indented) {
1151 minline = search;
1406 minline = search - 1;
1152 minindent = indented;
1407 minindent = indented;
1153 }
1408 }
1154 }
1409 }
@@ -1163,11 +1418,21 b' var CodeMirror = (function() {'
1163 line.highlight(mode, state);
1418 line.highlight(mode, state);
1164 line.stateAfter = copyState(mode, state);
1419 line.stateAfter = copyState(mode, state);
1165 }
1420 }
1166 if (!lines[n].stateAfter) work.push(n);
1421 changes.push({from: start, to: n});
1422 if (n < lines.length && !lines[n].stateAfter) work.push(n);
1167 return state;
1423 return state;
1168 }
1424 }
1425 function highlightLines(start, end) {
1426 var state = getStateBefore(start);
1427 for (var i = start; i < end; ++i) {
1428 var line = lines[i];
1429 line.highlight(mode, state);
1430 line.stateAfter = copyState(mode, state);
1431 }
1432 }
1169 function highlightWorker() {
1433 function highlightWorker() {
1170 var end = +new Date + options.workTime;
1434 var end = +new Date + options.workTime;
1435 var foundWork = work.length;
1171 while (work.length) {
1436 while (work.length) {
1172 if (!lines[showingFrom].stateAfter) var task = showingFrom;
1437 if (!lines[showingFrom].stateAfter) var task = showingFrom;
1173 else var task = work.pop();
1438 else var task = work.pop();
@@ -1176,20 +1441,29 b' var CodeMirror = (function() {'
1176 if (state) state = copyState(mode, state);
1441 if (state) state = copyState(mode, state);
1177 else state = startState(mode);
1442 else state = startState(mode);
1178
1443
1444 var unchanged = 0, compare = mode.compareStates, realChange = false;
1179 for (var i = start, l = lines.length; i < l; ++i) {
1445 for (var i = start, l = lines.length; i < l; ++i) {
1180 var line = lines[i], hadState = line.stateAfter;
1446 var line = lines[i], hadState = line.stateAfter;
1181 if (+new Date > end) {
1447 if (+new Date > end) {
1182 work.push(i);
1448 work.push(i);
1183 startWorker(options.workDelay);
1449 startWorker(options.workDelay);
1184 changes.push({from: task, to: i});
1450 if (realChange) changes.push({from: task, to: i + 1});
1185 return;
1451 return;
1186 }
1452 }
1187 var changed = line.highlight(mode, state);
1453 var changed = line.highlight(mode, state);
1454 if (changed) realChange = true;
1188 line.stateAfter = copyState(mode, state);
1455 line.stateAfter = copyState(mode, state);
1189 if (hadState && !changed && line.text) break;
1456 if (compare) {
1457 if (hadState && compare(hadState, state)) break;
1458 } else {
1459 if (changed !== false || !hadState) unchanged = 0;
1460 else if (++unchanged > 3) break;
1461 }
1190 }
1462 }
1191 changes.push({from: task, to: i});
1463 if (realChange) changes.push({from: task, to: i + 1});
1192 }
1464 }
1465 if (foundWork && options.onHighlightComplete)
1466 options.onHighlightComplete(instance);
1193 }
1467 }
1194 function startWorker(time) {
1468 function startWorker(time) {
1195 if (!work.length) return;
1469 if (!work.length) return;
@@ -1207,24 +1481,29 b' var CodeMirror = (function() {'
1207 var reScroll = false;
1481 var reScroll = false;
1208 if (selectionChanged) reScroll = !scrollCursorIntoView();
1482 if (selectionChanged) reScroll = !scrollCursorIntoView();
1209 if (changes.length) updateDisplay(changes);
1483 if (changes.length) updateDisplay(changes);
1210 else if (selectionChanged) updateCursor();
1484 else {
1485 if (selectionChanged) updateCursor();
1486 if (gutterDirty) updateGutter();
1487 }
1211 if (reScroll) scrollCursorIntoView();
1488 if (reScroll) scrollCursorIntoView();
1212 if (selectionChanged) restartBlink();
1489 if (selectionChanged) {scrollEditorIntoView(); restartBlink();}
1213
1490
1214 // updateInput can be set to a boolean value to force/prevent an
1491 // updateInput can be set to a boolean value to force/prevent an
1215 // update.
1492 // update.
1216 if (!leaveInputAlone && (updateInput === true || (updateInput !== false && selectionChanged)))
1493 if (focused && !leaveInputAlone &&
1494 (updateInput === true || (updateInput !== false && selectionChanged)))
1217 prepareInput();
1495 prepareInput();
1218
1496
1219 if (selectionChanged && options.onCursorActivity)
1220 options.onCursorActivity(instance);
1221 if (textChanged && options.onChange)
1222 options.onChange(instance);
1223 if (selectionChanged && options.matchBrackets)
1497 if (selectionChanged && options.matchBrackets)
1224 setTimeout(operation(function() {
1498 setTimeout(operation(function() {
1225 if (bracketHighlighted) {bracketHighlighted(); bracketHighlighted = null;}
1499 if (bracketHighlighted) {bracketHighlighted(); bracketHighlighted = null;}
1226 matchBrackets(false);
1500 matchBrackets(false);
1227 }), 20);
1501 }), 20);
1502 var tc = textChanged; // textChanged can be reset by cursoractivity callback
1503 if (selectionChanged && options.onCursorActivity)
1504 options.onCursorActivity(instance);
1505 if (tc && options.onChange && instance)
1506 options.onChange(instance, tc);
1228 }
1507 }
1229 var nestedOperation = 0;
1508 var nestedOperation = 0;
1230 function operation(f) {
1509 function operation(f) {
@@ -1259,6 +1538,7 b' var CodeMirror = (function() {'
1259 var newmatch = line.match(query);
1538 var newmatch = line.match(query);
1260 if (newmatch) match = newmatch;
1539 if (newmatch) match = newmatch;
1261 else break;
1540 else break;
1541 start++;
1262 }
1542 }
1263 }
1543 }
1264 else {
1544 else {
@@ -1338,9 +1618,21 b' var CodeMirror = (function() {'
1338 },
1618 },
1339
1619
1340 from: function() {if (this.atOccurrence) return copyPos(this.pos.from);},
1620 from: function() {if (this.atOccurrence) return copyPos(this.pos.from);},
1341 to: function() {if (this.atOccurrence) return copyPos(this.pos.to);}
1621 to: function() {if (this.atOccurrence) return copyPos(this.pos.to);},
1622
1623 replace: function(newText) {
1624 var self = this;
1625 if (this.atOccurrence)
1626 operation(function() {
1627 self.pos.to = replaceRange(newText, self.pos.from, self.pos.to);
1628 })();
1629 }
1342 };
1630 };
1343
1631
1632 for (var ext in extensions)
1633 if (extensions.propertyIsEnumerable(ext) &&
1634 !instance.propertyIsEnumerable(ext))
1635 instance[ext] = extensions[ext];
1344 return instance;
1636 return instance;
1345 } // (end of function CodeMirror)
1637 } // (end of function CodeMirror)
1346
1638
@@ -1348,6 +1640,7 b' var CodeMirror = (function() {'
1348 CodeMirror.defaults = {
1640 CodeMirror.defaults = {
1349 value: "",
1641 value: "",
1350 mode: null,
1642 mode: null,
1643 theme: "default",
1351 indentUnit: 2,
1644 indentUnit: 2,
1352 indentWithTabs: false,
1645 indentWithTabs: false,
1353 tabMode: "classic",
1646 tabMode: "classic",
@@ -1356,17 +1649,21 b' var CodeMirror = (function() {'
1356 onKeyEvent: null,
1649 onKeyEvent: null,
1357 lineNumbers: false,
1650 lineNumbers: false,
1358 gutter: false,
1651 gutter: false,
1652 fixedGutter: false,
1359 firstLineNumber: 1,
1653 firstLineNumber: 1,
1360 readOnly: false,
1654 readOnly: false,
1655 smartHome: true,
1361 onChange: null,
1656 onChange: null,
1362 onCursorActivity: null,
1657 onCursorActivity: null,
1363 onGutterClick: null,
1658 onGutterClick: null,
1659 onHighlightComplete: null,
1364 onFocus: null, onBlur: null, onScroll: null,
1660 onFocus: null, onBlur: null, onScroll: null,
1365 matchBrackets: false,
1661 matchBrackets: false,
1366 workTime: 100,
1662 workTime: 100,
1367 workDelay: 200,
1663 workDelay: 200,
1368 undoDepth: 40,
1664 undoDepth: 40,
1369 tabindex: null
1665 tabindex: null,
1666 document: window.document
1370 };
1667 };
1371
1668
1372 // Known modes, by name and by MIME
1669 // Known modes, by name and by MIME
@@ -1383,15 +1680,15 b' var CodeMirror = (function() {'
1383 spec = mimeModes[spec];
1680 spec = mimeModes[spec];
1384 if (typeof spec == "string")
1681 if (typeof spec == "string")
1385 var mname = spec, config = {};
1682 var mname = spec, config = {};
1386 else
1683 else if (spec != null)
1387 var mname = spec.name, config = spec;
1684 var mname = spec.name, config = spec;
1388 var mfactory = modes[mname];
1685 var mfactory = modes[mname];
1389 if (!mfactory) {
1686 if (!mfactory) {
1390 if (window.console) console.warn("No mode " + mname + " found, falling back to plain text.");
1687 if (window.console) console.warn("No mode " + mname + " found, falling back to plain text.");
1391 return CodeMirror.getMode(options, "text/plain");
1688 return CodeMirror.getMode(options, "text/plain");
1392 }
1689 }
1393 return mfactory(options, config);
1690 return mfactory(options, config || {});
1394 }
1691 };
1395 CodeMirror.listModes = function() {
1692 CodeMirror.listModes = function() {
1396 var list = [];
1693 var list = [];
1397 for (var m in modes)
1694 for (var m in modes)
@@ -1401,10 +1698,15 b' var CodeMirror = (function() {'
1401 CodeMirror.listMIMEs = function() {
1698 CodeMirror.listMIMEs = function() {
1402 var list = [];
1699 var list = [];
1403 for (var m in mimeModes)
1700 for (var m in mimeModes)
1404 if (mimeModes.propertyIsEnumerable(m)) list.push(m);
1701 if (mimeModes.propertyIsEnumerable(m)) list.push({mime: m, mode: mimeModes[m]});
1405 return list;
1702 return list;
1406 };
1703 };
1407
1704
1705 var extensions = {};
1706 CodeMirror.defineExtension = function(name, func) {
1707 extensions[name] = func;
1708 };
1709
1408 CodeMirror.fromTextArea = function(textarea, options) {
1710 CodeMirror.fromTextArea = function(textarea, options) {
1409 if (!options) options = {};
1711 if (!options) options = {};
1410 options.value = textarea.value;
1712 options.value = textarea.value;
@@ -1484,7 +1786,7 b' var CodeMirror = (function() {'
1484 if (ok) {++this.pos; return ch;}
1786 if (ok) {++this.pos; return ch;}
1485 },
1787 },
1486 eatWhile: function(match) {
1788 eatWhile: function(match) {
1487 var start = this.start;
1789 var start = this.pos;
1488 while (this.eat(match)){}
1790 while (this.eat(match)){}
1489 return this.pos > start;
1791 return this.pos > start;
1490 },
1792 },
@@ -1517,6 +1819,7 b' var CodeMirror = (function() {'
1517 },
1819 },
1518 current: function(){return this.string.slice(this.start, this.pos);}
1820 current: function(){return this.string.slice(this.start, this.pos);}
1519 };
1821 };
1822 CodeMirror.StringStream = StringStream;
1520
1823
1521 // Line objects. These hold state related to a line, including
1824 // Line objects. These hold state related to a line, including
1522 // highlighting info (the styles array).
1825 // highlighting info (the styles array).
@@ -1526,10 +1829,23 b' var CodeMirror = (function() {'
1526 this.text = text;
1829 this.text = text;
1527 this.marked = this.gutterMarker = this.className = null;
1830 this.marked = this.gutterMarker = this.className = null;
1528 }
1831 }
1832 Line.inheritMarks = function(text, orig) {
1833 var ln = new Line(text), mk = orig.marked;
1834 if (mk) {
1835 for (var i = 0; i < mk.length; ++i) {
1836 if (mk[i].to == null) {
1837 var newmk = ln.marked || (ln.marked = []), mark = mk[i];
1838 newmk.push({from: null, to: null, style: mark.style, set: mark.set});
1839 mark.set.push(ln);
1840 }
1841 }
1842 }
1843 return ln;
1844 }
1529 Line.prototype = {
1845 Line.prototype = {
1530 // Replace a piece of a line, keeping the styles around it intact.
1846 // Replace a piece of a line, keeping the styles around it intact.
1531 replace: function(from, to, text) {
1847 replace: function(from, to_, text) {
1532 var st = [], mk = this.marked;
1848 var st = [], mk = this.marked, to = to_ == null ? this.text.length : to_;
1533 copyStyles(0, from, this.styles, st);
1849 copyStyles(0, from, this.styles, st);
1534 if (text) st.push(text, null);
1850 if (text) st.push(text, null);
1535 copyStyles(to, this.text.length, this.styles, st);
1851 copyStyles(to, this.text.length, this.styles, st);
@@ -1538,39 +1854,86 b' var CodeMirror = (function() {'
1538 this.stateAfter = null;
1854 this.stateAfter = null;
1539 if (mk) {
1855 if (mk) {
1540 var diff = text.length - (to - from), end = this.text.length;
1856 var diff = text.length - (to - from), end = this.text.length;
1541 function fix(n) {return n <= Math.min(to, to + diff) ? n : n + diff;}
1857 var changeStart = Math.min(from, from + diff);
1542 for (var i = 0; i < mk.length; ++i) {
1858 for (var i = 0; i < mk.length; ++i) {
1543 var mark = mk[i], del = false;
1859 var mark = mk[i], del = false;
1544 if (mark.from >= end) del = true;
1860 if (mark.from != null && mark.from >= end) del = true;
1545 else {mark.from = fix(mark.from); if (mark.to != null) mark.to = fix(mark.to);}
1861 else {
1546 if (del || mark.from >= mark.to) {mk.splice(i, 1); i--;}
1862 if (mark.from != null && mark.from >= from) {
1863 mark.from += diff;
1864 if (mark.from <= 0) mark.from = from == null ? null : 0;
1865 }
1866 else if (to_ == null) mark.to = null;
1867 if (mark.to != null && mark.to > from) {
1868 mark.to += diff;
1869 if (mark.to < 0) del = true;
1870 }
1871 }
1872 if (del || (mark.from != null && mark.to != null && mark.from >= mark.to)) mk.splice(i--, 1);
1547 }
1873 }
1548 }
1874 }
1549 },
1875 },
1550 // Split a line in two, again keeping styles intact.
1876 // Split a part off a line, keeping styles and markers intact.
1551 split: function(pos, textBefore) {
1877 split: function(pos, textBefore) {
1552 var st = [textBefore, null];
1878 var st = [textBefore, null], mk = this.marked;
1553 copyStyles(pos, this.text.length, this.styles, st);
1879 copyStyles(pos, this.text.length, this.styles, st);
1554 return new Line(textBefore + this.text.slice(pos), st);
1880 var taken = new Line(textBefore + this.text.slice(pos), st);
1881 if (mk) {
1882 for (var i = 0; i < mk.length; ++i) {
1883 var mark = mk[i];
1884 if (mark.to > pos || mark.to == null) {
1885 if (!taken.marked) taken.marked = [];
1886 taken.marked.push({
1887 from: mark.from < pos || mark.from == null ? null : mark.from - pos + textBefore.length,
1888 to: mark.to == null ? null : mark.to - pos + textBefore.length,
1889 style: mark.style, set: mark.set
1890 });
1891 mark.set.push(taken);
1892 }
1893 }
1894 }
1895 return taken;
1555 },
1896 },
1556 addMark: function(from, to, style) {
1897 append: function(line) {
1557 var mk = this.marked, mark = {from: from, to: to, style: style};
1898 if (!line.text.length) return;
1899 var mylen = this.text.length, mk = line.marked;
1900 this.text += line.text;
1901 copyStyles(0, line.text.length, line.styles, this.styles);
1902 if (mk && mk.length) {
1903 var mymk = this.marked || (this.marked = []);
1904 for (var i = 0; i < mymk.length; ++i)
1905 if (mymk[i].to == null) mymk[i].to = mylen;
1906 outer: for (var i = 0; i < mk.length; ++i) {
1907 var mark = mk[i];
1908 if (!mark.from) {
1909 for (var j = 0; j < mymk.length; ++j) {
1910 var mymark = mymk[j];
1911 if (mymark.to == mylen && mymark.set == mark.set) {
1912 mymark.to = mark.to == null ? null : mark.to + mylen;
1913 continue outer;
1914 }
1915 }
1916 }
1917 mymk.push(mark);
1918 mark.set.push(this);
1919 mark.from += mylen;
1920 if (mark.to != null) mark.to += mylen;
1921 }
1922 }
1923 },
1924 addMark: function(from, to, style, set) {
1925 set.push(this);
1558 if (this.marked == null) this.marked = [];
1926 if (this.marked == null) this.marked = [];
1559 this.marked.push(mark);
1927 this.marked.push({from: from, to: to, style: style, set: set});
1560 this.marked.sort(function(a, b){return a.from - b.from;});
1928 this.marked.sort(function(a, b){return (a.from || 0) - (b.from || 0);});
1561 return mark;
1562 },
1563 removeMark: function(mark) {
1564 var mk = this.marked;
1565 if (!mk) return;
1566 for (var i = 0; i < mk.length; ++i)
1567 if (mk[i] == mark) {mk.splice(i, 1); break;}
1568 },
1929 },
1569 // Run the given mode's parser over a line, update the styles
1930 // Run the given mode's parser over a line, update the styles
1570 // array, which contains alternating fragments of text and CSS
1931 // array, which contains alternating fragments of text and CSS
1571 // classes.
1932 // classes.
1572 highlight: function(mode, state) {
1933 highlight: function(mode, state) {
1573 var stream = new StringStream(this.text), st = this.styles, pos = 0, changed = false;
1934 var stream = new StringStream(this.text), st = this.styles, pos = 0;
1935 var changed = false, curWord = st[0], prevWord;
1936 if (this.text == "" && mode.blankLine) mode.blankLine(state);
1574 while (!stream.eol()) {
1937 while (!stream.eol()) {
1575 var style = mode.token(stream, state);
1938 var style = mode.token(stream, state);
1576 var substr = this.text.slice(stream.start, stream.pos);
1939 var substr = this.text.slice(stream.start, stream.pos);
@@ -1578,8 +1941,9 b' var CodeMirror = (function() {'
1578 if (pos && st[pos-1] == style)
1941 if (pos && st[pos-1] == style)
1579 st[pos-2] += substr;
1942 st[pos-2] += substr;
1580 else if (substr) {
1943 else if (substr) {
1581 if (!changed && st[pos] != substr || st[pos+1] != style) changed = true;
1944 if (!changed && (st[pos+1] != style || (pos && st[pos-2] != prevWord))) changed = true;
1582 st[pos++] = substr; st[pos++] = style;
1945 st[pos++] = substr; st[pos++] = style;
1946 prevWord = curWord; curWord = st[pos];
1583 }
1947 }
1584 // Give up when line is ridiculously long
1948 // Give up when line is ridiculously long
1585 if (stream.pos > 5000) {
1949 if (stream.pos > 5000) {
@@ -1588,7 +1952,11 b' var CodeMirror = (function() {'
1588 }
1952 }
1589 }
1953 }
1590 if (st.length != pos) {st.length = pos; changed = true;}
1954 if (st.length != pos) {st.length = pos; changed = true;}
1591 return changed;
1955 if (pos && st[pos-2] != prevWord) changed = true;
1956 // Short lines with simple highlights return null, and are
1957 // counted as changed by the driver because they are likely to
1958 // highlight the same way in various contexts.
1959 return changed || (st.length < 5 && this.text.length < 10 ? null : false);
1592 },
1960 },
1593 // Fetch the parser token for a given character. Useful for hacks
1961 // Fetch the parser token for a given character. Useful for hacks
1594 // that want to inspect the mode state (say, for completion).
1962 // that want to inspect the mode state (say, for completion).
@@ -1607,7 +1975,7 b' var CodeMirror = (function() {'
1607 indentation: function() {return countColumn(this.text);},
1975 indentation: function() {return countColumn(this.text);},
1608 // Produces an HTML fragment for the line, taking selection,
1976 // Produces an HTML fragment for the line, taking selection,
1609 // marking, and highlighting into account.
1977 // marking, and highlighting into account.
1610 getHTML: function(sfrom, sto, includePre) {
1978 getHTML: function(sfrom, sto, includePre, endAt) {
1611 var html = [];
1979 var html = [];
1612 if (includePre)
1980 if (includePre)
1613 html.push(this.className ? '<pre class="' + this.className + '">': "<pre>");
1981 html.push(this.className ? '<pre class="' + this.className + '">': "<pre>");
@@ -1618,11 +1986,18 b' var CodeMirror = (function() {'
1618 }
1986 }
1619 var st = this.styles, allText = this.text, marked = this.marked;
1987 var st = this.styles, allText = this.text, marked = this.marked;
1620 if (sfrom == sto) sfrom = null;
1988 if (sfrom == sto) sfrom = null;
1989 var len = allText.length;
1990 if (endAt != null) len = Math.min(endAt, len);
1621
1991
1622 if (!allText)
1992 if (!allText && endAt == null)
1623 span(" ", sfrom != null && sto == null ? "CodeMirror-selected" : null);
1993 span(" ", sfrom != null && sto == null ? "CodeMirror-selected" : null);
1624 else if (!marked && sfrom == null)
1994 else if (!marked && sfrom == null)
1625 for (var i = 0, e = st.length; i < e; i+=2) span(st[i], st[i+1]);
1995 for (var i = 0, ch = 0; ch < len; i+=2) {
1996 var str = st[i], style = st[i+1], l = str.length;
1997 if (ch + l > len) str = str.slice(0, len - ch);
1998 ch += l;
1999 span(str, style && "cm-" + style);
2000 }
1626 else {
2001 else {
1627 var pos = 0, i = 0, text = "", style, sg = 0;
2002 var pos = 0, i = 0, text = "", style, sg = 0;
1628 var markpos = -1, mark = null;
2003 var markpos = -1, mark = null;
@@ -1632,9 +2007,9 b' var CodeMirror = (function() {'
1632 mark = (markpos < marked.length) ? marked[markpos] : null;
2007 mark = (markpos < marked.length) ? marked[markpos] : null;
1633 }
2008 }
1634 }
2009 }
1635 nextMark();
2010 nextMark();
1636 while (pos < allText.length) {
2011 while (pos < len) {
1637 var upto = allText.length;
2012 var upto = len;
1638 var extraStyle = "";
2013 var extraStyle = "";
1639 if (sfrom != null) {
2014 if (sfrom != null) {
1640 if (sfrom > pos) upto = sfrom;
2015 if (sfrom > pos) upto = sfrom;
@@ -1653,12 +2028,12 b' var CodeMirror = (function() {'
1653 }
2028 }
1654 for (;;) {
2029 for (;;) {
1655 var end = pos + text.length;
2030 var end = pos + text.length;
1656 var apliedStyle = style;
2031 var appliedStyle = style;
1657 if (extraStyle) apliedStyle = style ? style + extraStyle : extraStyle;
2032 if (extraStyle) appliedStyle = style ? style + extraStyle : extraStyle;
1658 span(end > upto ? text.slice(0, upto - pos) : text, apliedStyle);
2033 span(end > upto ? text.slice(0, upto - pos) : text, appliedStyle);
1659 if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;}
2034 if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;}
1660 pos = end;
2035 pos = end;
1661 text = st[i++]; style = st[i++];
2036 text = st[i++]; style = "cm-" + st[i++];
1662 }
2037 }
1663 }
2038 }
1664 if (sfrom != null && sto == null) span(" ", "CodeMirror-selected");
2039 if (sfrom != null && sto == null) span(" ", "CodeMirror-selected");
@@ -1716,42 +2091,34 b' var CodeMirror = (function() {'
1716 }
2091 }
1717 };
2092 };
1718
2093
1719 // Event stopping compatibility wrapper.
2094 function stopMethod() {e_stop(this);}
1720 function stopEvent() {
1721 if (this.preventDefault) {this.preventDefault(); this.stopPropagation();}
1722 else {this.returnValue = false; this.cancelBubble = true;}
1723 }
1724 // Ensure an event has a stop method.
2095 // Ensure an event has a stop method.
1725 function addStop(event) {
2096 function addStop(event) {
1726 if (!event.stop) event.stop = stopEvent;
2097 if (!event.stop) event.stop = stopMethod;
1727 return event;
2098 return event;
1728 }
2099 }
1729
2100
1730 // Event wrapper, exposing the few operations we need.
2101 function e_preventDefault(e) {
1731 function Event(orig) {this.e = orig;}
2102 if (e.preventDefault) e.preventDefault();
1732 Event.prototype = {
2103 else e.returnValue = false;
1733 stop: function() {stopEvent.call(this.e);},
2104 }
1734 target: function() {return this.e.target || this.e.srcElement;},
2105 function e_stopPropagation(e) {
1735 button: function() {
2106 if (e.stopPropagation) e.stopPropagation();
1736 if (this.e.which) return this.e.which;
2107 else e.cancelBubble = true;
1737 else if (this.e.button & 1) return 1;
2108 }
1738 else if (this.e.button & 2) return 3;
2109 function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);}
1739 else if (this.e.button & 4) return 2;
2110 function e_target(e) {return e.target || e.srcElement;}
1740 },
2111 function e_button(e) {
1741 pageX: function() {
2112 if (e.which) return e.which;
1742 if (this.e.pageX != null) return this.e.pageX;
2113 else if (e.button & 1) return 1;
1743 else return this.e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
2114 else if (e.button & 2) return 3;
1744 },
2115 else if (e.button & 4) return 2;
1745 pageY: function() {
2116 }
1746 if (this.e.pageY != null) return this.e.pageY;
1747 else return this.e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
1748 }
1749 };
1750
2117
1751 // Event handler registration. If disconnect is true, it'll return a
2118 // Event handler registration. If disconnect is true, it'll return a
1752 // function that unregisters the handler.
2119 // function that unregisters the handler.
1753 function connect(node, type, handler, disconnect) {
2120 function connect(node, type, handler, disconnect) {
1754 function wrapHandler(event) {handler(new Event(event || window.event));}
2121 function wrapHandler(event) {handler(event || window.event);}
1755 if (typeof node.addEventListener == "function") {
2122 if (typeof node.addEventListener == "function") {
1756 node.addEventListener(type, wrapHandler, false);
2123 node.addEventListener(type, wrapHandler, false);
1757 if (disconnect) return function() {node.removeEventListener(type, wrapHandler, false);};
2124 if (disconnect) return function() {node.removeEventListener(type, wrapHandler, false);};
@@ -1772,7 +2139,18 b' var CodeMirror = (function() {'
1772 pre.innerHTML = " "; return !pre.innerHTML;
2139 pre.innerHTML = " "; return !pre.innerHTML;
1773 })();
2140 })();
1774
2141
2142 // Detect drag-and-drop
2143 var dragAndDrop = (function() {
2144 // IE8 has ondragstart and ondrop properties, but doesn't seem to
2145 // actually support ondragstart the way it's supposed to work.
2146 if (/MSIE [1-8]\b/.test(navigator.userAgent)) return false;
2147 var div = document.createElement('div');
2148 return "ondragstart" in div && "ondrop" in div;
2149 })();
2150
1775 var gecko = /gecko\/\d{7}/i.test(navigator.userAgent);
2151 var gecko = /gecko\/\d{7}/i.test(navigator.userAgent);
2152 var ie = /MSIE \d/.test(navigator.userAgent);
2153 var safari = /Apple Computer/.test(navigator.vendor);
1776
2154
1777 var lineSep = "\n";
2155 var lineSep = "\n";
1778 // Feature-detect whether newlines in textareas are converted to \r\n
2156 // Feature-detect whether newlines in textareas are converted to \r\n
@@ -1802,11 +2180,23 b' var CodeMirror = (function() {'
1802 return n;
2180 return n;
1803 }
2181 }
1804
2182
2183 function computedStyle(elt) {
2184 if (elt.currentStyle) return elt.currentStyle;
2185 return window.getComputedStyle(elt, null);
2186 }
1805 // Find the position of an element by following the offsetParent chain.
2187 // Find the position of an element by following the offsetParent chain.
1806 function eltOffset(node) {
2188 // If screen==true, it returns screen (rather than page) coordinates.
1807 var x = 0, y = 0, n2 = node;
2189 function eltOffset(node, screen) {
1808 for (var n = node; n; n = n.offsetParent) {x += n.offsetLeft; y += n.offsetTop;}
2190 var doc = node.ownerDocument.body;
1809 for (var n = node; n != document.body; n = n.parentNode) {x -= n.scrollLeft; y -= n.scrollTop;}
2191 var x = 0, y = 0, skipDoc = false;
2192 for (var n = node; n; n = n.offsetParent) {
2193 x += n.offsetLeft; y += n.offsetTop;
2194 if (screen && computedStyle(n).position == "fixed")
2195 skipDoc = true;
2196 }
2197 var e = screen && !skipDoc ? null : doc;
2198 for (var n = node.parentNode; n != e; n = n.parentNode)
2199 if (n.scrollLeft != null) { x -= n.scrollLeft; y -= n.scrollTop;}
1810 return {left: x, top: y};
2200 return {left: x, top: y};
1811 }
2201 }
1812 // Get a node's text content.
2202 // Get a node's text content.
@@ -1819,9 +2209,18 b' var CodeMirror = (function() {'
1819 function posLess(a, b) {return a.line < b.line || (a.line == b.line && a.ch < b.ch);}
2209 function posLess(a, b) {return a.line < b.line || (a.line == b.line && a.ch < b.ch);}
1820 function copyPos(x) {return {line: x.line, ch: x.ch};}
2210 function copyPos(x) {return {line: x.line, ch: x.ch};}
1821
2211
2212 var escapeElement = document.createElement("pre");
1822 function htmlEscape(str) {
2213 function htmlEscape(str) {
1823 return str.replace(/[<&]/g, function(str) {return str == "&" ? "&amp;" : "&lt;";});
2214 if (badTextContent) {
2215 escapeElement.innerHTML = "";
2216 escapeElement.appendChild(document.createTextNode(str));
2217 } else {
2218 escapeElement.textContent = str;
2219 }
2220 return escapeElement.innerHTML;
1824 }
2221 }
2222 var badTextContent = htmlEscape("\t") != "\t";
2223 CodeMirror.htmlEscape = htmlEscape;
1825
2224
1826 // Used to position the cursor after an undo/redo by finding the
2225 // Used to position the cursor after an undo/redo by finding the
1827 // last edited character.
2226 // last edited character.
@@ -1842,8 +2241,9 b' var CodeMirror = (function() {'
1842
2241
1843 // See if "".split is the broken IE version, if so, provide an
2242 // See if "".split is the broken IE version, if so, provide an
1844 // alternative way to split lines.
2243 // alternative way to split lines.
2244 var splitLines, selRange, setSelRange;
1845 if ("\n\nb".split(/\n/).length != 3)
2245 if ("\n\nb".split(/\n/).length != 3)
1846 var splitLines = function(string) {
2246 splitLines = function(string) {
1847 var pos = 0, nl, result = [];
2247 var pos = 0, nl, result = [];
1848 while ((nl = string.indexOf("\n", pos)) > -1) {
2248 while ((nl = string.indexOf("\n", pos)) > -1) {
1849 result.push(string.slice(pos, string.charAt(nl-1) == "\r" ? nl - 1 : nl));
2249 result.push(string.slice(pos, string.charAt(nl-1) == "\r" ? nl - 1 : nl));
@@ -1853,23 +2253,40 b' var CodeMirror = (function() {'
1853 return result;
2253 return result;
1854 };
2254 };
1855 else
2255 else
1856 var splitLines = function(string){return string.split(/\r?\n/);};
2256 splitLines = function(string){return string.split(/\r?\n/);};
2257 CodeMirror.splitLines = splitLines;
1857
2258
1858 // Sane model of finding and setting the selection in a textarea
2259 // Sane model of finding and setting the selection in a textarea
1859 if (window.getSelection) {
2260 if (window.getSelection) {
1860 var selRange = function(te) {
2261 selRange = function(te) {
1861 try {return {start: te.selectionStart, end: te.selectionEnd};}
2262 try {return {start: te.selectionStart, end: te.selectionEnd};}
1862 catch(e) {return null;}
2263 catch(e) {return null;}
1863 };
2264 };
1864 var setSelRange = function(te, start, end) {
2265 if (safari)
1865 try {te.setSelectionRange(start, end);}
2266 // On Safari, selection set with setSelectionRange are in a sort
1866 catch(e) {} // Fails on Firefox when textarea isn't part of the document
2267 // of limbo wrt their anchor. If you press shift-left in them,
1867 };
2268 // the anchor is put at the end, and the selection expanded to
2269 // the left. If you press shift-right, the anchor ends up at the
2270 // front. This is not what CodeMirror wants, so it does a
2271 // spurious modify() call to get out of limbo.
2272 setSelRange = function(te, start, end) {
2273 if (start == end)
2274 te.setSelectionRange(start, end);
2275 else {
2276 te.setSelectionRange(start, end - 1);
2277 window.getSelection().modify("extend", "forward", "character");
2278 }
2279 };
2280 else
2281 setSelRange = function(te, start, end) {
2282 try {te.setSelectionRange(start, end);}
2283 catch(e) {} // Fails on Firefox when textarea isn't part of the document
2284 };
1868 }
2285 }
1869 // IE model. Don't ask.
2286 // IE model. Don't ask.
1870 else {
2287 else {
1871 var selRange = function(te) {
2288 selRange = function(te) {
1872 try {var range = document.selection.createRange();}
2289 try {var range = te.ownerDocument.selection.createRange();}
1873 catch(e) {return null;}
2290 catch(e) {return null;}
1874 if (!range || range.parentElement() != te) return null;
2291 if (!range || range.parentElement() != te) return null;
1875 var val = te.value, len = val.length, localRange = te.createTextRange();
2292 var val = te.value, len = val.length, localRange = te.createTextRange();
@@ -1890,7 +2307,7 b' var CodeMirror = (function() {'
1890 for (var i = val.indexOf("\r"); i > -1 && i < end; i = val.indexOf("\r", i+1), end++) {}
2307 for (var i = val.indexOf("\r"); i > -1 && i < end; i = val.indexOf("\r", i+1), end++) {}
1891 return {start: start, end: end};
2308 return {start: start, end: end};
1892 };
2309 };
1893 var setSelRange = function(te, start, end) {
2310 setSelRange = function(te, start, end) {
1894 var range = te.createTextRange();
2311 var range = te.createTextRange();
1895 range.collapse(true);
2312 range.collapse(true);
1896 var endrange = range.duplicate();
2313 var endrange = range.duplicate();
General Comments 0
You need to be logged in to leave comments. Login now