##// END OF EJS Templates
Do not use version # in codemirror directory name....
Fernando Perez -
Show More
1 NO CONTENT: new file 100644
1 NO CONTENT: file renamed from IPython/frontend/html/notebook/static/codemirror-2.12/LICENSE to IPython/frontend/html/notebook/static/codemirror/LICENSE
1 NO CONTENT: file renamed from IPython/frontend/html/notebook/static/codemirror-2.12/README.md to IPython/frontend/html/notebook/static/codemirror/README.md
1 NO CONTENT: file renamed from IPython/frontend/html/notebook/static/codemirror-2.12/lib/codemirror.css to IPython/frontend/html/notebook/static/codemirror/lib/codemirror.css
@@ -1,2197 +1,2197
1 1 // All functions that need access to the editor's state live inside
2 2 // the CodeMirror function. Below that, at the bottom of the file,
3 3 // some utilities are defined.
4 4
5 5 // CodeMirror is the only global var we claim
6 6 var CodeMirror = (function() {
7 7 // This is the function that produces an editor instance. It's
8 8 // closure is used to store the editor state.
9 9 function CodeMirror(place, givenOptions) {
10 10 // Determine effective options based on given values and defaults.
11 11 var options = {}, defaults = CodeMirror.defaults;
12 12 for (var opt in defaults)
13 13 if (defaults.hasOwnProperty(opt))
14 14 options[opt] = (givenOptions && givenOptions.hasOwnProperty(opt) ? givenOptions : defaults)[opt];
15 15
16 16 var targetDocument = options["document"];
17 17 // The element in which the editor lives.
18 18 var wrapper = targetDocument.createElement("div");
19 19 wrapper.className = "CodeMirror";
20 20 // This mess creates the base DOM structure for the editor.
21 21 wrapper.innerHTML =
22 22 '<div style="overflow: hidden; position: relative; width: 1px; height: 0px;">' + // Wraps and hides input textarea
23 23 '<textarea style="position: absolute; width: 2px;" wrap="off" ' +
24 24 'autocorrect="off" autocapitalize="off"></textarea></div>' +
25 25 '<div class="CodeMirror-scroll cm-s-' + options.theme + '">' +
26 26 '<div style="position: relative">' + // Set to the height of the text, causes scrolling
27 27 '<div style="position: absolute; height: 0; width: 0; overflow: hidden;"></div>' +
28 28 '<div style="position: relative">' + // Moved around its parent to cover visible view
29 29 '<div class="CodeMirror-gutter"><div class="CodeMirror-gutter-text"></div></div>' +
30 30 // Provides positioning relative to (visible) text origin
31 31 '<div class="CodeMirror-lines"><div style="position: relative" draggable="true">' +
32 32 '<pre class="CodeMirror-cursor">&#160;</pre>' + // Absolutely positioned blinky cursor
33 33 '<div></div>' + // This DIV contains the actual code
34 34 '</div></div></div></div></div>';
35 35 if (place.appendChild) place.appendChild(wrapper); else place(wrapper);
36 36 // I've never seen more elegant code in my life.
37 37 var inputDiv = wrapper.firstChild, input = inputDiv.firstChild,
38 38 scroller = wrapper.lastChild, code = scroller.firstChild,
39 39 measure = code.firstChild, mover = measure.nextSibling,
40 40 gutter = mover.firstChild, gutterText = gutter.firstChild,
41 41 lineSpace = gutter.nextSibling.firstChild,
42 42 cursor = lineSpace.firstChild, lineDiv = cursor.nextSibling;
43 43 if (options.tabindex != null) input.tabindex = options.tabindex;
44 44 if (!options.gutter && !options.lineNumbers) gutter.style.display = "none";
45 45
46 46 // Delayed object wrap timeouts, making sure only one is active. blinker holds an interval.
47 47 var poll = new Delayed(), highlight = new Delayed(), blinker;
48 48
49 49 // mode holds a mode API object. lines an array of Line objects
50 50 // (see Line constructor), work an array of lines that should be
51 51 // parsed, and history the undo history (instance of History
52 52 // constructor).
53 53 var mode, lines = [new Line("")], work, history = new History(), focused;
54 54 loadMode();
55 55 // The selection. These are always maintained to point at valid
56 56 // positions. Inverted is used to remember that the user is
57 57 // selecting bottom-to-top.
58 58 var sel = {from: {line: 0, ch: 0}, to: {line: 0, ch: 0}, inverted: false};
59 59 // Selection-related flags. shiftSelecting obviously tracks
60 60 // whether the user is holding shift. reducedSelection is a hack
61 61 // to get around the fact that we can't create inverted
62 62 // selections. See below.
63 63 var shiftSelecting, reducedSelection, lastClick, lastDoubleClick;
64 64 // Variables used by startOperation/endOperation to track what
65 65 // happened during the operation.
66 66 var updateInput, changes, textChanged, selectionChanged, leaveInputAlone, gutterDirty;
67 67 // Current visible range (may be bigger than the view window).
68 68 var showingFrom = 0, showingTo = 0, lastHeight = 0, curKeyId = null;
69 69 // editing will hold an object describing the things we put in the
70 70 // textarea, to help figure out whether something changed.
71 71 // bracketHighlighted is used to remember that a backet has been
72 72 // marked.
73 73 var editing, bracketHighlighted;
74 74 // Tracks the maximum line length so that the horizontal scrollbar
75 75 // can be kept static when scrolling.
76 76 var maxLine = "", maxWidth;
77 77
78 78 // Initialize the content.
79 79 operation(function(){setValue(options.value || ""); updateInput = false;})();
80 80
81 81 // Register our event handlers.
82 82 connect(scroller, "mousedown", operation(onMouseDown));
83 83 connect(lineSpace, "dragstart", onDragStart);
84 84 // Gecko browsers fire contextmenu *after* opening the menu, at
85 85 // which point we can't mess with it anymore. Context menu is
86 86 // handled in onMouseDown for Gecko.
87 87 if (!gecko) connect(scroller, "contextmenu", onContextMenu);
88 88 connect(scroller, "scroll", function() {
89 89 updateDisplay([]);
90 90 if (options.fixedGutter) gutter.style.left = scroller.scrollLeft + "px";
91 91 if (options.onScroll) options.onScroll(instance);
92 92 });
93 93 connect(window, "resize", function() {updateDisplay(true);});
94 94 connect(input, "keyup", operation(onKeyUp));
95 95 connect(input, "keydown", operation(onKeyDown));
96 96 connect(input, "keypress", operation(onKeyPress));
97 97 connect(input, "focus", onFocus);
98 98 connect(input, "blur", onBlur);
99 99
100 100 connect(scroller, "dragenter", e_stop);
101 101 connect(scroller, "dragover", e_stop);
102 102 connect(scroller, "drop", operation(onDrop));
103 103 connect(scroller, "paste", function(){focusInput(); fastPoll();});
104 104 connect(input, "paste", function(){fastPoll();});
105 105 connect(input, "cut", function(){fastPoll();});
106
107 // IE throws unspecified error in certain cases, when
106
107 // IE throws unspecified error in certain cases, when
108 108 // trying to access activeElement before onload
109 109 var hasFocus; try { hasFocus = (targetDocument.activeElement == input); } catch(e) { }
110 110 if (hasFocus) setTimeout(onFocus, 20);
111 111 else onBlur();
112 112
113 113 function isLine(l) {return l >= 0 && l < lines.length;}
114 114 // The instance object that we'll return. Mostly calls out to
115 115 // local functions in the CodeMirror function. Some do some extra
116 116 // range checking and/or clipping. operation is used to wrap the
117 117 // call so that changes it makes are tracked, and the display is
118 118 // updated afterwards.
119 119 var instance = wrapper.CodeMirror = {
120 120 getValue: getValue,
121 121 setValue: operation(setValue),
122 122 getSelection: getSelection,
123 123 replaceSelection: operation(replaceSelection),
124 124 focus: function(){focusInput(); onFocus(); fastPoll();},
125 125 setOption: function(option, value) {
126 126 options[option] = value;
127 127 if (option == "lineNumbers" || option == "gutter" || option == "firstLineNumber")
128 128 operation(gutterChanged)();
129 129 else if (option == "mode" || option == "indentUnit") loadMode();
130 130 else if (option == "readOnly" && value == "nocursor") input.blur();
131 131 else if (option == "theme") scroller.className = scroller.className.replace(/cm-s-\w+/, "cm-s-" + value);
132 132 },
133 133 getOption: function(option) {return options[option];},
134 134 undo: operation(undo),
135 135 redo: operation(redo),
136 136 indentLine: operation(function(n, dir) {
137 137 if (isLine(n)) indentLine(n, dir == null ? "smart" : dir ? "add" : "subtract");
138 138 }),
139 139 historySize: function() {return {undo: history.done.length, redo: history.undone.length};},
140 140 matchBrackets: operation(function(){matchBrackets(true);}),
141 141 getTokenAt: function(pos) {
142 142 pos = clipPos(pos);
143 143 return lines[pos.line].getTokenAt(mode, getStateBefore(pos.line), pos.ch);
144 144 },
145 145 getStateAfter: function(line) {
146 146 line = clipLine(line == null ? lines.length - 1: line);
147 147 return getStateBefore(line + 1);
148 148 },
149 149 cursorCoords: function(start){
150 150 if (start == null) start = sel.inverted;
151 151 return pageCoords(start ? sel.from : sel.to);
152 152 },
153 153 charCoords: function(pos){return pageCoords(clipPos(pos));},
154 154 coordsChar: function(coords) {
155 155 var off = eltOffset(lineSpace);
156 156 var line = clipLine(Math.min(lines.length - 1, showingFrom + Math.floor((coords.y - off.top) / lineHeight())));
157 157 return clipPos({line: line, ch: charFromX(clipLine(line), coords.x - off.left)});
158 158 },
159 159 getSearchCursor: function(query, pos, caseFold) {return new SearchCursor(query, pos, caseFold);},
160 160 markText: operation(function(a, b, c){return operation(markText(a, b, c));}),
161 161 setMarker: operation(addGutterMarker),
162 162 clearMarker: operation(removeGutterMarker),
163 163 setLineClass: operation(setLineClass),
164 164 lineInfo: lineInfo,
165 165 addWidget: function(pos, node, scroll, vert, horiz) {
166 166 pos = localCoords(clipPos(pos));
167 167 var top = pos.yBot, left = pos.x;
168 168 node.style.position = "absolute";
169 169 code.appendChild(node);
170 170 if (vert == "over") top = pos.y;
171 171 else if (vert == "near") {
172 172 var vspace = Math.max(scroller.offsetHeight, lines.length * lineHeight()),
173 173 hspace = Math.max(code.clientWidth, lineSpace.clientWidth) - paddingLeft();
174 174 if (pos.yBot + node.offsetHeight > vspace && pos.y > node.offsetHeight)
175 175 top = pos.y - node.offsetHeight;
176 176 if (left + node.offsetWidth > hspace)
177 177 left = hspace - node.offsetWidth;
178 178 }
179 179 node.style.top = (top + paddingTop()) + "px";
180 180 node.style.left = node.style.right = "";
181 181 if (horiz == "right") {
182 182 left = code.clientWidth - node.offsetWidth;
183 183 node.style.right = "0px";
184 184 } else {
185 185 if (horiz == "left") left = 0;
186 186 else if (horiz == "middle") left = (code.clientWidth - node.offsetWidth) / 2;
187 187 node.style.left = (left + paddingLeft()) + "px";
188 188 }
189 189 if (scroll)
190 190 scrollIntoView(left, top, left + node.offsetWidth, top + node.offsetHeight);
191 191 },
192 192
193 193 lineCount: function() {return lines.length;},
194 194 getCursor: function(start) {
195 195 if (start == null) start = sel.inverted;
196 196 return copyPos(start ? sel.from : sel.to);
197 197 },
198 198 somethingSelected: function() {return !posEq(sel.from, sel.to);},
199 199 setCursor: operation(function(line, ch) {
200 200 if (ch == null && typeof line.line == "number") setCursor(line.line, line.ch);
201 201 else setCursor(line, ch);
202 202 }),
203 203 setSelection: operation(function(from, to) {setSelection(clipPos(from), clipPos(to || from));}),
204 204 getLine: function(line) {if (isLine(line)) return lines[line].text;},
205 205 setLine: operation(function(line, text) {
206 206 if (isLine(line)) replaceRange(text, {line: line, ch: 0}, {line: line, ch: lines[line].text.length});
207 207 }),
208 208 removeLine: operation(function(line) {
209 209 if (isLine(line)) replaceRange("", {line: line, ch: 0}, clipPos({line: line+1, ch: 0}));
210 210 }),
211 211 replaceRange: operation(replaceRange),
212 212 getRange: function(from, to) {return getRange(clipPos(from), clipPos(to));},
213 213
214 214 operation: function(f){return operation(f)();},
215 215 refresh: function(){updateDisplay(true);},
216 216 getInputField: function(){return input;},
217 217 getWrapperElement: function(){return wrapper;},
218 218 getScrollerElement: function(){return scroller;},
219 219 getGutterElement: function(){return gutter;}
220 220 };
221 221
222 222 function setValue(code) {
223 223 history = null;
224 224 var top = {line: 0, ch: 0};
225 225 updateLines(top, {line: lines.length - 1, ch: lines[lines.length-1].text.length},
226 226 splitLines(code), top, top);
227 227 history = new History();
228 228 updateInput = true;
229 229 }
230 230 function getValue(code) {
231 231 var text = [];
232 232 for (var i = 0, l = lines.length; i < l; ++i)
233 233 text.push(lines[i].text);
234 234 return text.join("\n");
235 235 }
236 236
237 237 function onMouseDown(e) {
238 238 // Check whether this is a click in a widget
239 239 for (var n = e_target(e); n != wrapper; n = n.parentNode)
240 240 if (n.parentNode == code && n != mover) return;
241 241
242 242 // First, see if this is a click in the gutter
243 243 for (var n = e_target(e); n != wrapper; n = n.parentNode)
244 244 if (n.parentNode == gutterText) {
245 245 if (options.onGutterClick)
246 246 options.onGutterClick(instance, indexOf(gutterText.childNodes, n) + showingFrom, e);
247 247 return e_preventDefault(e);
248 248 }
249 249
250 250 var start = posFromMouse(e);
251
251
252 252 switch (e_button(e)) {
253 253 case 3:
254 254 if (gecko && !mac) onContextMenu(e);
255 255 return;
256 256 case 2:
257 257 if (start) setCursor(start.line, start.ch, true);
258 258 return;
259 259 }
260 260 // For button 1, if it was clicked inside the editor
261 261 // (posFromMouse returning non-null), we have to adjust the
262 262 // selection.
263 263 if (!start) {if (e_target(e) == scroller) e_preventDefault(e); return;}
264 264
265 265 if (!focused) onFocus();
266 266
267 267 var now = +new Date;
268 268 if (lastDoubleClick > now - 400) {
269 269 e_preventDefault(e);
270 270 return selectLine(start.line);
271 271 } else if (lastClick > now - 400) {
272 272 lastDoubleClick = now;
273 273 e_preventDefault(e);
274 274 return selectWordAt(start);
275 275 } else { lastClick = now; }
276 276
277 277 var last = start, going;
278 278 if (dragAndDrop && !posEq(sel.from, sel.to) &&
279 279 !posLess(start, sel.from) && !posLess(sel.to, start)) {
280 280 // Let the drag handler handle this.
281 281 return;
282 282 }
283 283 e_preventDefault(e);
284 284 setCursor(start.line, start.ch, true);
285 285
286 286 function extend(e) {
287 287 var cur = posFromMouse(e, true);
288 288 if (cur && !posEq(cur, last)) {
289 289 if (!focused) onFocus();
290 290 last = cur;
291 291 setSelectionUser(start, cur);
292 292 updateInput = false;
293 293 var visible = visibleLines();
294 294 if (cur.line >= visible.to || cur.line < visible.from)
295 295 going = setTimeout(operation(function(){extend(e);}), 150);
296 296 }
297 297 }
298 298
299 299 var move = connect(targetDocument, "mousemove", operation(function(e) {
300 300 clearTimeout(going);
301 301 e_preventDefault(e);
302 302 extend(e);
303 303 }), true);
304 304 var up = connect(targetDocument, "mouseup", operation(function(e) {
305 305 clearTimeout(going);
306 306 var cur = posFromMouse(e);
307 307 if (cur) setSelectionUser(start, cur);
308 308 e_preventDefault(e);
309 309 focusInput();
310 310 updateInput = true;
311 311 move(); up();
312 312 }), true);
313 313 }
314 314 function onDrop(e) {
315 315 e.preventDefault();
316 316 var pos = posFromMouse(e, true), files = e.dataTransfer.files;
317 317 if (!pos || options.readOnly) return;
318 318 if (files && files.length && window.FileReader && window.File) {
319 319 function loadFile(file, i) {
320 320 var reader = new FileReader;
321 321 reader.onload = function() {
322 322 text[i] = reader.result;
323 323 if (++read == n) replaceRange(text.join(""), clipPos(pos), clipPos(pos));
324 324 };
325 325 reader.readAsText(file);
326 326 }
327 327 var n = files.length, text = Array(n), read = 0;
328 328 for (var i = 0; i < n; ++i) loadFile(files[i], i);
329 329 }
330 330 else {
331 331 try {
332 332 var text = e.dataTransfer.getData("Text");
333 333 if (text) replaceRange(text, pos, pos);
334 334 }
335 335 catch(e){}
336 336 }
337 337 }
338 338 function onDragStart(e) {
339 339 var txt = getSelection();
340 340 // This will reset escapeElement
341 341 htmlEscape(txt);
342 342 e.dataTransfer.setDragImage(escapeElement, 0, 0);
343 343 e.dataTransfer.setData("Text", txt);
344 344 }
345 345 function onKeyDown(e) {
346 346 if (!focused) onFocus();
347 347
348 348 var code = e.keyCode;
349 349 // IE does strange things with escape.
350 350 if (ie && code == 27) { e.returnValue = false; }
351 351 // Tries to detect ctrl on non-mac, cmd on mac.
352 352 var mod = (mac ? e.metaKey : e.ctrlKey) && !e.altKey, anyMod = e.ctrlKey || e.altKey || e.metaKey;
353 353 if (code == 16 || e.shiftKey) shiftSelecting = shiftSelecting || (sel.inverted ? sel.to : sel.from);
354 354 else shiftSelecting = null;
355 355 // First give onKeyEvent option a chance to handle this.
356 356 if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return;
357 357
358 358 if (code == 33 || code == 34) {scrollPage(code == 34); return e_preventDefault(e);} // page up/down
359 359 if (mod && ((code == 36 || code == 35) || // ctrl-home/end
360 360 mac && (code == 38 || code == 40))) { // cmd-up/down
361 361 scrollEnd(code == 36 || code == 38); return e_preventDefault(e);
362 362 }
363 363 if (mod && code == 65) {selectAll(); return e_preventDefault(e);} // ctrl-a
364 364 if (!options.readOnly) {
365 365 if (!anyMod && code == 13) {return;} // enter
366 366 if (!anyMod && code == 9 && handleTab(e.shiftKey)) return e_preventDefault(e); // tab
367 367 if (mod && code == 90) {undo(); return e_preventDefault(e);} // ctrl-z
368 368 if (mod && ((e.shiftKey && code == 90) || code == 89)) {redo(); return e_preventDefault(e);} // ctrl-shift-z, ctrl-y
369 369 }
370 370 if (code == 36) { if (options.smartHome) { smartHome(); return e_preventDefault(e); } }
371 371
372 372 // Key id to use in the movementKeys map. We also pass it to
373 373 // fastPoll in order to 'self learn'. We need this because
374 374 // reducedSelection, the hack where we collapse the selection to
375 375 // its start when it is inverted and a movement key is pressed
376 376 // (and later restore it again), shouldn't be used for
377 377 // non-movement keys.
378 378 curKeyId = (mod ? "c" : "") + (e.altKey ? "a" : "") + code;
379 379 if (sel.inverted && movementKeys[curKeyId] === true) {
380 380 var range = selRange(input);
381 381 if (range) {
382 382 reducedSelection = {anchor: range.start};
383 383 setSelRange(input, range.start, range.start);
384 384 }
385 385 }
386 386 // Don't save the key as a movementkey unless it had a modifier
387 387 if (!mod && !e.altKey) curKeyId = null;
388 388 fastPoll(curKeyId);
389 389 }
390 390 function onKeyUp(e) {
391 391 if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return;
392 392 if (reducedSelection) {
393 393 reducedSelection = null;
394 394 updateInput = true;
395 395 }
396 396 if (e.keyCode == 16) shiftSelecting = null;
397 397 }
398 398 function onKeyPress(e) {
399 399 if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return;
400 400 if (options.electricChars && mode.electricChars) {
401 401 var ch = String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode);
402 402 if (mode.electricChars.indexOf(ch) > -1)
403 403 setTimeout(operation(function() {indentLine(sel.to.line, "smart");}), 50);
404 404 }
405 405 var code = e.keyCode;
406 406 // Re-stop tab and enter. Necessary on some browsers.
407 407 if (code == 13) {if (!options.readOnly) handleEnter(); e_preventDefault(e);}
408 408 else if (!e.ctrlKey && !e.altKey && !e.metaKey && code == 9 && options.tabMode != "default") e_preventDefault(e);
409 409 else fastPoll(curKeyId);
410 410 }
411 411
412 412 function onFocus() {
413 413 if (options.readOnly == "nocursor") return;
414 414 if (!focused) {
415 415 if (options.onFocus) options.onFocus(instance);
416 416 focused = true;
417 417 if (wrapper.className.search(/\bCodeMirror-focused\b/) == -1)
418 418 wrapper.className += " CodeMirror-focused";
419 419 if (!leaveInputAlone) prepareInput();
420 420 }
421 421 slowPoll();
422 422 restartBlink();
423 423 }
424 424 function onBlur() {
425 425 if (focused) {
426 426 if (options.onBlur) options.onBlur(instance);
427 427 focused = false;
428 428 wrapper.className = wrapper.className.replace(" CodeMirror-focused", "");
429 429 }
430 430 clearInterval(blinker);
431 431 setTimeout(function() {if (!focused) shiftSelecting = null;}, 150);
432 432 }
433 433
434 434 // Replace the range from from to to by the strings in newText.
435 435 // Afterwards, set the selection to selFrom, selTo.
436 436 function updateLines(from, to, newText, selFrom, selTo) {
437 437 if (history) {
438 438 var old = [];
439 439 for (var i = from.line, e = to.line + 1; i < e; ++i) old.push(lines[i].text);
440 440 history.addChange(from.line, newText.length, old);
441 441 while (history.done.length > options.undoDepth) history.done.shift();
442 442 }
443 443 updateLinesNoUndo(from, to, newText, selFrom, selTo);
444 444 }
445 445 function unredoHelper(from, to) {
446 446 var change = from.pop();
447 447 if (change) {
448 448 var replaced = [], end = change.start + change.added;
449 449 for (var i = change.start; i < end; ++i) replaced.push(lines[i].text);
450 450 to.push({start: change.start, added: change.old.length, old: replaced});
451 451 var pos = clipPos({line: change.start + change.old.length - 1,
452 452 ch: editEnd(replaced[replaced.length-1], change.old[change.old.length-1])});
453 453 updateLinesNoUndo({line: change.start, ch: 0}, {line: end - 1, ch: lines[end-1].text.length}, change.old, pos, pos);
454 454 updateInput = true;
455 455 }
456 456 }
457 457 function undo() {unredoHelper(history.done, history.undone);}
458 458 function redo() {unredoHelper(history.undone, history.done);}
459 459
460 460 function updateLinesNoUndo(from, to, newText, selFrom, selTo) {
461 461 var recomputeMaxLength = false, maxLineLength = maxLine.length;
462 462 for (var i = from.line; i <= to.line; ++i) {
463 463 if (lines[i].text.length == maxLineLength) {recomputeMaxLength = true; break;}
464 464 }
465 465
466 466 var nlines = to.line - from.line, firstLine = lines[from.line], lastLine = lines[to.line];
467 467 // First adjust the line structure, taking some care to leave highlighting intact.
468 468 if (firstLine == lastLine) {
469 469 if (newText.length == 1)
470 470 firstLine.replace(from.ch, to.ch, newText[0]);
471 471 else {
472 472 lastLine = firstLine.split(to.ch, newText[newText.length-1]);
473 473 var spliceargs = [from.line + 1, nlines];
474 474 firstLine.replace(from.ch, firstLine.text.length, newText[0]);
475 475 for (var i = 1, e = newText.length - 1; i < e; ++i) spliceargs.push(new Line(newText[i]));
476 476 spliceargs.push(lastLine);
477 477 lines.splice.apply(lines, spliceargs);
478 478 }
479 479 }
480 480 else if (newText.length == 1) {
481 481 firstLine.replace(from.ch, firstLine.text.length, newText[0] + lastLine.text.slice(to.ch));
482 482 lines.splice(from.line + 1, nlines);
483 483 }
484 484 else {
485 485 var spliceargs = [from.line + 1, nlines - 1];
486 486 firstLine.replace(from.ch, firstLine.text.length, newText[0]);
487 487 lastLine.replace(0, to.ch, newText[newText.length-1]);
488 488 for (var i = 1, e = newText.length - 1; i < e; ++i) spliceargs.push(new Line(newText[i]));
489 489 lines.splice.apply(lines, spliceargs);
490 490 }
491 491
492 492
493 493 for (var i = from.line, e = i + newText.length; i < e; ++i) {
494 494 var l = lines[i].text;
495 495 if (l.length > maxLineLength) {
496 496 maxLine = l; maxLineLength = l.length; maxWidth = null;
497 497 recomputeMaxLength = false;
498 498 }
499 499 }
500 500 if (recomputeMaxLength) {
501 501 maxLineLength = 0; maxLine = ""; maxWidth = null;
502 502 for (var i = 0, e = lines.length; i < e; ++i) {
503 503 var l = lines[i].text;
504 504 if (l.length > maxLineLength) {
505 505 maxLineLength = l.length; maxLine = l;
506 506 }
507 507 }
508 508 }
509 509
510 510 // Add these lines to the work array, so that they will be
511 511 // highlighted. Adjust work lines if lines were added/removed.
512 512 var newWork = [], lendiff = newText.length - nlines - 1;
513 513 for (var i = 0, l = work.length; i < l; ++i) {
514 514 var task = work[i];
515 515 if (task < from.line) newWork.push(task);
516 516 else if (task > to.line) newWork.push(task + lendiff);
517 517 }
518 518 if (newText.length < 5) {
519 519 highlightLines(from.line, from.line + newText.length);
520 520 newWork.push(from.line + newText.length);
521 521 } else {
522 522 newWork.push(from.line);
523 523 }
524 524 work = newWork;
525 525 startWorker(100);
526 526 // Remember that these lines changed, for updating the display
527 527 changes.push({from: from.line, to: to.line + 1, diff: lendiff});
528 528 textChanged = {from: from, to: to, text: newText};
529 529
530 530 // Update the selection
531 531 function updateLine(n) {return n <= Math.min(to.line, to.line + lendiff) ? n : n + lendiff;}
532 532 setSelection(selFrom, selTo, updateLine(sel.from.line), updateLine(sel.to.line));
533 533
534 534 // Make sure the scroll-size div has the correct height.
535 535 code.style.height = (lines.length * lineHeight() + 2 * paddingTop()) + "px";
536 536 }
537 537
538 538 function replaceRange(code, from, to) {
539 539 from = clipPos(from);
540 540 if (!to) to = from; else to = clipPos(to);
541 541 code = splitLines(code);
542 542 function adjustPos(pos) {
543 543 if (posLess(pos, from)) return pos;
544 544 if (!posLess(to, pos)) return end;
545 545 var line = pos.line + code.length - (to.line - from.line) - 1;
546 546 var ch = pos.ch;
547 547 if (pos.line == to.line)
548 548 ch += code[code.length-1].length - (to.ch - (to.line == from.line ? from.ch : 0));
549 549 return {line: line, ch: ch};
550 550 }
551 551 var end;
552 552 replaceRange1(code, from, to, function(end1) {
553 553 end = end1;
554 554 return {from: adjustPos(sel.from), to: adjustPos(sel.to)};
555 555 });
556 556 return end;
557 557 }
558 558 function replaceSelection(code, collapse) {
559 559 replaceRange1(splitLines(code), sel.from, sel.to, function(end) {
560 560 if (collapse == "end") return {from: end, to: end};
561 561 else if (collapse == "start") return {from: sel.from, to: sel.from};
562 562 else return {from: sel.from, to: end};
563 563 });
564 564 }
565 565 function replaceRange1(code, from, to, computeSel) {
566 566 var endch = code.length == 1 ? code[0].length + from.ch : code[code.length-1].length;
567 567 var newSel = computeSel({line: from.line + code.length - 1, ch: endch});
568 568 updateLines(from, to, code, newSel.from, newSel.to);
569 569 }
570 570
571 571 function getRange(from, to) {
572 572 var l1 = from.line, l2 = to.line;
573 573 if (l1 == l2) return lines[l1].text.slice(from.ch, to.ch);
574 574 var code = [lines[l1].text.slice(from.ch)];
575 575 for (var i = l1 + 1; i < l2; ++i) code.push(lines[i].text);
576 576 code.push(lines[l2].text.slice(0, to.ch));
577 577 return code.join("\n");
578 578 }
579 579 function getSelection() {
580 580 return getRange(sel.from, sel.to);
581 581 }
582 582
583 583 var pollingFast = false; // Ensures slowPoll doesn't cancel fastPoll
584 584 function slowPoll() {
585 585 if (pollingFast) return;
586 586 poll.set(2000, function() {
587 587 startOperation();
588 588 readInput();
589 589 if (focused) slowPoll();
590 590 endOperation();
591 591 });
592 592 }
593 593 function fastPoll(keyId) {
594 594 var missed = false;
595 595 pollingFast = true;
596 596 function p() {
597 597 startOperation();
598 598 var changed = readInput();
599 599 if (changed && keyId) {
600 600 if (changed == "moved" && movementKeys[keyId] == null) movementKeys[keyId] = true;
601 601 if (changed == "changed") movementKeys[keyId] = false;
602 602 }
603 603 if (!changed && !missed) {missed = true; poll.set(80, p);}
604 604 else {pollingFast = false; slowPoll();}
605 605 endOperation();
606 606 }
607 607 poll.set(20, p);
608 608 }
609 609
610 610 // Inspects the textarea, compares its state (content, selection)
611 611 // to the data in the editing variable, and updates the editor
612 612 // content or cursor if something changed.
613 613 function readInput() {
614 614 if (leaveInputAlone || !focused) return;
615 615 var changed = false, text = input.value, sr = selRange(input);
616 616 if (!sr) return false;
617 617 var changed = editing.text != text, rs = reducedSelection;
618 618 var moved = changed || sr.start != editing.start || sr.end != (rs ? editing.start : editing.end);
619 619 if (!moved && !rs) return false;
620 620 if (changed) {
621 621 shiftSelecting = reducedSelection = null;
622 622 if (options.readOnly) {updateInput = true; return "changed";}
623 623 }
624 624
625 625 // Compute selection start and end based on start/end offsets in textarea
626 626 function computeOffset(n, startLine) {
627 627 var pos = 0;
628 628 for (;;) {
629 629 var found = text.indexOf("\n", pos);
630 630 if (found == -1 || (text.charAt(found-1) == "\r" ? found - 1 : found) >= n)
631 631 return {line: startLine, ch: n - pos};
632 632 ++startLine;
633 633 pos = found + 1;
634 634 }
635 635 }
636 636 var from = computeOffset(sr.start, editing.from),
637 637 to = computeOffset(sr.end, editing.from);
638 638 // Here we have to take the reducedSelection hack into account,
639 639 // so that you can, for example, press shift-up at the start of
640 640 // your selection and have the right thing happen.
641 641 if (rs) {
642 642 var head = sr.start == rs.anchor ? to : from;
643 643 var tail = shiftSelecting ? sel.to : sr.start == rs.anchor ? from : to;
644 644 if (sel.inverted = posLess(head, tail)) { from = head; to = tail; }
645 645 else { reducedSelection = null; from = tail; to = head; }
646 646 }
647 647
648 648 // In some cases (cursor on same line as before), we don't have
649 649 // to update the textarea content at all.
650 650 if (from.line == to.line && from.line == sel.from.line && from.line == sel.to.line && !shiftSelecting)
651 651 updateInput = false;
652 652
653 653 // Magic mess to extract precise edited range from the changed
654 654 // string.
655 655 if (changed) {
656 656 var start = 0, end = text.length, len = Math.min(end, editing.text.length);
657 657 var c, line = editing.from, nl = -1;
658 658 while (start < len && (c = text.charAt(start)) == editing.text.charAt(start)) {
659 659 ++start;
660 660 if (c == "\n") {line++; nl = start;}
661 661 }
662 662 var ch = nl > -1 ? start - nl : start, endline = editing.to - 1, edend = editing.text.length;
663 663 for (;;) {
664 664 c = editing.text.charAt(edend);
665 665 if (text.charAt(end) != c) {++end; ++edend; break;}
666 666 if (c == "\n") endline--;
667 667 if (edend <= start || end <= start) break;
668 668 --end; --edend;
669 669 }
670 670 var nl = editing.text.lastIndexOf("\n", edend - 1), endch = nl == -1 ? edend : edend - nl - 1;
671 671 updateLines({line: line, ch: ch}, {line: endline, ch: endch}, splitLines(text.slice(start, end)), from, to);
672 672 if (line != endline || from.line != line) updateInput = true;
673 673 }
674 674 else setSelection(from, to);
675 675
676 676 editing.text = text; editing.start = sr.start; editing.end = sr.end;
677 677 return changed ? "changed" : moved ? "moved" : false;
678 678 }
679 679
680 680 // Set the textarea content and selection range to match the
681 681 // editor state.
682 682 function prepareInput() {
683 683 var text = [];
684 684 var from = Math.max(0, sel.from.line - 1), to = Math.min(lines.length, sel.to.line + 2);
685 685 for (var i = from; i < to; ++i) text.push(lines[i].text);
686 686 text = input.value = text.join(lineSep);
687 687 var startch = sel.from.ch, endch = sel.to.ch;
688 688 for (var i = from; i < sel.from.line; ++i)
689 689 startch += lineSep.length + lines[i].text.length;
690 690 for (var i = from; i < sel.to.line; ++i)
691 691 endch += lineSep.length + lines[i].text.length;
692 692 editing = {text: text, from: from, to: to, start: startch, end: endch};
693 693 setSelRange(input, startch, reducedSelection ? startch : endch);
694 694 }
695 695 function focusInput() {
696 696 if (options.readOnly != "nocursor") input.focus();
697 697 }
698 698
699 699 function scrollEditorIntoView() {
700 700 if (!cursor.getBoundingClientRect) return;
701 701 var rect = cursor.getBoundingClientRect();
702 702 var winH = window.innerHeight || document.body.offsetHeight || document.documentElement.offsetHeight;
703 703 if (rect.top < 0 || rect.bottom > winH) cursor.scrollIntoView();
704 704 }
705 705 function scrollCursorIntoView() {
706 706 var cursor = localCoords(sel.inverted ? sel.from : sel.to);
707 707 return scrollIntoView(cursor.x, cursor.y, cursor.x, cursor.yBot);
708 708 }
709 709 function scrollIntoView(x1, y1, x2, y2) {
710 710 var pl = paddingLeft(), pt = paddingTop(), lh = lineHeight();
711 711 y1 += pt; y2 += pt; x1 += pl; x2 += pl;
712 712 var screen = scroller.clientHeight, screentop = scroller.scrollTop, scrolled = false, result = true;
713 713 if (y1 < screentop) {scroller.scrollTop = Math.max(0, y1 - 2*lh); scrolled = true;}
714 714 else if (y2 > screentop + screen) {scroller.scrollTop = y2 + lh - screen; scrolled = true;}
715 715
716 716 var screenw = scroller.clientWidth, screenleft = scroller.scrollLeft;
717 717 var gutterw = options.fixedGutter ? gutter.clientWidth : 0;
718 718 if (x1 < screenleft + gutterw) {
719 719 if (x1 < 50) x1 = 0;
720 720 scroller.scrollLeft = Math.max(0, x1 - 10 - gutterw);
721 721 scrolled = true;
722 722 }
723 723 else if (x2 > screenw + screenleft) {
724 724 scroller.scrollLeft = x2 + 10 - screenw;
725 725 scrolled = true;
726 726 if (x2 > code.clientWidth) result = false;
727 727 }
728 728 if (scrolled && options.onScroll) options.onScroll(instance);
729 729 return result;
730 730 }
731 731
732 732 function visibleLines() {
733 733 var lh = lineHeight(), top = scroller.scrollTop - paddingTop();
734 734 return {from: Math.min(lines.length, Math.max(0, Math.floor(top / lh))),
735 735 to: Math.min(lines.length, Math.ceil((top + scroller.clientHeight) / lh))};
736 736 }
737 737 // Uses a set of changes plus the current scroll position to
738 738 // determine which DOM updates have to be made, and makes the
739 739 // updates.
740 740 function updateDisplay(changes) {
741 741 if (!scroller.clientWidth) {
742 742 showingFrom = showingTo = 0;
743 743 return;
744 744 }
745 745 // First create a range of theoretically intact lines, and punch
746 746 // holes in that using the change info.
747 747 var intact = changes === true ? [] : [{from: showingFrom, to: showingTo, domStart: 0}];
748 748 for (var i = 0, l = changes.length || 0; i < l; ++i) {
749 749 var change = changes[i], intact2 = [], diff = change.diff || 0;
750 750 for (var j = 0, l2 = intact.length; j < l2; ++j) {
751 751 var range = intact[j];
752 752 if (change.to <= range.from)
753 753 intact2.push({from: range.from + diff, to: range.to + diff, domStart: range.domStart});
754 754 else if (range.to <= change.from)
755 755 intact2.push(range);
756 756 else {
757 757 if (change.from > range.from)
758 758 intact2.push({from: range.from, to: change.from, domStart: range.domStart})
759 759 if (change.to < range.to)
760 760 intact2.push({from: change.to + diff, to: range.to + diff,
761 761 domStart: range.domStart + (change.to - range.from)});
762 762 }
763 763 }
764 764 intact = intact2;
765 765 }
766 766
767 767 // Then, determine which lines we'd want to see, and which
768 768 // updates have to be made to get there.
769 769 var visible = visibleLines();
770 770 var from = Math.min(showingFrom, Math.max(visible.from - 3, 0)),
771 771 to = Math.min(lines.length, Math.max(showingTo, visible.to + 3)),
772 772 updates = [], domPos = 0, domEnd = showingTo - showingFrom, pos = from, changedLines = 0;
773 773
774 774 for (var i = 0, l = intact.length; i < l; ++i) {
775 775 var range = intact[i];
776 776 if (range.to <= from) continue;
777 777 if (range.from >= to) break;
778 778 if (range.domStart > domPos || range.from > pos) {
779 779 updates.push({from: pos, to: range.from, domSize: range.domStart - domPos, domStart: domPos});
780 780 changedLines += range.from - pos;
781 781 }
782 782 pos = range.to;
783 783 domPos = range.domStart + (range.to - range.from);
784 784 }
785 785 if (domPos != domEnd || pos != to) {
786 786 changedLines += Math.abs(to - pos);
787 787 updates.push({from: pos, to: to, domSize: domEnd - domPos, domStart: domPos});
788 788 if (to - pos != domEnd - domPos) gutterDirty = true;
789 789 }
790 790
791 791 if (!updates.length) return;
792 792 lineDiv.style.display = "none";
793 793 // If more than 30% of the screen needs update, just do a full
794 794 // redraw (which is quicker than patching)
795 795 if (changedLines > (visible.to - visible.from) * .3)
796 796 refreshDisplay(from = Math.max(visible.from - 10, 0), to = Math.min(visible.to + 7, lines.length));
797 797 // Otherwise, only update the stuff that needs updating.
798 798 else
799 799 patchDisplay(updates);
800 800 lineDiv.style.display = "";
801 801
802 802 // Position the mover div to align with the lines it's supposed
803 803 // to be showing (which will cover the visible display)
804 804 var different = from != showingFrom || to != showingTo || lastHeight != scroller.clientHeight;
805 805 showingFrom = from; showingTo = to;
806 806 mover.style.top = (from * lineHeight()) + "px";
807 807 if (different) {
808 808 lastHeight = scroller.clientHeight;
809 809 code.style.height = (lines.length * lineHeight() + 2 * paddingTop()) + "px";
810 810 }
811 811 if (different || gutterDirty) updateGutter();
812 812
813 813 if (maxWidth == null) maxWidth = stringWidth(maxLine);
814 814 if (maxWidth > scroller.clientWidth) {
815 815 lineSpace.style.width = maxWidth + "px";
816 816 // Needed to prevent odd wrapping/hiding of widgets placed in here.
817 817 code.style.width = "";
818 818 code.style.width = scroller.scrollWidth + "px";
819 819 } else {
820 820 lineSpace.style.width = code.style.width = "";
821 821 }
822 822
823 823 // Since this is all rather error prone, it is honoured with the
824 824 // only assertion in the whole file.
825 825 if (lineDiv.childNodes.length != showingTo - showingFrom)
826 826 throw new Error("BAD PATCH! " + JSON.stringify(updates) + " size=" + (showingTo - showingFrom) +
827 827 " nodes=" + lineDiv.childNodes.length);
828 828 updateCursor();
829 829 }
830 830
831 831 function refreshDisplay(from, to) {
832 832 var html = [], start = {line: from, ch: 0}, inSel = posLess(sel.from, start) && !posLess(sel.to, start);
833 833 for (var i = from; i < to; ++i) {
834 834 var ch1 = null, ch2 = null;
835 835 if (inSel) {
836 836 ch1 = 0;
837 837 if (sel.to.line == i) {inSel = false; ch2 = sel.to.ch;}
838 838 }
839 839 else if (sel.from.line == i) {
840 840 if (sel.to.line == i) {ch1 = sel.from.ch; ch2 = sel.to.ch;}
841 841 else {inSel = true; ch1 = sel.from.ch;}
842 842 }
843 843 html.push(lines[i].getHTML(ch1, ch2, true));
844 844 }
845 845 lineDiv.innerHTML = html.join("");
846 846 }
847 847 function patchDisplay(updates) {
848 848 // Slightly different algorithm for IE (badInnerHTML), since
849 849 // there .innerHTML on PRE nodes is dumb, and discards
850 850 // whitespace.
851 851 var sfrom = sel.from.line, sto = sel.to.line, off = 0,
852 852 scratch = badInnerHTML && targetDocument.createElement("div");
853 853 for (var i = 0, e = updates.length; i < e; ++i) {
854 854 var rec = updates[i];
855 855 var extra = (rec.to - rec.from) - rec.domSize;
856 856 var nodeAfter = lineDiv.childNodes[rec.domStart + rec.domSize + off] || null;
857 857 if (badInnerHTML)
858 858 for (var j = Math.max(-extra, rec.domSize); j > 0; --j)
859 859 lineDiv.removeChild(nodeAfter ? nodeAfter.previousSibling : lineDiv.lastChild);
860 860 else if (extra) {
861 861 for (var j = Math.max(0, extra); j > 0; --j)
862 862 lineDiv.insertBefore(targetDocument.createElement("pre"), nodeAfter);
863 863 for (var j = Math.max(0, -extra); j > 0; --j)
864 864 lineDiv.removeChild(nodeAfter ? nodeAfter.previousSibling : lineDiv.lastChild);
865 865 }
866 866 var node = lineDiv.childNodes[rec.domStart + off], inSel = sfrom < rec.from && sto >= rec.from;
867 867 for (var j = rec.from; j < rec.to; ++j) {
868 868 var ch1 = null, ch2 = null;
869 869 if (inSel) {
870 870 ch1 = 0;
871 871 if (sto == j) {inSel = false; ch2 = sel.to.ch;}
872 872 }
873 873 else if (sfrom == j) {
874 874 if (sto == j) {ch1 = sel.from.ch; ch2 = sel.to.ch;}
875 875 else {inSel = true; ch1 = sel.from.ch;}
876 876 }
877 877 if (badInnerHTML) {
878 878 scratch.innerHTML = lines[j].getHTML(ch1, ch2, true);
879 879 lineDiv.insertBefore(scratch.firstChild, nodeAfter);
880 880 }
881 881 else {
882 882 node.innerHTML = lines[j].getHTML(ch1, ch2, false);
883 883 node.className = lines[j].className || "";
884 884 node = node.nextSibling;
885 885 }
886 886 }
887 887 off += extra;
888 888 }
889 889 }
890 890
891 891 function updateGutter() {
892 892 if (!options.gutter && !options.lineNumbers) return;
893 893 var hText = mover.offsetHeight, hEditor = scroller.clientHeight;
894 894 gutter.style.height = (hText - hEditor < 2 ? hEditor : hText) + "px";
895 895 var html = [];
896 896 for (var i = showingFrom; i < Math.max(showingTo, showingFrom + 1); ++i) {
897 897 var marker = lines[i].gutterMarker;
898 898 var text = options.lineNumbers ? i + options.firstLineNumber : null;
899 899 if (marker && marker.text)
900 900 text = marker.text.replace("%N%", text != null ? text : "");
901 901 else if (text == null)
902 902 text = "\u00a0";
903 903 html.push((marker && marker.style ? '<pre class="' + marker.style + '">' : "<pre>"), text, "</pre>");
904 904 }
905 905 gutter.style.display = "none";
906 906 gutterText.innerHTML = html.join("");
907 907 var minwidth = String(lines.length).length, firstNode = gutterText.firstChild, val = eltText(firstNode), pad = "";
908 908 while (val.length + pad.length < minwidth) pad += "\u00a0";
909 909 if (pad) firstNode.insertBefore(targetDocument.createTextNode(pad), firstNode.firstChild);
910 910 gutter.style.display = "";
911 911 lineSpace.style.marginLeft = gutter.offsetWidth + "px";
912 912 gutterDirty = false;
913 913 }
914 914 function updateCursor() {
915 915 var head = sel.inverted ? sel.from : sel.to, lh = lineHeight();
916 916 var x = charX(head.line, head.ch);
917 917 var top = head.line * lh - scroller.scrollTop;
918 918 inputDiv.style.top = Math.max(Math.min(top, scroller.offsetHeight), 0) + "px";
919 919 inputDiv.style.left = (x - scroller.scrollLeft) + "px";
920 920 if (posEq(sel.from, sel.to)) {
921 921 cursor.style.top = (head.line - showingFrom) * lh + "px";
922 922 cursor.style.left = x + "px";
923 923 cursor.style.display = "";
924 924 }
925 925 else cursor.style.display = "none";
926 926 }
927 927
928 928 function setSelectionUser(from, to) {
929 929 var sh = shiftSelecting && clipPos(shiftSelecting);
930 930 if (sh) {
931 931 if (posLess(sh, from)) from = sh;
932 932 else if (posLess(to, sh)) to = sh;
933 933 }
934 934 setSelection(from, to);
935 935 }
936 936 // Update the selection. Last two args are only used by
937 937 // updateLines, since they have to be expressed in the line
938 938 // numbers before the update.
939 939 function setSelection(from, to, oldFrom, oldTo) {
940 940 if (posEq(sel.from, from) && posEq(sel.to, to)) return;
941 941 if (posLess(to, from)) {var tmp = to; to = from; from = tmp;}
942 942
943 943 if (posEq(from, to)) sel.inverted = false;
944 944 else if (posEq(from, sel.to)) sel.inverted = false;
945 945 else if (posEq(to, sel.from)) sel.inverted = true;
946 946
947 947 // Some ugly logic used to only mark the lines that actually did
948 948 // see a change in selection as changed, rather than the whole
949 949 // selected range.
950 950 if (oldFrom == null) {oldFrom = sel.from.line; oldTo = sel.to.line;}
951 951 if (posEq(from, to)) {
952 952 if (!posEq(sel.from, sel.to))
953 953 changes.push({from: oldFrom, to: oldTo + 1});
954 954 }
955 955 else if (posEq(sel.from, sel.to)) {
956 956 changes.push({from: from.line, to: to.line + 1});
957 957 }
958 958 else {
959 959 if (!posEq(from, sel.from)) {
960 960 if (from.line < oldFrom)
961 961 changes.push({from: from.line, to: Math.min(to.line, oldFrom) + 1});
962 962 else
963 963 changes.push({from: oldFrom, to: Math.min(oldTo, from.line) + 1});
964 964 }
965 965 if (!posEq(to, sel.to)) {
966 966 if (to.line < oldTo)
967 967 changes.push({from: Math.max(oldFrom, from.line), to: oldTo + 1});
968 968 else
969 969 changes.push({from: Math.max(from.line, oldTo), to: to.line + 1});
970 970 }
971 971 }
972 972 sel.from = from; sel.to = to;
973 973 selectionChanged = true;
974 974 }
975 975 function setCursor(line, ch, user) {
976 976 var pos = clipPos({line: line, ch: ch || 0});
977 977 (user ? setSelectionUser : setSelection)(pos, pos);
978 978 }
979 979
980 980 function clipLine(n) {return Math.max(0, Math.min(n, lines.length-1));}
981 981 function clipPos(pos) {
982 982 if (pos.line < 0) return {line: 0, ch: 0};
983 983 if (pos.line >= lines.length) return {line: lines.length-1, ch: lines[lines.length-1].text.length};
984 984 var ch = pos.ch, linelen = lines[pos.line].text.length;
985 985 if (ch == null || ch > linelen) return {line: pos.line, ch: linelen};
986 986 else if (ch < 0) return {line: pos.line, ch: 0};
987 987 else return pos;
988 988 }
989 989
990 990 function scrollPage(down) {
991 991 var linesPerPage = Math.floor(scroller.clientHeight / lineHeight()), head = sel.inverted ? sel.from : sel.to;
992 992 setCursor(head.line + (Math.max(linesPerPage - 1, 1) * (down ? 1 : -1)), head.ch, true);
993 993 }
994 994 function scrollEnd(top) {
995 995 var pos = top ? {line: 0, ch: 0} : {line: lines.length - 1, ch: lines[lines.length-1].text.length};
996 996 setSelectionUser(pos, pos);
997 997 }
998 998 function selectAll() {
999 999 var endLine = lines.length - 1;
1000 1000 setSelection({line: 0, ch: 0}, {line: endLine, ch: lines[endLine].text.length});
1001 1001 }
1002 1002 function selectWordAt(pos) {
1003 1003 var line = lines[pos.line].text;
1004 1004 var start = pos.ch, end = pos.ch;
1005 1005 while (start > 0 && /\w/.test(line.charAt(start - 1))) --start;
1006 1006 while (end < line.length && /\w/.test(line.charAt(end))) ++end;
1007 1007 setSelectionUser({line: pos.line, ch: start}, {line: pos.line, ch: end});
1008 1008 }
1009 1009 function selectLine(line) {
1010 1010 setSelectionUser({line: line, ch: 0}, {line: line, ch: lines[line].text.length});
1011 1011 }
1012 1012 function handleEnter() {
1013 1013 replaceSelection("\n", "end");
1014 1014 if (options.enterMode != "flat")
1015 1015 indentLine(sel.from.line, options.enterMode == "keep" ? "prev" : "smart");
1016 1016 }
1017 1017 function handleTab(shift) {
1018 1018 function indentSelected(mode) {
1019 1019 if (posEq(sel.from, sel.to)) return indentLine(sel.from.line, mode);
1020 1020 var e = sel.to.line - (sel.to.ch ? 0 : 1);
1021 1021 for (var i = sel.from.line; i <= e; ++i) indentLine(i, mode);
1022 1022 }
1023 1023 shiftSelecting = null;
1024 1024 switch (options.tabMode) {
1025 1025 case "default":
1026 1026 return false;
1027 1027 case "indent":
1028 1028 indentSelected("smart");
1029 1029 break;
1030 1030 case "classic":
1031 1031 if (posEq(sel.from, sel.to)) {
1032 1032 if (shift) indentLine(sel.from.line, "smart");
1033 1033 else replaceSelection("\t", "end");
1034 1034 break;
1035 1035 }
1036 1036 case "shift":
1037 1037 indentSelected(shift ? "subtract" : "add");
1038 1038 break;
1039 1039 }
1040 1040 return true;
1041 1041 }
1042 1042 function smartHome() {
1043 1043 var firstNonWS = Math.max(0, lines[sel.from.line].text.search(/\S/));
1044 1044 setCursor(sel.from.line, sel.from.ch <= firstNonWS && sel.from.ch ? 0 : firstNonWS, true);
1045 1045 }
1046 1046
1047 1047 function indentLine(n, how) {
1048 1048 if (how == "smart") {
1049 1049 if (!mode.indent) how = "prev";
1050 1050 else var state = getStateBefore(n);
1051 1051 }
1052 1052
1053 1053 var line = lines[n], curSpace = line.indentation(), curSpaceString = line.text.match(/^\s*/)[0], indentation;
1054 1054 if (how == "prev") {
1055 1055 if (n) indentation = lines[n-1].indentation();
1056 1056 else indentation = 0;
1057 1057 }
1058 1058 else if (how == "smart") indentation = mode.indent(state, line.text.slice(curSpaceString.length));
1059 1059 else if (how == "add") indentation = curSpace + options.indentUnit;
1060 1060 else if (how == "subtract") indentation = curSpace - options.indentUnit;
1061 1061 indentation = Math.max(0, indentation);
1062 1062 var diff = indentation - curSpace;
1063 1063
1064 1064 if (!diff) {
1065 1065 if (sel.from.line != n && sel.to.line != n) return;
1066 1066 var indentString = curSpaceString;
1067 1067 }
1068 1068 else {
1069 1069 var indentString = "", pos = 0;
1070 1070 if (options.indentWithTabs)
1071 1071 for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";}
1072 1072 while (pos < indentation) {++pos; indentString += " ";}
1073 1073 }
1074 1074
1075 1075 replaceRange(indentString, {line: n, ch: 0}, {line: n, ch: curSpaceString.length});
1076 1076 }
1077 1077
1078 1078 function loadMode() {
1079 1079 mode = CodeMirror.getMode(options, options.mode);
1080 1080 for (var i = 0, l = lines.length; i < l; ++i)
1081 1081 lines[i].stateAfter = null;
1082 1082 work = [0];
1083 1083 startWorker();
1084 1084 }
1085 1085 function gutterChanged() {
1086 1086 var visible = options.gutter || options.lineNumbers;
1087 1087 gutter.style.display = visible ? "" : "none";
1088 1088 if (visible) gutterDirty = true;
1089 1089 else lineDiv.parentNode.style.marginLeft = 0;
1090 1090 }
1091 1091
1092 1092 function markText(from, to, className) {
1093 1093 from = clipPos(from); to = clipPos(to);
1094 1094 var accum = [];
1095 1095 function add(line, from, to, className) {
1096 1096 var line = lines[line], mark = line.addMark(from, to, className);
1097 1097 mark.line = line;
1098 1098 accum.push(mark);
1099 1099 }
1100 1100 if (from.line == to.line) add(from.line, from.ch, to.ch, className);
1101 1101 else {
1102 1102 add(from.line, from.ch, null, className);
1103 1103 for (var i = from.line + 1, e = to.line; i < e; ++i)
1104 1104 add(i, 0, null, className);
1105 1105 add(to.line, 0, to.ch, className);
1106 1106 }
1107 1107 changes.push({from: from.line, to: to.line + 1});
1108 1108 return function() {
1109 1109 var start, end;
1110 1110 for (var i = 0; i < accum.length; ++i) {
1111 1111 var mark = accum[i], found = indexOf(lines, mark.line);
1112 1112 mark.line.removeMark(mark);
1113 1113 if (found > -1) {
1114 1114 if (start == null) start = found;
1115 1115 end = found;
1116 1116 }
1117 1117 }
1118 1118 if (start != null) changes.push({from: start, to: end + 1});
1119 1119 };
1120 1120 }
1121 1121
1122 1122 function addGutterMarker(line, text, className) {
1123 1123 if (typeof line == "number") line = lines[clipLine(line)];
1124 1124 line.gutterMarker = {text: text, style: className};
1125 1125 gutterDirty = true;
1126 1126 return line;
1127 1127 }
1128 1128 function removeGutterMarker(line) {
1129 1129 if (typeof line == "number") line = lines[clipLine(line)];
1130 1130 line.gutterMarker = null;
1131 1131 gutterDirty = true;
1132 1132 }
1133 1133 function setLineClass(line, className) {
1134 1134 if (typeof line == "number") {
1135 1135 var no = line;
1136 1136 line = lines[clipLine(line)];
1137 1137 }
1138 1138 else {
1139 1139 var no = indexOf(lines, line);
1140 1140 if (no == -1) return null;
1141 1141 }
1142 1142 if (line.className != className) {
1143 1143 line.className = className;
1144 1144 changes.push({from: no, to: no + 1});
1145 1145 }
1146 1146 return line;
1147 1147 }
1148 1148
1149 1149 function lineInfo(line) {
1150 1150 if (typeof line == "number") {
1151 1151 var n = line;
1152 1152 line = lines[line];
1153 1153 if (!line) return null;
1154 1154 }
1155 1155 else {
1156 1156 var n = indexOf(lines, line);
1157 1157 if (n == -1) return null;
1158 1158 }
1159 1159 var marker = line.gutterMarker;
1160 1160 return {line: n, text: line.text, markerText: marker && marker.text, markerClass: marker && marker.style};
1161 1161 }
1162 1162
1163 1163 function stringWidth(str) {
1164 1164 measure.innerHTML = "<pre><span>x</span></pre>";
1165 1165 measure.firstChild.firstChild.firstChild.nodeValue = str;
1166 1166 return measure.firstChild.firstChild.offsetWidth || 10;
1167 1167 }
1168 1168 // These are used to go from pixel positions to character
1169 1169 // positions, taking varying character widths into account.
1170 1170 function charX(line, pos) {
1171 1171 if (pos == 0) return 0;
1172 1172 measure.innerHTML = "<pre><span>" + lines[line].getHTML(null, null, false, pos) + "</span></pre>";
1173 1173 return measure.firstChild.firstChild.offsetWidth;
1174 1174 }
1175 1175 function charFromX(line, x) {
1176 1176 if (x <= 0) return 0;
1177 1177 var lineObj = lines[line], text = lineObj.text;
1178 1178 function getX(len) {
1179 1179 measure.innerHTML = "<pre><span>" + lineObj.getHTML(null, null, false, len) + "</span></pre>";
1180 1180 return measure.firstChild.firstChild.offsetWidth;
1181 1181 }
1182 1182 var from = 0, fromX = 0, to = text.length, toX;
1183 1183 // Guess a suitable upper bound for our search.
1184 1184 var estimated = Math.min(to, Math.ceil(x / stringWidth("x")));
1185 1185 for (;;) {
1186 1186 var estX = getX(estimated);
1187 1187 if (estX <= x && estimated < to) estimated = Math.min(to, Math.ceil(estimated * 1.2));
1188 1188 else {toX = estX; to = estimated; break;}
1189 1189 }
1190 1190 if (x > toX) return to;
1191 1191 // Try to guess a suitable lower bound as well.
1192 1192 estimated = Math.floor(to * 0.8); estX = getX(estimated);
1193 1193 if (estX < x) {from = estimated; fromX = estX;}
1194 1194 // Do a binary search between these bounds.
1195 1195 for (;;) {
1196 1196 if (to - from <= 1) return (toX - x > x - fromX) ? from : to;
1197 1197 var middle = Math.ceil((from + to) / 2), middleX = getX(middle);
1198 1198 if (middleX > x) {to = middle; toX = middleX;}
1199 1199 else {from = middle; fromX = middleX;}
1200 1200 }
1201 1201 }
1202 1202
1203 1203 function localCoords(pos, inLineWrap) {
1204 1204 var lh = lineHeight(), line = pos.line - (inLineWrap ? showingFrom : 0);
1205 1205 return {x: charX(pos.line, pos.ch), y: line * lh, yBot: (line + 1) * lh};
1206 1206 }
1207 1207 function pageCoords(pos) {
1208 1208 var local = localCoords(pos, true), off = eltOffset(lineSpace);
1209 1209 return {x: off.left + local.x, y: off.top + local.y, yBot: off.top + local.yBot};
1210 1210 }
1211 1211
1212 1212 function lineHeight() {
1213 1213 var nlines = lineDiv.childNodes.length;
1214 1214 if (nlines) return (lineDiv.offsetHeight / nlines) || 1;
1215 1215 measure.innerHTML = "<pre>x</pre>";
1216 1216 return measure.firstChild.offsetHeight || 1;
1217 1217 }
1218 1218 function paddingTop() {return lineSpace.offsetTop;}
1219 1219 function paddingLeft() {return lineSpace.offsetLeft;}
1220 1220
1221 1221 function posFromMouse(e, liberal) {
1222 1222 var offW = eltOffset(scroller, true), x, y;
1223 1223 // Fails unpredictably on IE[67] when mouse is dragged around quickly.
1224 1224 try { x = e.clientX; y = e.clientY; } catch (e) { return null; }
1225 1225 // This is a mess of a heuristic to try and determine whether a
1226 1226 // scroll-bar was clicked or not, and to return null if one was
1227 1227 // (and !liberal).
1228 1228 if (!liberal && (x - offW.left > scroller.clientWidth || y - offW.top > scroller.clientHeight))
1229 1229 return null;
1230 1230 var offL = eltOffset(lineSpace, true);
1231 1231 var line = showingFrom + Math.floor((y - offL.top) / lineHeight());
1232 1232 return clipPos({line: line, ch: charFromX(clipLine(line), x - offL.left)});
1233 1233 }
1234 1234 function onContextMenu(e) {
1235 1235 var pos = posFromMouse(e);
1236 1236 if (!pos || window.opera) return; // Opera is difficult.
1237 1237 if (posEq(sel.from, sel.to) || posLess(pos, sel.from) || !posLess(pos, sel.to))
1238 1238 operation(setCursor)(pos.line, pos.ch);
1239 1239
1240 1240 var oldCSS = input.style.cssText;
1241 1241 inputDiv.style.position = "absolute";
1242 1242 input.style.cssText = "position: fixed; width: 30px; height: 30px; top: " + (e.clientY - 5) +
1243 1243 "px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: white; " +
1244 1244 "border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);";
1245 1245 leaveInputAlone = true;
1246 1246 var val = input.value = getSelection();
1247 1247 focusInput();
1248 1248 setSelRange(input, 0, input.value.length);
1249 1249 function rehide() {
1250 1250 var newVal = splitLines(input.value).join("\n");
1251 1251 if (newVal != val) operation(replaceSelection)(newVal, "end");
1252 1252 inputDiv.style.position = "relative";
1253 1253 input.style.cssText = oldCSS;
1254 1254 leaveInputAlone = false;
1255 1255 prepareInput();
1256 1256 slowPoll();
1257 1257 }
1258
1258
1259 1259 if (gecko) {
1260 1260 e_stop(e);
1261 1261 var mouseup = connect(window, "mouseup", function() {
1262 1262 mouseup();
1263 1263 setTimeout(rehide, 20);
1264 1264 }, true);
1265 1265 }
1266 1266 else {
1267 1267 setTimeout(rehide, 50);
1268 1268 }
1269 1269 }
1270 1270
1271 1271 // Cursor-blinking
1272 1272 function restartBlink() {
1273 1273 clearInterval(blinker);
1274 1274 var on = true;
1275 1275 cursor.style.visibility = "";
1276 1276 blinker = setInterval(function() {
1277 1277 cursor.style.visibility = (on = !on) ? "" : "hidden";
1278 1278 }, 650);
1279 1279 }
1280 1280
1281 1281 var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<"};
1282 1282 function matchBrackets(autoclear) {
1283 1283 var head = sel.inverted ? sel.from : sel.to, line = lines[head.line], pos = head.ch - 1;
1284 1284 var match = (pos >= 0 && matching[line.text.charAt(pos)]) || matching[line.text.charAt(++pos)];
1285 1285 if (!match) return;
1286 1286 var ch = match.charAt(0), forward = match.charAt(1) == ">", d = forward ? 1 : -1, st = line.styles;
1287 1287 for (var off = pos + 1, i = 0, e = st.length; i < e; i+=2)
1288 1288 if ((off -= st[i].length) <= 0) {var style = st[i+1]; break;}
1289 1289
1290 1290 var stack = [line.text.charAt(pos)], re = /[(){}[\]]/;
1291 1291 function scan(line, from, to) {
1292 1292 if (!line.text) return;
1293 1293 var st = line.styles, pos = forward ? 0 : line.text.length - 1, cur;
1294 1294 for (var i = forward ? 0 : st.length - 2, e = forward ? st.length : -2; i != e; i += 2*d) {
1295 1295 var text = st[i];
1296 1296 if (st[i+1] != null && st[i+1] != style) {pos += d * text.length; continue;}
1297 1297 for (var j = forward ? 0 : text.length - 1, te = forward ? text.length : -1; j != te; j += d, pos+=d) {
1298 1298 if (pos >= from && pos < to && re.test(cur = text.charAt(j))) {
1299 1299 var match = matching[cur];
1300 1300 if (match.charAt(1) == ">" == forward) stack.push(cur);
1301 1301 else if (stack.pop() != match.charAt(0)) return {pos: pos, match: false};
1302 1302 else if (!stack.length) return {pos: pos, match: true};
1303 1303 }
1304 1304 }
1305 1305 }
1306 1306 }
1307 1307 for (var i = head.line, e = forward ? Math.min(i + 100, lines.length) : Math.max(-1, i - 100); i != e; i+=d) {
1308 1308 var line = lines[i], first = i == head.line;
1309 1309 var found = scan(line, first && forward ? pos + 1 : 0, first && !forward ? pos : line.text.length);
1310 1310 if (found) break;
1311 1311 }
1312 1312 if (!found) found = {pos: null, match: false};
1313 1313 var style = found.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket";
1314 1314 var one = markText({line: head.line, ch: pos}, {line: head.line, ch: pos+1}, style),
1315 1315 two = found.pos != null
1316 1316 ? markText({line: i, ch: found.pos}, {line: i, ch: found.pos + 1}, style)
1317 1317 : function() {};
1318 1318 var clear = operation(function(){one(); two();});
1319 1319 if (autoclear) setTimeout(clear, 800);
1320 1320 else bracketHighlighted = clear;
1321 1321 }
1322 1322
1323 1323 // Finds the line to start with when starting a parse. Tries to
1324 1324 // find a line with a stateAfter, so that it can start with a
1325 1325 // valid state. If that fails, it returns the line with the
1326 1326 // smallest indentation, which tends to need the least context to
1327 1327 // parse correctly.
1328 1328 function findStartLine(n) {
1329 1329 var minindent, minline;
1330 1330 for (var search = n, lim = n - 40; search > lim; --search) {
1331 1331 if (search == 0) return 0;
1332 1332 var line = lines[search-1];
1333 1333 if (line.stateAfter) return search;
1334 1334 var indented = line.indentation();
1335 1335 if (minline == null || minindent > indented) {
1336 1336 minline = search - 1;
1337 1337 minindent = indented;
1338 1338 }
1339 1339 }
1340 1340 return minline;
1341 1341 }
1342 1342 function getStateBefore(n) {
1343 1343 var start = findStartLine(n), state = start && lines[start-1].stateAfter;
1344 1344 if (!state) state = startState(mode);
1345 1345 else state = copyState(mode, state);
1346 1346 for (var i = start; i < n; ++i) {
1347 1347 var line = lines[i];
1348 1348 line.highlight(mode, state);
1349 1349 line.stateAfter = copyState(mode, state);
1350 1350 }
1351 1351 if (n < lines.length && !lines[n].stateAfter) work.push(n);
1352 1352 return state;
1353 1353 }
1354 1354 function highlightLines(start, end) {
1355 1355 var state = getStateBefore(start);
1356 1356 for (var i = start; i < end; ++i) {
1357 1357 var line = lines[i];
1358 1358 line.highlight(mode, state);
1359 1359 line.stateAfter = copyState(mode, state);
1360 1360 }
1361 1361 }
1362 1362 function highlightWorker() {
1363 1363 var end = +new Date + options.workTime;
1364 1364 var foundWork = work.length;
1365 1365 while (work.length) {
1366 1366 if (!lines[showingFrom].stateAfter) var task = showingFrom;
1367 1367 else var task = work.pop();
1368 1368 if (task >= lines.length) continue;
1369 1369 var start = findStartLine(task), state = start && lines[start-1].stateAfter;
1370 1370 if (state) state = copyState(mode, state);
1371 1371 else state = startState(mode);
1372 1372
1373 1373 var unchanged = 0, compare = mode.compareStates, realChange = false;
1374 1374 for (var i = start, l = lines.length; i < l; ++i) {
1375 1375 var line = lines[i], hadState = line.stateAfter;
1376 1376 if (+new Date > end) {
1377 1377 work.push(i);
1378 1378 startWorker(options.workDelay);
1379 1379 if (realChange) changes.push({from: task, to: i + 1});
1380 1380 return;
1381 1381 }
1382 1382 var changed = line.highlight(mode, state);
1383 1383 if (changed) realChange = true;
1384 1384 line.stateAfter = copyState(mode, state);
1385 1385 if (compare) {
1386 1386 if (hadState && compare(hadState, state)) break;
1387 1387 } else {
1388 1388 if (changed !== false || !hadState) unchanged = 0;
1389 1389 else if (++unchanged > 3) break;
1390 1390 }
1391 1391 }
1392 1392 if (realChange) changes.push({from: task, to: i + 1});
1393 1393 }
1394 1394 if (foundWork && options.onHighlightComplete)
1395 1395 options.onHighlightComplete(instance);
1396 1396 }
1397 1397 function startWorker(time) {
1398 1398 if (!work.length) return;
1399 1399 highlight.set(time, operation(highlightWorker));
1400 1400 }
1401 1401
1402 1402 // Operations are used to wrap changes in such a way that each
1403 1403 // change won't have to update the cursor and display (which would
1404 1404 // be awkward, slow, and error-prone), but instead updates are
1405 1405 // batched and then all combined and executed at once.
1406 1406 function startOperation() {
1407 1407 updateInput = null; changes = []; textChanged = selectionChanged = false;
1408 1408 }
1409 1409 function endOperation() {
1410 1410 var reScroll = false;
1411 1411 if (selectionChanged) reScroll = !scrollCursorIntoView();
1412 1412 if (changes.length) updateDisplay(changes);
1413 1413 else {
1414 1414 if (selectionChanged) updateCursor();
1415 1415 if (gutterDirty) updateGutter();
1416 1416 }
1417 1417 if (reScroll) scrollCursorIntoView();
1418 1418 if (selectionChanged) {scrollEditorIntoView(); restartBlink();}
1419 1419
1420 1420 // updateInput can be set to a boolean value to force/prevent an
1421 1421 // update.
1422 1422 if (focused && !leaveInputAlone &&
1423 1423 (updateInput === true || (updateInput !== false && selectionChanged)))
1424 1424 prepareInput();
1425 1425
1426 1426 if (selectionChanged && options.matchBrackets)
1427 1427 setTimeout(operation(function() {
1428 1428 if (bracketHighlighted) {bracketHighlighted(); bracketHighlighted = null;}
1429 1429 matchBrackets(false);
1430 1430 }), 20);
1431 1431 var tc = textChanged; // textChanged can be reset by cursoractivity callback
1432 1432 if (selectionChanged && options.onCursorActivity)
1433 1433 options.onCursorActivity(instance);
1434 1434 if (tc && options.onChange && instance)
1435 1435 options.onChange(instance, tc);
1436 1436 }
1437 1437 var nestedOperation = 0;
1438 1438 function operation(f) {
1439 1439 return function() {
1440 1440 if (!nestedOperation++) startOperation();
1441 1441 try {var result = f.apply(this, arguments);}
1442 1442 finally {if (!--nestedOperation) endOperation();}
1443 1443 return result;
1444 1444 };
1445 1445 }
1446 1446
1447 1447 function SearchCursor(query, pos, caseFold) {
1448 1448 this.atOccurrence = false;
1449 1449 if (caseFold == null) caseFold = typeof query == "string" && query == query.toLowerCase();
1450 1450
1451 1451 if (pos && typeof pos == "object") pos = clipPos(pos);
1452 1452 else pos = {line: 0, ch: 0};
1453 1453 this.pos = {from: pos, to: pos};
1454 1454
1455 1455 // The matches method is filled in based on the type of query.
1456 1456 // It takes a position and a direction, and returns an object
1457 1457 // describing the next occurrence of the query, or null if no
1458 1458 // more matches were found.
1459 1459 if (typeof query != "string") // Regexp match
1460 1460 this.matches = function(reverse, pos) {
1461 1461 if (reverse) {
1462 1462 var line = lines[pos.line].text.slice(0, pos.ch), match = line.match(query), start = 0;
1463 1463 while (match) {
1464 1464 var ind = line.indexOf(match[0]);
1465 1465 start += ind;
1466 1466 line = line.slice(ind + 1);
1467 1467 var newmatch = line.match(query);
1468 1468 if (newmatch) match = newmatch;
1469 1469 else break;
1470 1470 start++;
1471 1471 }
1472 1472 }
1473 1473 else {
1474 1474 var line = lines[pos.line].text.slice(pos.ch), match = line.match(query),
1475 1475 start = match && pos.ch + line.indexOf(match[0]);
1476 1476 }
1477 1477 if (match)
1478 1478 return {from: {line: pos.line, ch: start},
1479 1479 to: {line: pos.line, ch: start + match[0].length},
1480 1480 match: match};
1481 1481 };
1482 1482 else { // String query
1483 1483 if (caseFold) query = query.toLowerCase();
1484 1484 var fold = caseFold ? function(str){return str.toLowerCase();} : function(str){return str;};
1485 1485 var target = query.split("\n");
1486 1486 // Different methods for single-line and multi-line queries
1487 1487 if (target.length == 1)
1488 1488 this.matches = function(reverse, pos) {
1489 1489 var line = fold(lines[pos.line].text), len = query.length, match;
1490 1490 if (reverse ? (pos.ch >= len && (match = line.lastIndexOf(query, pos.ch - len)) != -1)
1491 1491 : (match = line.indexOf(query, pos.ch)) != -1)
1492 1492 return {from: {line: pos.line, ch: match},
1493 1493 to: {line: pos.line, ch: match + len}};
1494 1494 };
1495 1495 else
1496 1496 this.matches = function(reverse, pos) {
1497 1497 var ln = pos.line, idx = (reverse ? target.length - 1 : 0), match = target[idx], line = fold(lines[ln].text);
1498 1498 var offsetA = (reverse ? line.indexOf(match) + match.length : line.lastIndexOf(match));
1499 1499 if (reverse ? offsetA >= pos.ch || offsetA != match.length
1500 1500 : offsetA <= pos.ch || offsetA != line.length - match.length)
1501 1501 return;
1502 1502 for (;;) {
1503 1503 if (reverse ? !ln : ln == lines.length - 1) return;
1504 1504 line = fold(lines[ln += reverse ? -1 : 1].text);
1505 1505 match = target[reverse ? --idx : ++idx];
1506 1506 if (idx > 0 && idx < target.length - 1) {
1507 1507 if (line != match) return;
1508 1508 else continue;
1509 1509 }
1510 1510 var offsetB = (reverse ? line.lastIndexOf(match) : line.indexOf(match) + match.length);
1511 1511 if (reverse ? offsetB != line.length - match.length : offsetB != match.length)
1512 1512 return;
1513 1513 var start = {line: pos.line, ch: offsetA}, end = {line: ln, ch: offsetB};
1514 1514 return {from: reverse ? end : start, to: reverse ? start : end};
1515 1515 }
1516 1516 };
1517 1517 }
1518 1518 }
1519 1519
1520 1520 SearchCursor.prototype = {
1521 1521 findNext: function() {return this.find(false);},
1522 1522 findPrevious: function() {return this.find(true);},
1523 1523
1524 1524 find: function(reverse) {
1525 1525 var self = this, pos = clipPos(reverse ? this.pos.from : this.pos.to);
1526 1526 function savePosAndFail(line) {
1527 1527 var pos = {line: line, ch: 0};
1528 1528 self.pos = {from: pos, to: pos};
1529 1529 self.atOccurrence = false;
1530 1530 return false;
1531 1531 }
1532 1532
1533 1533 for (;;) {
1534 1534 if (this.pos = this.matches(reverse, pos)) {
1535 1535 this.atOccurrence = true;
1536 1536 return this.pos.match || true;
1537 1537 }
1538 1538 if (reverse) {
1539 1539 if (!pos.line) return savePosAndFail(0);
1540 1540 pos = {line: pos.line-1, ch: lines[pos.line-1].text.length};
1541 1541 }
1542 1542 else {
1543 1543 if (pos.line == lines.length - 1) return savePosAndFail(lines.length);
1544 1544 pos = {line: pos.line+1, ch: 0};
1545 1545 }
1546 1546 }
1547 1547 },
1548 1548
1549 1549 from: function() {if (this.atOccurrence) return copyPos(this.pos.from);},
1550 1550 to: function() {if (this.atOccurrence) return copyPos(this.pos.to);},
1551 1551
1552 1552 replace: function(newText) {
1553 1553 var self = this;
1554 1554 if (this.atOccurrence)
1555 1555 operation(function() {
1556 1556 self.pos.to = replaceRange(newText, self.pos.from, self.pos.to);
1557 1557 })();
1558 1558 }
1559 1559 };
1560 1560
1561 1561 for (var ext in extensions)
1562 1562 if (extensions.propertyIsEnumerable(ext) &&
1563 1563 !instance.propertyIsEnumerable(ext))
1564 1564 instance[ext] = extensions[ext];
1565 1565 return instance;
1566 1566 } // (end of function CodeMirror)
1567 1567
1568 1568 // The default configuration options.
1569 1569 CodeMirror.defaults = {
1570 1570 value: "",
1571 1571 mode: null,
1572 1572 theme: "default",
1573 1573 indentUnit: 2,
1574 1574 indentWithTabs: false,
1575 1575 tabMode: "classic",
1576 1576 enterMode: "indent",
1577 1577 electricChars: true,
1578 1578 onKeyEvent: null,
1579 1579 lineNumbers: false,
1580 1580 gutter: false,
1581 1581 fixedGutter: false,
1582 1582 firstLineNumber: 1,
1583 1583 readOnly: false,
1584 1584 smartHome: true,
1585 1585 onChange: null,
1586 1586 onCursorActivity: null,
1587 1587 onGutterClick: null,
1588 1588 onHighlightComplete: null,
1589 1589 onFocus: null, onBlur: null, onScroll: null,
1590 1590 matchBrackets: false,
1591 1591 workTime: 100,
1592 1592 workDelay: 200,
1593 1593 undoDepth: 40,
1594 1594 tabindex: null,
1595 1595 document: window.document
1596 1596 };
1597 1597
1598 1598 // Known modes, by name and by MIME
1599 1599 var modes = {}, mimeModes = {};
1600 1600 CodeMirror.defineMode = function(name, mode) {
1601 1601 if (!CodeMirror.defaults.mode && name != "null") CodeMirror.defaults.mode = name;
1602 1602 modes[name] = mode;
1603 1603 };
1604 1604 CodeMirror.defineMIME = function(mime, spec) {
1605 1605 mimeModes[mime] = spec;
1606 1606 };
1607 1607 CodeMirror.getMode = function(options, spec) {
1608 1608 if (typeof spec == "string" && mimeModes.hasOwnProperty(spec))
1609 1609 spec = mimeModes[spec];
1610 1610 if (typeof spec == "string")
1611 1611 var mname = spec, config = {};
1612 1612 else if (spec != null)
1613 1613 var mname = spec.name, config = spec;
1614 1614 var mfactory = modes[mname];
1615 1615 if (!mfactory) {
1616 1616 if (window.console) console.warn("No mode " + mname + " found, falling back to plain text.");
1617 1617 return CodeMirror.getMode(options, "text/plain");
1618 1618 }
1619 1619 return mfactory(options, config || {});
1620 1620 };
1621 1621 CodeMirror.listModes = function() {
1622 1622 var list = [];
1623 1623 for (var m in modes)
1624 1624 if (modes.propertyIsEnumerable(m)) list.push(m);
1625 1625 return list;
1626 1626 };
1627 1627 CodeMirror.listMIMEs = function() {
1628 1628 var list = [];
1629 1629 for (var m in mimeModes)
1630 1630 if (mimeModes.propertyIsEnumerable(m)) list.push({mime: m, mode: mimeModes[m]});
1631 1631 return list;
1632 1632 };
1633 1633
1634 1634 var extensions = {};
1635 1635 CodeMirror.defineExtension = function(name, func) {
1636 1636 extensions[name] = func;
1637 1637 };
1638 1638
1639 1639 CodeMirror.fromTextArea = function(textarea, options) {
1640 1640 if (!options) options = {};
1641 1641 options.value = textarea.value;
1642 1642 if (!options.tabindex && textarea.tabindex)
1643 1643 options.tabindex = textarea.tabindex;
1644 1644
1645 1645 function save() {textarea.value = instance.getValue();}
1646 1646 if (textarea.form) {
1647 1647 // Deplorable hack to make the submit method do the right thing.
1648 1648 var rmSubmit = connect(textarea.form, "submit", save, true);
1649 1649 if (typeof textarea.form.submit == "function") {
1650 1650 var realSubmit = textarea.form.submit;
1651 1651 function wrappedSubmit() {
1652 1652 save();
1653 1653 textarea.form.submit = realSubmit;
1654 1654 textarea.form.submit();
1655 1655 textarea.form.submit = wrappedSubmit;
1656 1656 }
1657 1657 textarea.form.submit = wrappedSubmit;
1658 1658 }
1659 1659 }
1660 1660
1661 1661 textarea.style.display = "none";
1662 1662 var instance = CodeMirror(function(node) {
1663 1663 textarea.parentNode.insertBefore(node, textarea.nextSibling);
1664 1664 }, options);
1665 1665 instance.save = save;
1666 1666 instance.toTextArea = function() {
1667 1667 save();
1668 1668 textarea.parentNode.removeChild(instance.getWrapperElement());
1669 1669 textarea.style.display = "";
1670 1670 if (textarea.form) {
1671 1671 rmSubmit();
1672 1672 if (typeof textarea.form.submit == "function")
1673 1673 textarea.form.submit = realSubmit;
1674 1674 }
1675 1675 };
1676 1676 return instance;
1677 1677 };
1678 1678
1679 1679 // Utility functions for working with state. Exported because modes
1680 1680 // sometimes need to do this.
1681 1681 function copyState(mode, state) {
1682 1682 if (state === true) return state;
1683 1683 if (mode.copyState) return mode.copyState(state);
1684 1684 var nstate = {};
1685 1685 for (var n in state) {
1686 1686 var val = state[n];
1687 1687 if (val instanceof Array) val = val.concat([]);
1688 1688 nstate[n] = val;
1689 1689 }
1690 1690 return nstate;
1691 1691 }
1692 1692 CodeMirror.startState = startState;
1693 1693 function startState(mode, a1, a2) {
1694 1694 return mode.startState ? mode.startState(a1, a2) : true;
1695 1695 }
1696 1696 CodeMirror.copyState = copyState;
1697 1697
1698 1698 // The character stream used by a mode's parser.
1699 1699 function StringStream(string) {
1700 1700 this.pos = this.start = 0;
1701 1701 this.string = string;
1702 1702 }
1703 1703 StringStream.prototype = {
1704 1704 eol: function() {return this.pos >= this.string.length;},
1705 1705 sol: function() {return this.pos == 0;},
1706 1706 peek: function() {return this.string.charAt(this.pos);},
1707 1707 next: function() {
1708 1708 if (this.pos < this.string.length)
1709 1709 return this.string.charAt(this.pos++);
1710 1710 },
1711 1711 eat: function(match) {
1712 1712 var ch = this.string.charAt(this.pos);
1713 1713 if (typeof match == "string") var ok = ch == match;
1714 1714 else var ok = ch && (match.test ? match.test(ch) : match(ch));
1715 1715 if (ok) {++this.pos; return ch;}
1716 1716 },
1717 1717 eatWhile: function(match) {
1718 1718 var start = this.pos;
1719 1719 while (this.eat(match)){}
1720 1720 return this.pos > start;
1721 1721 },
1722 1722 eatSpace: function() {
1723 1723 var start = this.pos;
1724 1724 while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) ++this.pos;
1725 1725 return this.pos > start;
1726 1726 },
1727 1727 skipToEnd: function() {this.pos = this.string.length;},
1728 1728 skipTo: function(ch) {
1729 1729 var found = this.string.indexOf(ch, this.pos);
1730 1730 if (found > -1) {this.pos = found; return true;}
1731 1731 },
1732 1732 backUp: function(n) {this.pos -= n;},
1733 1733 column: function() {return countColumn(this.string, this.start);},
1734 1734 indentation: function() {return countColumn(this.string);},
1735 1735 match: function(pattern, consume, caseInsensitive) {
1736 1736 if (typeof pattern == "string") {
1737 1737 function cased(str) {return caseInsensitive ? str.toLowerCase() : str;}
1738 1738 if (cased(this.string).indexOf(cased(pattern), this.pos) == this.pos) {
1739 1739 if (consume !== false) this.pos += pattern.length;
1740 1740 return true;
1741 1741 }
1742 1742 }
1743 1743 else {
1744 1744 var match = this.string.slice(this.pos).match(pattern);
1745 1745 if (match && consume !== false) this.pos += match[0].length;
1746 1746 return match;
1747 1747 }
1748 1748 },
1749 1749 current: function(){return this.string.slice(this.start, this.pos);}
1750 1750 };
1751 1751 CodeMirror.StringStream = StringStream;
1752 1752
1753 1753 // Line objects. These hold state related to a line, including
1754 1754 // highlighting info (the styles array).
1755 1755 function Line(text, styles) {
1756 1756 this.styles = styles || [text, null];
1757 1757 this.stateAfter = null;
1758 1758 this.text = text;
1759 1759 this.marked = this.gutterMarker = this.className = null;
1760 1760 }
1761 1761 Line.prototype = {
1762 1762 // Replace a piece of a line, keeping the styles around it intact.
1763 1763 replace: function(from, to, text) {
1764 1764 var st = [], mk = this.marked;
1765 1765 copyStyles(0, from, this.styles, st);
1766 1766 if (text) st.push(text, null);
1767 1767 copyStyles(to, this.text.length, this.styles, st);
1768 1768 this.styles = st;
1769 1769 this.text = this.text.slice(0, from) + text + this.text.slice(to);
1770 1770 this.stateAfter = null;
1771 1771 if (mk) {
1772 1772 var diff = text.length - (to - from), end = this.text.length;
1773 1773 function fix(n) {return n <= Math.min(to, to + diff) ? n : n + diff;}
1774 1774 for (var i = 0; i < mk.length; ++i) {
1775 1775 var mark = mk[i], del = false;
1776 1776 if (mark.from >= end) del = true;
1777 1777 else {mark.from = fix(mark.from); if (mark.to != null) mark.to = fix(mark.to);}
1778 1778 if (del || mark.from >= mark.to) {mk.splice(i, 1); i--;}
1779 1779 }
1780 1780 }
1781 1781 },
1782 1782 // Split a line in two, again keeping styles intact.
1783 1783 split: function(pos, textBefore) {
1784 1784 var st = [textBefore, null];
1785 1785 copyStyles(pos, this.text.length, this.styles, st);
1786 1786 return new Line(textBefore + this.text.slice(pos), st);
1787 1787 },
1788 1788 addMark: function(from, to, style) {
1789 1789 var mk = this.marked, mark = {from: from, to: to, style: style};
1790 1790 if (this.marked == null) this.marked = [];
1791 1791 this.marked.push(mark);
1792 1792 this.marked.sort(function(a, b){return a.from - b.from;});
1793 1793 return mark;
1794 1794 },
1795 1795 removeMark: function(mark) {
1796 1796 var mk = this.marked;
1797 1797 if (!mk) return;
1798 1798 for (var i = 0; i < mk.length; ++i)
1799 1799 if (mk[i] == mark) {mk.splice(i, 1); break;}
1800 1800 },
1801 1801 // Run the given mode's parser over a line, update the styles
1802 1802 // array, which contains alternating fragments of text and CSS
1803 1803 // classes.
1804 1804 highlight: function(mode, state) {
1805 1805 var stream = new StringStream(this.text), st = this.styles, pos = 0;
1806 1806 var changed = false, curWord = st[0], prevWord;
1807 1807 if (this.text == "" && mode.blankLine) mode.blankLine(state);
1808 1808 while (!stream.eol()) {
1809 1809 var style = mode.token(stream, state);
1810 1810 var substr = this.text.slice(stream.start, stream.pos);
1811 1811 stream.start = stream.pos;
1812 1812 if (pos && st[pos-1] == style)
1813 1813 st[pos-2] += substr;
1814 1814 else if (substr) {
1815 1815 if (!changed && (st[pos+1] != style || (pos && st[pos-2] != prevWord))) changed = true;
1816 1816 st[pos++] = substr; st[pos++] = style;
1817 1817 prevWord = curWord; curWord = st[pos];
1818 1818 }
1819 1819 // Give up when line is ridiculously long
1820 1820 if (stream.pos > 5000) {
1821 1821 st[pos++] = this.text.slice(stream.pos); st[pos++] = null;
1822 1822 break;
1823 1823 }
1824 1824 }
1825 1825 if (st.length != pos) {st.length = pos; changed = true;}
1826 1826 if (pos && st[pos-2] != prevWord) changed = true;
1827 1827 // Short lines with simple highlights return null, and are
1828 1828 // counted as changed by the driver because they are likely to
1829 1829 // highlight the same way in various contexts.
1830 1830 return changed || (st.length < 5 && this.text.length < 10 ? null : false);
1831 1831 },
1832 1832 // Fetch the parser token for a given character. Useful for hacks
1833 1833 // that want to inspect the mode state (say, for completion).
1834 1834 getTokenAt: function(mode, state, ch) {
1835 1835 var txt = this.text, stream = new StringStream(txt);
1836 1836 while (stream.pos < ch && !stream.eol()) {
1837 1837 stream.start = stream.pos;
1838 1838 var style = mode.token(stream, state);
1839 1839 }
1840 1840 return {start: stream.start,
1841 1841 end: stream.pos,
1842 1842 string: stream.current(),
1843 1843 className: style || null,
1844 1844 state: state};
1845 1845 },
1846 1846 indentation: function() {return countColumn(this.text);},
1847 1847 // Produces an HTML fragment for the line, taking selection,
1848 1848 // marking, and highlighting into account.
1849 1849 getHTML: function(sfrom, sto, includePre, endAt) {
1850 1850 var html = [];
1851 1851 if (includePre)
1852 1852 html.push(this.className ? '<pre class="' + this.className + '">': "<pre>");
1853 1853 function span(text, style) {
1854 1854 if (!text) return;
1855 1855 if (style) html.push('<span class="', style, '">', htmlEscape(text), "</span>");
1856 1856 else html.push(htmlEscape(text));
1857 1857 }
1858 1858 var st = this.styles, allText = this.text, marked = this.marked;
1859 1859 if (sfrom == sto) sfrom = null;
1860 1860 var len = allText.length;
1861 1861 if (endAt != null) len = Math.min(endAt, len);
1862 1862
1863 1863 if (!allText && endAt == null)
1864 1864 span(" ", sfrom != null && sto == null ? "CodeMirror-selected" : null);
1865 1865 else if (!marked && sfrom == null)
1866 1866 for (var i = 0, ch = 0; ch < len; i+=2) {
1867 1867 var str = st[i], l = str.length;
1868 1868 if (ch + l > len) str = str.slice(0, len - ch);
1869 1869 ch += l;
1870 1870 span(str, "cm-" + st[i+1]);
1871 1871 }
1872 1872 else {
1873 1873 var pos = 0, i = 0, text = "", style, sg = 0;
1874 1874 var markpos = -1, mark = null;
1875 1875 function nextMark() {
1876 1876 if (marked) {
1877 1877 markpos += 1;
1878 1878 mark = (markpos < marked.length) ? marked[markpos] : null;
1879 1879 }
1880 1880 }
1881 1881 nextMark();
1882 1882 while (pos < len) {
1883 1883 var upto = len;
1884 1884 var extraStyle = "";
1885 1885 if (sfrom != null) {
1886 1886 if (sfrom > pos) upto = sfrom;
1887 1887 else if (sto == null || sto > pos) {
1888 1888 extraStyle = " CodeMirror-selected";
1889 1889 if (sto != null) upto = Math.min(upto, sto);
1890 1890 }
1891 1891 }
1892 1892 while (mark && mark.to != null && mark.to <= pos) nextMark();
1893 1893 if (mark) {
1894 1894 if (mark.from > pos) upto = Math.min(upto, mark.from);
1895 1895 else {
1896 1896 extraStyle += " " + mark.style;
1897 1897 if (mark.to != null) upto = Math.min(upto, mark.to);
1898 1898 }
1899 1899 }
1900 1900 for (;;) {
1901 1901 var end = pos + text.length;
1902 1902 var appliedStyle = style;
1903 1903 if (extraStyle) appliedStyle = style ? style + extraStyle : extraStyle;
1904 1904 span(end > upto ? text.slice(0, upto - pos) : text, appliedStyle);
1905 1905 if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;}
1906 1906 pos = end;
1907 1907 text = st[i++]; style = "cm-" + st[i++];
1908 1908 }
1909 1909 }
1910 1910 if (sfrom != null && sto == null) span(" ", "CodeMirror-selected");
1911 1911 }
1912 1912 if (includePre) html.push("</pre>");
1913 1913 return html.join("");
1914 1914 }
1915 1915 };
1916 1916 // Utility used by replace and split above
1917 1917 function copyStyles(from, to, source, dest) {
1918 1918 for (var i = 0, pos = 0, state = 0; pos < to; i+=2) {
1919 1919 var part = source[i], end = pos + part.length;
1920 1920 if (state == 0) {
1921 1921 if (end > from) dest.push(part.slice(from - pos, Math.min(part.length, to - pos)), source[i+1]);
1922 1922 if (end >= from) state = 1;
1923 1923 }
1924 1924 else if (state == 1) {
1925 1925 if (end > to) dest.push(part.slice(0, to - pos), source[i+1]);
1926 1926 else dest.push(part, source[i+1]);
1927 1927 }
1928 1928 pos = end;
1929 1929 }
1930 1930 }
1931 1931
1932 1932 // The history object 'chunks' changes that are made close together
1933 1933 // and at almost the same time into bigger undoable units.
1934 1934 function History() {
1935 1935 this.time = 0;
1936 1936 this.done = []; this.undone = [];
1937 1937 }
1938 1938 History.prototype = {
1939 1939 addChange: function(start, added, old) {
1940 1940 this.undone.length = 0;
1941 1941 var time = +new Date, last = this.done[this.done.length - 1];
1942 1942 if (time - this.time > 400 || !last ||
1943 1943 last.start > start + added || last.start + last.added < start - last.added + last.old.length)
1944 1944 this.done.push({start: start, added: added, old: old});
1945 1945 else {
1946 1946 var oldoff = 0;
1947 1947 if (start < last.start) {
1948 1948 for (var i = last.start - start - 1; i >= 0; --i)
1949 1949 last.old.unshift(old[i]);
1950 1950 last.added += last.start - start;
1951 1951 last.start = start;
1952 1952 }
1953 1953 else if (last.start < start) {
1954 1954 oldoff = start - last.start;
1955 1955 added += oldoff;
1956 1956 }
1957 1957 for (var i = last.added - oldoff, e = old.length; i < e; ++i)
1958 1958 last.old.push(old[i]);
1959 1959 if (last.added < added) last.added = added;
1960 1960 }
1961 1961 this.time = time;
1962 1962 }
1963 1963 };
1964 1964
1965 1965 function stopMethod() {e_stop(this);}
1966 1966 // Ensure an event has a stop method.
1967 1967 function addStop(event) {
1968 1968 if (!event.stop) event.stop = stopMethod;
1969 1969 return event;
1970 1970 }
1971 1971
1972 1972 function e_preventDefault(e) {
1973 1973 if (e.preventDefault) e.preventDefault();
1974 1974 else e.returnValue = false;
1975 1975 }
1976 1976 function e_stopPropagation(e) {
1977 1977 if (e.stopPropagation) e.stopPropagation();
1978 1978 else e.cancelBubble = true;
1979 1979 }
1980 1980 function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);}
1981 1981 function e_target(e) {return e.target || e.srcElement;}
1982 1982 function e_button(e) {
1983 1983 if (e.which) return e.which;
1984 1984 else if (e.button & 1) return 1;
1985 1985 else if (e.button & 2) return 3;
1986 1986 else if (e.button & 4) return 2;
1987 1987 }
1988 1988
1989 1989 // Event handler registration. If disconnect is true, it'll return a
1990 1990 // function that unregisters the handler.
1991 1991 function connect(node, type, handler, disconnect) {
1992 1992 function wrapHandler(event) {handler(event || window.event);}
1993 1993 if (typeof node.addEventListener == "function") {
1994 1994 node.addEventListener(type, wrapHandler, false);
1995 1995 if (disconnect) return function() {node.removeEventListener(type, wrapHandler, false);};
1996 1996 }
1997 1997 else {
1998 1998 node.attachEvent("on" + type, wrapHandler);
1999 1999 if (disconnect) return function() {node.detachEvent("on" + type, wrapHandler);};
2000 2000 }
2001 2001 }
2002 2002
2003 2003 function Delayed() {this.id = null;}
2004 2004 Delayed.prototype = {set: function(ms, f) {clearTimeout(this.id); this.id = setTimeout(f, ms);}};
2005 2005
2006 2006 // Some IE versions don't preserve whitespace when setting the
2007 2007 // innerHTML of a PRE tag.
2008 2008 var badInnerHTML = (function() {
2009 2009 var pre = document.createElement("pre");
2010 2010 pre.innerHTML = " "; return !pre.innerHTML;
2011 2011 })();
2012 2012
2013 2013 // Detect drag-and-drop
2014 2014 var dragAndDrop = (function() {
2015 2015 // IE8 has ondragstart and ondrop properties, but doesn't seem to
2016 2016 // actually support ondragstart the way it's supposed to work.
2017 2017 if (/MSIE [1-8]\b/.test(navigator.userAgent)) return false;
2018 2018 var div = document.createElement('div');
2019 2019 return "ondragstart" in div && "ondrop" in div;
2020 2020 })();
2021 2021
2022 2022 var gecko = /gecko\/\d{7}/i.test(navigator.userAgent);
2023 2023 var ie = /MSIE \d/.test(navigator.userAgent);
2024 2024 var safari = /Apple Computer/.test(navigator.vendor);
2025 2025
2026 2026 var lineSep = "\n";
2027 2027 // Feature-detect whether newlines in textareas are converted to \r\n
2028 2028 (function () {
2029 2029 var te = document.createElement("textarea");
2030 2030 te.value = "foo\nbar";
2031 2031 if (te.value.indexOf("\r") > -1) lineSep = "\r\n";
2032 2032 }());
2033 2033
2034 2034 var tabSize = 8;
2035 2035 var mac = /Mac/.test(navigator.platform);
2036 2036 var movementKeys = {};
2037 2037 for (var i = 35; i <= 40; ++i)
2038 2038 movementKeys[i] = movementKeys["c" + i] = true;
2039 2039
2040 2040 // Counts the column offset in a string, taking tabs into account.
2041 2041 // Used mostly to find indentation.
2042 2042 function countColumn(string, end) {
2043 2043 if (end == null) {
2044 2044 end = string.search(/[^\s\u00a0]/);
2045 2045 if (end == -1) end = string.length;
2046 2046 }
2047 2047 for (var i = 0, n = 0; i < end; ++i) {
2048 2048 if (string.charAt(i) == "\t") n += tabSize - (n % tabSize);
2049 2049 else ++n;
2050 2050 }
2051 2051 return n;
2052 2052 }
2053 2053
2054 2054 function computedStyle(elt) {
2055 2055 if (elt.currentStyle) return elt.currentStyle;
2056 2056 return window.getComputedStyle(elt, null);
2057 2057 }
2058 2058 // Find the position of an element by following the offsetParent chain.
2059 2059 // If screen==true, it returns screen (rather than page) coordinates.
2060 2060 function eltOffset(node, screen) {
2061 2061 var doc = node.ownerDocument.body;
2062 2062 var x = 0, y = 0, skipDoc = false;
2063 2063 for (var n = node; n; n = n.offsetParent) {
2064 2064 x += n.offsetLeft; y += n.offsetTop;
2065 2065 if (screen && computedStyle(n).position == "fixed")
2066 2066 skipDoc = true;
2067 2067 }
2068 2068 var e = screen && !skipDoc ? null : doc;
2069 2069 for (var n = node.parentNode; n != e; n = n.parentNode)
2070 2070 if (n.scrollLeft != null) { x -= n.scrollLeft; y -= n.scrollTop;}
2071 2071 return {left: x, top: y};
2072 2072 }
2073 2073 // Get a node's text content.
2074 2074 function eltText(node) {
2075 2075 return node.textContent || node.innerText || node.nodeValue || "";
2076 2076 }
2077 2077
2078 2078 // Operations on {line, ch} objects.
2079 2079 function posEq(a, b) {return a.line == b.line && a.ch == b.ch;}
2080 2080 function posLess(a, b) {return a.line < b.line || (a.line == b.line && a.ch < b.ch);}
2081 2081 function copyPos(x) {return {line: x.line, ch: x.ch};}
2082 2082
2083 2083 var escapeElement = document.createElement("div");
2084 2084 function htmlEscape(str) {
2085 2085 escapeElement.innerText = escapeElement.textContent = str;
2086 2086 return escapeElement.innerHTML;
2087 2087 }
2088 2088 CodeMirror.htmlEscape = htmlEscape;
2089 2089
2090 2090 // Used to position the cursor after an undo/redo by finding the
2091 2091 // last edited character.
2092 2092 function editEnd(from, to) {
2093 2093 if (!to) return from ? from.length : 0;
2094 2094 if (!from) return to.length;
2095 2095 for (var i = from.length, j = to.length; i >= 0 && j >= 0; --i, --j)
2096 2096 if (from.charAt(i) != to.charAt(j)) break;
2097 2097 return j + 1;
2098 2098 }
2099 2099
2100 2100 function indexOf(collection, elt) {
2101 2101 if (collection.indexOf) return collection.indexOf(elt);
2102 2102 for (var i = 0, e = collection.length; i < e; ++i)
2103 2103 if (collection[i] == elt) return i;
2104 2104 return -1;
2105 2105 }
2106 2106
2107 2107 // See if "".split is the broken IE version, if so, provide an
2108 2108 // alternative way to split lines.
2109 2109 var splitLines, selRange, setSelRange;
2110 2110 if ("\n\nb".split(/\n/).length != 3)
2111 2111 splitLines = function(string) {
2112 2112 var pos = 0, nl, result = [];
2113 2113 while ((nl = string.indexOf("\n", pos)) > -1) {
2114 2114 result.push(string.slice(pos, string.charAt(nl-1) == "\r" ? nl - 1 : nl));
2115 2115 pos = nl + 1;
2116 2116 }
2117 2117 result.push(string.slice(pos));
2118 2118 return result;
2119 2119 };
2120 2120 else
2121 2121 splitLines = function(string){return string.split(/\r?\n/);};
2122 2122 CodeMirror.splitLines = splitLines;
2123 2123
2124 2124 // Sane model of finding and setting the selection in a textarea
2125 2125 if (window.getSelection) {
2126 2126 selRange = function(te) {
2127 2127 try {return {start: te.selectionStart, end: te.selectionEnd};}
2128 2128 catch(e) {return null;}
2129 2129 };
2130 2130 if (safari)
2131 2131 // On Safari, selection set with setSelectionRange are in a sort
2132 2132 // of limbo wrt their anchor. If you press shift-left in them,
2133 2133 // the anchor is put at the end, and the selection expanded to
2134 2134 // the left. If you press shift-right, the anchor ends up at the
2135 2135 // front. This is not what CodeMirror wants, so it does a
2136 2136 // spurious modify() call to get out of limbo.
2137 2137 setSelRange = function(te, start, end) {
2138 2138 if (start == end)
2139 2139 te.setSelectionRange(start, end);
2140 2140 else {
2141 2141 te.setSelectionRange(start, end - 1);
2142 2142 window.getSelection().modify("extend", "forward", "character");
2143 2143 }
2144 2144 };
2145 2145 else
2146 2146 setSelRange = function(te, start, end) {
2147 2147 try {te.setSelectionRange(start, end);}
2148 2148 catch(e) {} // Fails on Firefox when textarea isn't part of the document
2149 2149 };
2150 2150 }
2151 2151 // IE model. Don't ask.
2152 2152 else {
2153 2153 selRange = function(te) {
2154 2154 try {var range = te.ownerDocument.selection.createRange();}
2155 2155 catch(e) {return null;}
2156 2156 if (!range || range.parentElement() != te) return null;
2157 2157 var val = te.value, len = val.length, localRange = te.createTextRange();
2158 2158 localRange.moveToBookmark(range.getBookmark());
2159 2159 var endRange = te.createTextRange();
2160 2160 endRange.collapse(false);
2161 2161
2162 2162 if (localRange.compareEndPoints("StartToEnd", endRange) > -1)
2163 2163 return {start: len, end: len};
2164 2164
2165 2165 var start = -localRange.moveStart("character", -len);
2166 2166 for (var i = val.indexOf("\r"); i > -1 && i < start; i = val.indexOf("\r", i+1), start++) {}
2167 2167
2168 2168 if (localRange.compareEndPoints("EndToEnd", endRange) > -1)
2169 2169 return {start: start, end: len};
2170 2170
2171 2171 var end = -localRange.moveEnd("character", -len);
2172 2172 for (var i = val.indexOf("\r"); i > -1 && i < end; i = val.indexOf("\r", i+1), end++) {}
2173 2173 return {start: start, end: end};
2174 2174 };
2175 2175 setSelRange = function(te, start, end) {
2176 2176 var range = te.createTextRange();
2177 2177 range.collapse(true);
2178 2178 var endrange = range.duplicate();
2179 2179 var newlines = 0, txt = te.value;
2180 2180 for (var pos = txt.indexOf("\n"); pos > -1 && pos < start; pos = txt.indexOf("\n", pos + 1))
2181 2181 ++newlines;
2182 2182 range.move("character", start - newlines);
2183 2183 for (; pos > -1 && pos < end; pos = txt.indexOf("\n", pos + 1))
2184 2184 ++newlines;
2185 2185 endrange.move("character", end - newlines);
2186 2186 range.setEndPoint("EndToEnd", endrange);
2187 2187 range.select();
2188 2188 };
2189 2189 }
2190 2190
2191 2191 CodeMirror.defineMode("null", function() {
2192 2192 return {token: function(stream) {stream.skipToEnd();}};
2193 2193 });
2194 2194 CodeMirror.defineMIME("text/plain", "null");
2195 2195
2196 2196 return CodeMirror;
2197 2197 })();
@@ -1,51 +1,51
1 1 // Utility function that allows modes to be combined. The mode given
2 2 // as the base argument takes care of most of the normal mode
3 3 // functionality, but a second (typically simple) mode is used, which
4 4 // can override the style of text. Both modes get to parse all of the
5 5 // text, but when both assign a non-null style to a piece of code, the
6 6 // overlay wins, unless the combine argument was true, in which case
7 7 // the styles are combined.
8 8
9 9 CodeMirror.overlayParser = function(base, overlay, combine) {
10 10 return {
11 11 startState: function() {
12 12 return {
13 13 base: CodeMirror.startState(base),
14 14 overlay: CodeMirror.startState(overlay),
15 15 basePos: 0, baseCur: null,
16 16 overlayPos: 0, overlayCur: null
17 17 };
18 18 },
19 19 copyState: function(state) {
20 20 return {
21 21 base: CodeMirror.copyState(base, state.base),
22 22 overlay: CodeMirror.copyState(overlay, state.overlay),
23 23 basePos: state.basePos, baseCur: null,
24 24 overlayPos: state.overlayPos, overlayCur: null
25 25 };
26 26 },
27 27
28 28 token: function(stream, state) {
29 29 if (stream.start == state.basePos) {
30 30 state.baseCur = base.token(stream, state.base);
31 31 state.basePos = stream.pos;
32 32 }
33 33 if (stream.start == state.overlayPos) {
34 34 stream.pos = stream.start;
35 35 state.overlayCur = overlay.token(stream, state.overlay);
36 36 state.overlayPos = stream.pos;
37 37 }
38 38 stream.pos = Math.min(state.basePos, state.overlayPos);
39 39 if (stream.eol()) state.basePos = state.overlayPos = 0;
40 40
41 41 if (state.overlayCur == null) return state.baseCur;
42 42 if (state.baseCur != null && combine) return state.baseCur + " " + state.overlayCur;
43 43 else return state.overlayCur;
44 44 },
45
45
46 46 indent: function(state, textAfter) {
47 47 return base.indent(state.base, textAfter);
48 48 },
49 49 electricChars: base.electricChars
50 50 };
51 51 };
1 NO CONTENT: file renamed from IPython/frontend/html/notebook/static/codemirror-2.12/lib/runmode.js to IPython/frontend/html/notebook/static/codemirror/lib/runmode.js
1 NO CONTENT: file renamed from IPython/frontend/html/notebook/static/codemirror-2.12/mode/css/css.js to IPython/frontend/html/notebook/static/codemirror/mode/css/css.js
1 NO CONTENT: file renamed from IPython/frontend/html/notebook/static/codemirror-2.12/mode/css/index.html to IPython/frontend/html/notebook/static/codemirror/mode/css/index.html
1 NO CONTENT: file renamed from IPython/frontend/html/notebook/static/codemirror-2.12/mode/htmlmixed/htmlmixed.js to IPython/frontend/html/notebook/static/codemirror/mode/htmlmixed/htmlmixed.js
1 NO CONTENT: file renamed from IPython/frontend/html/notebook/static/codemirror-2.12/mode/htmlmixed/index.html to IPython/frontend/html/notebook/static/codemirror/mode/htmlmixed/index.html
1 NO CONTENT: file renamed from IPython/frontend/html/notebook/static/codemirror-2.12/mode/javascript/index.html to IPython/frontend/html/notebook/static/codemirror/mode/javascript/index.html
@@ -1,352 +1,352
1 1 CodeMirror.defineMode("javascript", function(config, parserConfig) {
2 2 var indentUnit = config.indentUnit;
3 3 var jsonMode = parserConfig.json;
4 4
5 5 // Tokenizer
6 6
7 7 var keywords = function(){
8 8 function kw(type) {return {type: type, style: "keyword"};}
9 9 var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c");
10 10 var operator = kw("operator"), atom = {type: "atom", style: "atom"};
11 11 return {
12 12 "if": A, "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B,
13 13 "return": C, "break": C, "continue": C, "new": C, "delete": C, "throw": C,
14 14 "var": kw("var"), "function": kw("function"), "catch": kw("catch"),
15 15 "for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"),
16 16 "in": operator, "typeof": operator, "instanceof": operator,
17 17 "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom
18 18 };
19 19 }();
20 20
21 21 var isOperatorChar = /[+\-*&%=<>!?|]/;
22 22
23 23 function chain(stream, state, f) {
24 24 state.tokenize = f;
25 25 return f(stream, state);
26 26 }
27 27
28 28 function nextUntilUnescaped(stream, end) {
29 29 var escaped = false, next;
30 30 while ((next = stream.next()) != null) {
31 31 if (next == end && !escaped)
32 32 return false;
33 33 escaped = !escaped && next == "\\";
34 34 }
35 35 return escaped;
36 36 }
37 37
38 38 // Used as scratch variables to communicate multiple values without
39 39 // consing up tons of objects.
40 40 var type, content;
41 41 function ret(tp, style, cont) {
42 42 type = tp; content = cont;
43 43 return style;
44 44 }
45 45
46 46 function jsTokenBase(stream, state) {
47 47 var ch = stream.next();
48 48 if (ch == '"' || ch == "'")
49 49 return chain(stream, state, jsTokenString(ch));
50 50 else if (/[\[\]{}\(\),;\:\.]/.test(ch))
51 51 return ret(ch);
52 52 else if (ch == "0" && stream.eat(/x/i)) {
53 53 stream.eatWhile(/[\da-f]/i);
54 54 return ret("number", "number");
55 }
55 }
56 56 else if (/\d/.test(ch)) {
57 57 stream.match(/^\d*(?:\.\d*)?(?:e[+\-]?\d+)?/);
58 58 return ret("number", "number");
59 59 }
60 60 else if (ch == "/") {
61 61 if (stream.eat("*")) {
62 62 return chain(stream, state, jsTokenComment);
63 63 }
64 64 else if (stream.eat("/")) {
65 65 stream.skipToEnd();
66 66 return ret("comment", "comment");
67 67 }
68 68 else if (state.reAllowed) {
69 69 nextUntilUnescaped(stream, "/");
70 70 stream.eatWhile(/[gimy]/); // 'y' is "sticky" option in Mozilla
71 71 return ret("regexp", "string");
72 72 }
73 73 else {
74 74 stream.eatWhile(isOperatorChar);
75 75 return ret("operator", null, stream.current());
76 76 }
77 77 }
78 78 else if (ch == "#") {
79 79 stream.skipToEnd();
80 80 return ret("error", "error");
81 81 }
82 82 else if (isOperatorChar.test(ch)) {
83 83 stream.eatWhile(isOperatorChar);
84 84 return ret("operator", null, stream.current());
85 85 }
86 86 else {
87 87 stream.eatWhile(/[\w\$_]/);
88 88 var word = stream.current(), known = keywords.propertyIsEnumerable(word) && keywords[word];
89 89 return known ? ret(known.type, known.style, word) :
90 90 ret("variable", "variable", word);
91 91 }
92 92 }
93 93
94 94 function jsTokenString(quote) {
95 95 return function(stream, state) {
96 96 if (!nextUntilUnescaped(stream, quote))
97 97 state.tokenize = jsTokenBase;
98 98 return ret("string", "string");
99 99 };
100 100 }
101 101
102 102 function jsTokenComment(stream, state) {
103 103 var maybeEnd = false, ch;
104 104 while (ch = stream.next()) {
105 105 if (ch == "/" && maybeEnd) {
106 106 state.tokenize = jsTokenBase;
107 107 break;
108 108 }
109 109 maybeEnd = (ch == "*");
110 110 }
111 111 return ret("comment", "comment");
112 112 }
113 113
114 114 // Parser
115 115
116 116 var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true};
117 117
118 118 function JSLexical(indented, column, type, align, prev, info) {
119 119 this.indented = indented;
120 120 this.column = column;
121 121 this.type = type;
122 122 this.prev = prev;
123 123 this.info = info;
124 124 if (align != null) this.align = align;
125 125 }
126 126
127 127 function inScope(state, varname) {
128 128 for (var v = state.localVars; v; v = v.next)
129 129 if (v.name == varname) return true;
130 130 }
131 131
132 132 function parseJS(state, style, type, content, stream) {
133 133 var cc = state.cc;
134 134 // Communicate our context to the combinators.
135 135 // (Less wasteful than consing up a hundred closures on every call.)
136 136 cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc;
137
137
138 138 if (!state.lexical.hasOwnProperty("align"))
139 139 state.lexical.align = true;
140 140
141 141 while(true) {
142 142 var combinator = cc.length ? cc.pop() : jsonMode ? expression : statement;
143 143 if (combinator(type, content)) {
144 144 while(cc.length && cc[cc.length - 1].lex)
145 145 cc.pop()();
146 146 if (cx.marked) return cx.marked;
147 147 if (type == "variable" && inScope(state, content)) return "variable-2";
148 148 return style;
149 149 }
150 150 }
151 151 }
152 152
153 153 // Combinator utils
154 154
155 155 var cx = {state: null, column: null, marked: null, cc: null};
156 156 function pass() {
157 157 for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]);
158 158 }
159 159 function cont() {
160 160 pass.apply(null, arguments);
161 161 return true;
162 162 }
163 163 function register(varname) {
164 164 var state = cx.state;
165 165 if (state.context) {
166 166 cx.marked = "def";
167 167 for (var v = state.localVars; v; v = v.next)
168 168 if (v.name == varname) return;
169 169 state.localVars = {name: varname, next: state.localVars};
170 170 }
171 171 }
172 172
173 173 // Combinators
174 174
175 175 var defaultVars = {name: "this", next: {name: "arguments"}};
176 176 function pushcontext() {
177 177 if (!cx.state.context) cx.state.localVars = defaultVars;
178 178 cx.state.context = {prev: cx.state.context, vars: cx.state.localVars};
179 179 }
180 180 function popcontext() {
181 181 cx.state.localVars = cx.state.context.vars;
182 182 cx.state.context = cx.state.context.prev;
183 183 }
184 184 function pushlex(type, info) {
185 185 var result = function() {
186 186 var state = cx.state;
187 187 state.lexical = new JSLexical(state.indented, cx.stream.column(), type, null, state.lexical, info)
188 188 };
189 189 result.lex = true;
190 190 return result;
191 191 }
192 192 function poplex() {
193 193 var state = cx.state;
194 194 if (state.lexical.prev) {
195 195 if (state.lexical.type == ")")
196 196 state.indented = state.lexical.indented;
197 197 state.lexical = state.lexical.prev;
198 198 }
199 199 }
200 200 poplex.lex = true;
201 201
202 202 function expect(wanted) {
203 203 return function expecting(type) {
204 204 if (type == wanted) return cont();
205 205 else if (wanted == ";") return pass();
206 206 else return cont(arguments.callee);
207 207 };
208 208 }
209 209
210 210 function statement(type) {
211 211 if (type == "var") return cont(pushlex("vardef"), vardef1, expect(";"), poplex);
212 212 if (type == "keyword a") return cont(pushlex("form"), expression, statement, poplex);
213 213 if (type == "keyword b") return cont(pushlex("form"), statement, poplex);
214 214 if (type == "{") return cont(pushlex("}"), block, poplex);
215 215 if (type == ";") return cont();
216 216 if (type == "function") return cont(functiondef);
217 217 if (type == "for") return cont(pushlex("form"), expect("("), pushlex(")"), forspec1, expect(")"),
218 218 poplex, statement, poplex);
219 219 if (type == "variable") return cont(pushlex("stat"), maybelabel);
220 220 if (type == "switch") return cont(pushlex("form"), expression, pushlex("}", "switch"), expect("{"),
221 221 block, poplex, poplex);
222 222 if (type == "case") return cont(expression, expect(":"));
223 223 if (type == "default") return cont(expect(":"));
224 224 if (type == "catch") return cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"),
225 225 statement, poplex, popcontext);
226 226 return pass(pushlex("stat"), expression, expect(";"), poplex);
227 227 }
228 228 function expression(type) {
229 229 if (atomicTypes.hasOwnProperty(type)) return cont(maybeoperator);
230 230 if (type == "function") return cont(functiondef);
231 231 if (type == "keyword c") return cont(expression);
232 232 if (type == "(") return cont(pushlex(")"), expression, expect(")"), poplex, maybeoperator);
233 233 if (type == "operator") return cont(expression);
234 234 if (type == "[") return cont(pushlex("]"), commasep(expression, "]"), poplex, maybeoperator);
235 235 if (type == "{") return cont(pushlex("}"), commasep(objprop, "}"), poplex, maybeoperator);
236 236 return cont();
237 237 }
238 238 function maybeoperator(type, value) {
239 239 if (type == "operator" && /\+\+|--/.test(value)) return cont(maybeoperator);
240 240 if (type == "operator") return cont(expression);
241 241 if (type == ";") return;
242 242 if (type == "(") return cont(pushlex(")"), commasep(expression, ")"), poplex, maybeoperator);
243 243 if (type == ".") return cont(property, maybeoperator);
244 244 if (type == "[") return cont(pushlex("]"), expression, expect("]"), poplex, maybeoperator);
245 245 }
246 246 function maybelabel(type) {
247 247 if (type == ":") return cont(poplex, statement);
248 248 return pass(maybeoperator, expect(";"), poplex);
249 249 }
250 250 function property(type) {
251 251 if (type == "variable") {cx.marked = "property"; return cont();}
252 252 }
253 253 function objprop(type) {
254 254 if (type == "variable") cx.marked = "property";
255 255 if (atomicTypes.hasOwnProperty(type)) return cont(expect(":"), expression);
256 256 }
257 257 function commasep(what, end) {
258 258 function proceed(type) {
259 259 if (type == ",") return cont(what, proceed);
260 260 if (type == end) return cont();
261 261 return cont(expect(end));
262 262 }
263 263 return function commaSeparated(type) {
264 264 if (type == end) return cont();
265 265 else return pass(what, proceed);
266 266 };
267 267 }
268 268 function block(type) {
269 269 if (type == "}") return cont();
270 270 return pass(statement, block);
271 271 }
272 272 function vardef1(type, value) {
273 273 if (type == "variable"){register(value); return cont(vardef2);}
274 274 return cont();
275 275 }
276 276 function vardef2(type, value) {
277 277 if (value == "=") return cont(expression, vardef2);
278 278 if (type == ",") return cont(vardef1);
279 279 }
280 280 function forspec1(type) {
281 281 if (type == "var") return cont(vardef1, forspec2);
282 282 if (type == ";") return pass(forspec2);
283 283 if (type == "variable") return cont(formaybein);
284 284 return pass(forspec2);
285 285 }
286 286 function formaybein(type, value) {
287 287 if (value == "in") return cont(expression);
288 288 return cont(maybeoperator, forspec2);
289 289 }
290 290 function forspec2(type, value) {
291 291 if (type == ";") return cont(forspec3);
292 292 if (value == "in") return cont(expression);
293 293 return cont(expression, expect(";"), forspec3);
294 294 }
295 295 function forspec3(type) {
296 296 if (type != ")") cont(expression);
297 297 }
298 298 function functiondef(type, value) {
299 299 if (type == "variable") {register(value); return cont(functiondef);}
300 300 if (type == "(") return cont(pushlex(")"), pushcontext, commasep(funarg, ")"), poplex, statement, popcontext);
301 301 }
302 302 function funarg(type, value) {
303 303 if (type == "variable") {register(value); return cont();}
304 304 }
305 305
306 306 // Interface
307 307
308 308 return {
309 309 startState: function(basecolumn) {
310 310 return {
311 311 tokenize: jsTokenBase,
312 312 reAllowed: true,
313 313 cc: [],
314 314 lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false),
315 315 localVars: null,
316 316 context: null,
317 317 indented: 0
318 318 };
319 319 },
320 320
321 321 token: function(stream, state) {
322 322 if (stream.sol()) {
323 323 if (!state.lexical.hasOwnProperty("align"))
324 324 state.lexical.align = false;
325 325 state.indented = stream.indentation();
326 326 }
327 327 if (stream.eatSpace()) return null;
328 328 var style = state.tokenize(stream, state);
329 329 if (type == "comment") return style;
330 330 state.reAllowed = type == "operator" || type == "keyword c" || type.match(/^[\[{}\(,;:]$/);
331 331 return parseJS(state, style, type, content, stream);
332 332 },
333 333
334 334 indent: function(state, textAfter) {
335 335 if (state.tokenize != jsTokenBase) return 0;
336 336 var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical,
337 337 type = lexical.type, closing = firstChar == type;
338 338 if (type == "vardef") return lexical.indented + 4;
339 339 else if (type == "form" && firstChar == "{") return lexical.indented;
340 340 else if (type == "stat" || type == "form") return lexical.indented + indentUnit;
341 341 else if (lexical.info == "switch" && !closing)
342 342 return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit);
343 343 else if (lexical.align) return lexical.column + (closing ? 0 : 1);
344 344 else return lexical.indented + (closing ? 0 : indentUnit);
345 345 },
346 346
347 347 electricChars: ":{}"
348 348 };
349 349 });
350 350
351 351 CodeMirror.defineMIME("text/javascript", "javascript");
352 352 CodeMirror.defineMIME("application/json", {name: "javascript", json: true});
1 NO CONTENT: file renamed from IPython/frontend/html/notebook/static/codemirror-2.12/mode/markdown/index.html to IPython/frontend/html/notebook/static/codemirror/mode/markdown/index.html
1 NO CONTENT: file renamed from IPython/frontend/html/notebook/static/codemirror-2.12/mode/markdown/markdown.css to IPython/frontend/html/notebook/static/codemirror/mode/markdown/markdown.css
1 NO CONTENT: file renamed from IPython/frontend/html/notebook/static/codemirror-2.12/mode/markdown/markdown.js to IPython/frontend/html/notebook/static/codemirror/mode/markdown/markdown.js
1 NO CONTENT: file renamed from IPython/frontend/html/notebook/static/codemirror-2.12/mode/python/LICENSE.txt to IPython/frontend/html/notebook/static/codemirror/mode/python/LICENSE.txt
@@ -1,123 +1,123
1 1 <!doctype html>
2 2 <html>
3 3 <head>
4 4 <title>CodeMirror 2: Python mode</title>
5 5 <link rel="stylesheet" href="../../lib/codemirror.css">
6 6 <script src="../../lib/codemirror.js"></script>
7 7 <script src="python.js"></script>
8 8 <link rel="stylesheet" href="../../theme/default.css">
9 9 <link rel="stylesheet" href="../../css/docs.css">
10 10 <style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
11 11 </head>
12 12 <body>
13 13 <h1>CodeMirror 2: Python mode</h1>
14
14
15 15 <div><textarea id="code" name="code">
16 16 # Literals
17 17 1234
18 18 0.0e101
19 19 .123
20 20 0b01010011100
21 21 0o01234567
22 22 0x0987654321abcdef
23 23 7
24 24 2147483647
25 25 3L
26 26 79228162514264337593543950336L
27 27 0x100000000L
28 28 79228162514264337593543950336
29 29 0xdeadbeef
30 30 3.14j
31 31 10.j
32 32 10j
33 33 .001j
34 34 1e100j
35 35 3.14e-10j
36 36
37 37
38 38 # String Literals
39 39 'For\''
40 40 "God\""
41 41 """so loved
42 42 the world"""
43 43 '''that he gave
44 44 his only begotten\' '''
45 45 'that whosoever believeth \
46 46 in him'
47 47 ''
48 48
49 49 # Identifiers
50 50 __a__
51 51 a.b
52 52 a.b.c
53 53
54 54 # Operators
55 55 + - * / % & | ^ ~ < >
56 56 == != <= >= <> << >> // **
57 57 and or not in is
58 58
59 59 # Delimiters
60 60 () [] {} , : ` = ; @ . # Note that @ and . require the proper context.
61 61 += -= *= /= %= &= |= ^=
62 62 //= >>= <<= **=
63 63
64 64 # Keywords
65 65 as assert break class continue def del elif else except
66 66 finally for from global if import lambda pass raise
67 67 return try while with yield
68 68
69 69 # Python 2 Keywords (otherwise Identifiers)
70 70 exec print
71 71
72 72 # Python 3 Keywords (otherwise Identifiers)
73 73 nonlocal
74 74
75 75 # Types
76 76 bool classmethod complex dict enumerate float frozenset int list object
77 77 property reversed set slice staticmethod str super tuple type
78 78
79 79 # Python 2 Types (otherwise Identifiers)
80 80 basestring buffer file long unicode xrange
81 81
82 82 # Python 3 Types (otherwise Identifiers)
83 83 bytearray bytes filter map memoryview open range zip
84 84
85 85 # Some Example code
86 86 import os
87 87 from package import ParentClass
88 88
89 89 @nonsenseDecorator
90 90 def doesNothing():
91 91 pass
92 92
93 93 class ExampleClass(ParentClass):
94 94 @staticmethod
95 95 def example(inputStr):
96 96 a = list(inputStr)
97 97 a.reverse()
98 98 return ''.join(a)
99 99
100 100 def __init__(self, mixin = 'Hello'):
101 101 self.mixin = mixin
102 102
103 103 </textarea></div>
104 104 <script>
105 105 var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
106 106 mode: {name: "python",
107 107 version: 2,
108 108 singleLineStringErrors: false},
109 109 lineNumbers: true,
110 110 indentUnit: 4,
111 111 tabMode: "shift",
112 112 matchBrackets: true
113 113 });
114 114 </script>
115 115 <h2>Configuration Options:</h2>
116 116 <ul>
117 117 <li>version - 2/3 - The version of Python to recognize. Default is 2.</li>
118 118 <li>singleLineStringErrors - true/false - If you have a single-line string that is not terminated at the end of the line, this will show subsequent lines as errors if true, otherwise it will consider the newline as the end of the string. Default is false.</li>
119 119 </ul>
120 120
121 121 <p><strong>MIME types defined:</strong> <code>text/x-python</code>.</p>
122 122 </body>
123 123 </html>
@@ -1,320 +1,320
1 1 CodeMirror.defineMode("python", function(conf, parserConf) {
2 2 var ERRORCLASS = 'error';
3
3
4 4 function wordRegexp(words) {
5 5 return new RegExp("^((" + words.join(")|(") + "))\\b");
6 6 }
7
7
8 8 var singleOperators = new RegExp("^[\\+\\-\\*/%&|\\^~<>!]");
9 9 var singleDelimiters = new RegExp('^[\\(\\)\\[\\]\\{\\}@,:`=;\\.]');
10 10 var doubleOperators = new RegExp("^((==)|(!=)|(<=)|(>=)|(<>)|(<<)|(>>)|(//)|(\\*\\*))");
11 11 var doubleDelimiters = new RegExp("^((\\+=)|(\\-=)|(\\*=)|(%=)|(/=)|(&=)|(\\|=)|(\\^=))");
12 12 var tripleDelimiters = new RegExp("^((//=)|(>>=)|(<<=)|(\\*\\*=))");
13 13 var identifiers = new RegExp("^[_A-Za-z][_A-Za-z0-9]*");
14 14
15 15 var wordOperators = wordRegexp(['and', 'or', 'not', 'is', 'in']);
16 16 var commonkeywords = ['as', 'assert', 'break', 'class', 'continue',
17 17 'def', 'del', 'elif', 'else', 'except', 'finally',
18 18 'for', 'from', 'global', 'if', 'import',
19 19 'lambda', 'pass', 'raise', 'return',
20 20 'try', 'while', 'with', 'yield'];
21 21 var commontypes = ['bool', 'classmethod', 'complex', 'dict', 'enumerate',
22 22 'float', 'frozenset', 'int', 'list', 'object',
23 23 'property', 'reversed', 'set', 'slice', 'staticmethod',
24 24 'str', 'super', 'tuple', 'type'];
25 25 var py2 = {'types': ['basestring', 'buffer', 'file', 'long', 'unicode',
26 26 'xrange'],
27 27 'keywords': ['exec', 'print']};
28 28 var py3 = {'types': ['bytearray', 'bytes', 'filter', 'map', 'memoryview',
29 29 'open', 'range', 'zip'],
30 30 'keywords': ['nonlocal']};
31 31
32 32 if (!!parserConf.version && parseInt(parserConf.version, 10) === 3) {
33 33 commonkeywords = commonkeywords.concat(py3.keywords);
34 34 commontypes = commontypes.concat(py3.types);
35 35 var stringPrefixes = new RegExp("^(([rb]|(br))?('{3}|\"{3}|['\"]))", "i");
36 36 } else {
37 37 commonkeywords = commonkeywords.concat(py2.keywords);
38 38 commontypes = commontypes.concat(py2.types);
39 39 var stringPrefixes = new RegExp("^(([rub]|(ur)|(br))?('{3}|\"{3}|['\"]))", "i");
40 40 }
41 41 var keywords = wordRegexp(commonkeywords);
42 42 var types = wordRegexp(commontypes);
43 43
44 44 var indentInfo = null;
45 45
46 46 // tokenizers
47 47 function tokenBase(stream, state) {
48 48 // Handle scope changes
49 49 if (stream.sol()) {
50 50 var scopeOffset = state.scopes[0].offset;
51 51 if (stream.eatSpace()) {
52 52 var lineOffset = stream.indentation();
53 53 if (lineOffset > scopeOffset) {
54 54 indentInfo = 'indent';
55 55 } else if (lineOffset < scopeOffset) {
56 56 indentInfo = 'dedent';
57 57 }
58 58 return null;
59 59 } else {
60 60 if (scopeOffset > 0) {
61 61 dedent(stream, state);
62 62 }
63 63 }
64 64 }
65 65 if (stream.eatSpace()) {
66 66 return null;
67 67 }
68
68
69 69 var ch = stream.peek();
70
70
71 71 // Handle Comments
72 72 if (ch === '#') {
73 73 stream.skipToEnd();
74 74 return 'comment';
75 75 }
76
76
77 77 // Handle Number Literals
78 78 if (stream.match(/^[0-9\.]/, false)) {
79 79 var floatLiteral = false;
80 80 // Floats
81 81 if (stream.match(/^\d*\.\d+(e[\+\-]?\d+)?/i)) { floatLiteral = true; }
82 82 if (stream.match(/^\d+\.\d*/)) { floatLiteral = true; }
83 83 if (stream.match(/^\.\d+/)) { floatLiteral = true; }
84 84 if (floatLiteral) {
85 85 // Float literals may be "imaginary"
86 86 stream.eat(/J/i);
87 87 return 'number';
88 88 }
89 89 // Integers
90 90 var intLiteral = false;
91 91 // Hex
92 92 if (stream.match(/^0x[0-9a-f]+/i)) { intLiteral = true; }
93 93 // Binary
94 94 if (stream.match(/^0b[01]+/i)) { intLiteral = true; }
95 95 // Octal
96 96 if (stream.match(/^0o[0-7]+/i)) { intLiteral = true; }
97 97 // Decimal
98 98 if (stream.match(/^[1-9]\d*(e[\+\-]?\d+)?/)) {
99 99 // Decimal literals may be "imaginary"
100 100 stream.eat(/J/i);
101 101 // TODO - Can you have imaginary longs?
102 102 intLiteral = true;
103 103 }
104 104 // Zero by itself with no other piece of number.
105 105 if (stream.match(/^0(?![\dx])/i)) { intLiteral = true; }
106 106 if (intLiteral) {
107 107 // Integer literals may be "long"
108 108 stream.eat(/L/i);
109 109 return 'number';
110 110 }
111 111 }
112
112
113 113 // Handle Strings
114 114 if (stream.match(stringPrefixes)) {
115 115 state.tokenize = tokenStringFactory(stream.current());
116 116 return state.tokenize(stream, state);
117 117 }
118
118
119 119 // Handle operators and Delimiters
120 120 if (stream.match(tripleDelimiters) || stream.match(doubleDelimiters)) {
121 121 return null;
122 122 }
123 123 if (stream.match(doubleOperators)
124 124 || stream.match(singleOperators)
125 125 || stream.match(wordOperators)) {
126 126 return 'operator';
127 127 }
128 128 if (stream.match(singleDelimiters)) {
129 129 return null;
130 130 }
131
131
132 132 if (stream.match(types)) {
133 133 return 'builtin';
134 134 }
135
135
136 136 if (stream.match(keywords)) {
137 137 return 'keyword';
138 138 }
139
139
140 140 if (stream.match(identifiers)) {
141 141 return 'variable';
142 142 }
143
143
144 144 // Handle non-detected items
145 145 stream.next();
146 146 return ERRORCLASS;
147 147 }
148
148
149 149 function tokenStringFactory(delimiter) {
150 150 while ('rub'.indexOf(delimiter.charAt(0).toLowerCase()) >= 0) {
151 151 delimiter = delimiter.substr(1);
152 152 }
153 153 var singleline = delimiter.length == 1;
154 154 var OUTCLASS = 'string';
155
155
156 156 return function tokenString(stream, state) {
157 157 while (!stream.eol()) {
158 158 stream.eatWhile(/[^'"\\]/);
159 159 if (stream.eat('\\')) {
160 160 stream.next();
161 161 if (singleline && stream.eol()) {
162 162 return OUTCLASS;
163 163 }
164 164 } else if (stream.match(delimiter)) {
165 165 state.tokenize = tokenBase;
166 166 return OUTCLASS;
167 167 } else {
168 168 stream.eat(/['"]/);
169 169 }
170 170 }
171 171 if (singleline) {
172 172 if (parserConf.singleLineStringErrors) {
173 173 return ERRORCLASS;
174 174 } else {
175 175 state.tokenize = tokenBase;
176 176 }
177 177 }
178 178 return OUTCLASS;
179 179 };
180 180 }
181
181
182 182 function indent(stream, state, type) {
183 183 type = type || 'py';
184 184 var indentUnit = 0;
185 185 if (type === 'py') {
186 186 for (var i = 0; i < state.scopes.length; ++i) {
187 187 if (state.scopes[i].type === 'py') {
188 188 indentUnit = state.scopes[i].offset + conf.indentUnit;
189 189 break;
190 190 }
191 191 }
192 192 } else {
193 193 indentUnit = stream.column() + stream.current().length;
194 194 }
195 195 state.scopes.unshift({
196 196 offset: indentUnit,
197 197 type: type
198 198 });
199 199 }
200
200
201 201 function dedent(stream, state) {
202 202 if (state.scopes.length == 1) return;
203 203 if (state.scopes[0].type === 'py') {
204 204 var _indent = stream.indentation();
205 205 var _indent_index = -1;
206 206 for (var i = 0; i < state.scopes.length; ++i) {
207 207 if (_indent === state.scopes[i].offset) {
208 208 _indent_index = i;
209 209 break;
210 210 }
211 211 }
212 212 if (_indent_index === -1) {
213 213 return true;
214 214 }
215 215 while (state.scopes[0].offset !== _indent) {
216 216 state.scopes.shift();
217 217 }
218 218 return false
219 219 } else {
220 220 state.scopes.shift();
221 221 return false;
222 222 }
223 223 }
224 224
225 225 function tokenLexer(stream, state) {
226 226 indentInfo = null;
227 227 var style = state.tokenize(stream, state);
228 228 var current = stream.current();
229 229
230 230 // Handle '.' connected identifiers
231 231 if (current === '.') {
232 232 style = state.tokenize(stream, state);
233 233 current = stream.current();
234 234 if (style === 'variable') {
235 235 return 'variable';
236 236 } else {
237 237 return ERRORCLASS;
238 238 }
239 239 }
240
240
241 241 // Handle decorators
242 242 if (current === '@') {
243 243 style = state.tokenize(stream, state);
244 244 current = stream.current();
245 245 if (style === 'variable'
246 246 || current === '@staticmethod'
247 247 || current === '@classmethod') {
248 248 return 'meta';
249 249 } else {
250 250 return ERRORCLASS;
251 251 }
252 252 }
253
253
254 254 // Handle scope changes.
255 255 if (current === 'pass' || current === 'return') {
256 256 state.dedent += 1;
257 257 }
258 258 if ((current === ':' && !state.lambda && state.scopes[0].type == 'py')
259 259 || indentInfo === 'indent') {
260 260 indent(stream, state);
261 261 }
262 262 var delimiter_index = '[({'.indexOf(current);
263 263 if (delimiter_index !== -1) {
264 264 indent(stream, state, '])}'.slice(delimiter_index, delimiter_index+1));
265 265 }
266 266 if (indentInfo === 'dedent') {
267 267 if (dedent(stream, state)) {
268 268 return ERRORCLASS;
269 269 }
270 270 }
271 271 delimiter_index = '])}'.indexOf(current);
272 272 if (delimiter_index !== -1) {
273 273 if (dedent(stream, state)) {
274 274 return ERRORCLASS;
275 275 }
276 276 }
277 277 if (state.dedent > 0 && stream.eol() && state.scopes[0].type == 'py') {
278 278 if (state.scopes.length > 1) state.scopes.shift();
279 279 state.dedent -= 1;
280 280 }
281
281
282 282 return style;
283 283 }
284 284
285 285 var external = {
286 286 startState: function(basecolumn) {
287 287 return {
288 288 tokenize: tokenBase,
289 289 scopes: [{offset:basecolumn || 0, type:'py'}],
290 290 lastToken: null,
291 291 lambda: false,
292 292 dedent: 0
293 293 };
294 294 },
295
295
296 296 token: function(stream, state) {
297 297 var style = tokenLexer(stream, state);
298
298
299 299 state.lastToken = {style:style, content: stream.current()};
300
300
301 301 if (stream.eol() && stream.lambda) {
302 302 state.lambda = false;
303 303 }
304
304
305 305 return style;
306 306 },
307
307
308 308 indent: function(state, textAfter) {
309 309 if (state.tokenize != tokenBase) {
310 310 return 0;
311 311 }
312
312
313 313 return state.scopes[0].offset;
314 314 }
315
315
316 316 };
317 317 return external;
318 318 });
319 319
320 320 CodeMirror.defineMIME("text/x-python", "python");
@@ -1,526 +1,525
1 1 <!doctype html>
2 2 <html>
3 3 <head>
4 4 <title>CodeMirror 2: reStructuredText mode</title>
5 5 <link rel="stylesheet" href="../../lib/codemirror.css">
6 6 <script src="../../lib/codemirror.js"></script>
7 7 <script src="rst.js"></script>
8 8 <link rel="stylesheet" href="rst.css">
9 9 <style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
10 10 <link rel="stylesheet" href="../../css/docs.css">
11 11 </head>
12 12 <body>
13 13 <h1>CodeMirror 2: reStructuredText mode</h1>
14 14
15 15 <form><textarea id="code" name="code">
16 16 .. This is an excerpt from Sphinx documentation: http://sphinx.pocoo.org/_sources/rest.txt
17 17
18 18 .. highlightlang:: rest
19 19
20 20 .. _rst-primer:
21 21
22 22 reStructuredText Primer
23 23 =======================
24 24
25 25 This section is a brief introduction to reStructuredText (reST) concepts and
26 26 syntax, intended to provide authors with enough information to author documents
27 27 productively. Since reST was designed to be a simple, unobtrusive markup
28 28 language, this will not take too long.
29 29
30 30 .. seealso::
31 31
32 32 The authoritative `reStructuredText User Documentation
33 33 &lt;http://docutils.sourceforge.net/rst.html&gt;`_. The "ref" links in this
34 34 document link to the description of the individual constructs in the reST
35 35 reference.
36 36
37 37
38 38 Paragraphs
39 39 ----------
40 40
41 41 The paragraph (:duref:`ref &lt;paragraphs&gt;`) is the most basic block in a reST
42 42 document. Paragraphs are simply chunks of text separated by one or more blank
43 43 lines. As in Python, indentation is significant in reST, so all lines of the
44 44 same paragraph must be left-aligned to the same level of indentation.
45 45
46 46
47 47 .. _inlinemarkup:
48 48
49 49 Inline markup
50 50 -------------
51 51
52 52 The standard reST inline markup is quite simple: use
53 53
54 54 * one asterisk: ``*text*`` for emphasis (italics),
55 55 * two asterisks: ``**text**`` for strong emphasis (boldface), and
56 56 * backquotes: ````text```` for code samples.
57 57
58 58 If asterisks or backquotes appear in running text and could be confused with
59 59 inline markup delimiters, they have to be escaped with a backslash.
60 60
61 61 Be aware of some restrictions of this markup:
62 62
63 63 * it may not be nested,
64 64 * content may not start or end with whitespace: ``* text*`` is wrong,
65 65 * it must be separated from surrounding text by non-word characters. Use a
66 66 backslash escaped space to work around that: ``thisis\ *one*\ word``.
67 67
68 68 These restrictions may be lifted in future versions of the docutils.
69 69
70 70 reST also allows for custom "interpreted text roles"', which signify that the
71 71 enclosed text should be interpreted in a specific way. Sphinx uses this to
72 72 provide semantic markup and cross-referencing of identifiers, as described in
73 73 the appropriate section. The general syntax is ``:rolename:`content```.
74 74
75 75 Standard reST provides the following roles:
76 76
77 77 * :durole:`emphasis` -- alternate spelling for ``*emphasis*``
78 78 * :durole:`strong` -- alternate spelling for ``**strong**``
79 79 * :durole:`literal` -- alternate spelling for ````literal````
80 80 * :durole:`subscript` -- subscript text
81 81 * :durole:`superscript` -- superscript text
82 82 * :durole:`title-reference` -- for titles of books, periodicals, and other
83 83 materials
84 84
85 85 See :ref:`inline-markup` for roles added by Sphinx.
86 86
87 87
88 88 Lists and Quote-like blocks
89 89 ---------------------------
90 90
91 91 List markup (:duref:`ref &lt;bullet-lists&gt;`) is natural: just place an asterisk at
92 92 the start of a paragraph and indent properly. The same goes for numbered lists;
93 93 they can also be autonumbered using a ``#`` sign::
94 94
95 95 * This is a bulleted list.
96 96 * It has two items, the second
97 97 item uses two lines.
98 98
99 99 1. This is a numbered list.
100 100 2. It has two items too.
101 101
102 102 #. This is a numbered list.
103 103 #. It has two items too.
104 104
105 105
106 106 Nested lists are possible, but be aware that they must be separated from the
107 107 parent list items by blank lines::
108 108
109 109 * this is
110 110 * a list
111 111
112 112 * with a nested list
113 113 * and some subitems
114 114
115 115 * and here the parent list continues
116 116
117 117 Definition lists (:duref:`ref &lt;definition-lists&gt;`) are created as follows::
118 118
119 119 term (up to a line of text)
120 120 Definition of the term, which must be indented
121 121
122 122 and can even consist of multiple paragraphs
123 123
124 124 next term
125 125 Description.
126 126
127 127 Note that the term cannot have more than one line of text.
128 128
129 129 Quoted paragraphs (:duref:`ref &lt;block-quotes&gt;`) are created by just indenting
130 130 them more than the surrounding paragraphs.
131 131
132 132 Line blocks (:duref:`ref &lt;line-blocks&gt;`) are a way of preserving line breaks::
133 133
134 134 | These lines are
135 135 | broken exactly like in
136 136 | the source file.
137 137
138 138 There are also several more special blocks available:
139 139
140 140 * field lists (:duref:`ref &lt;field-lists&gt;`)
141 141 * option lists (:duref:`ref &lt;option-lists&gt;`)
142 142 * quoted literal blocks (:duref:`ref &lt;quoted-literal-blocks&gt;`)
143 143 * doctest blocks (:duref:`ref &lt;doctest-blocks&gt;`)
144 144
145 145
146 146 Source Code
147 147 -----------
148 148
149 149 Literal code blocks (:duref:`ref &lt;literal-blocks&gt;`) are introduced by ending a
150 150 paragraph with the special marker ``::``. The literal block must be indented
151 151 (and, like all paragraphs, separated from the surrounding ones by blank lines)::
152 152
153 153 This is a normal text paragraph. The next paragraph is a code sample::
154 154
155 155 It is not processed in any way, except
156 156 that the indentation is removed.
157 157
158 158 It can span multiple lines.
159 159
160 160 This is a normal text paragraph again.
161 161
162 162 The handling of the ``::`` marker is smart:
163 163
164 164 * If it occurs as a paragraph of its own, that paragraph is completely left
165 165 out of the document.
166 166 * If it is preceded by whitespace, the marker is removed.
167 167 * If it is preceded by non-whitespace, the marker is replaced by a single
168 168 colon.
169 169
170 170 That way, the second sentence in the above example's first paragraph would be
171 171 rendered as "The next paragraph is a code sample:".
172 172
173 173
174 174 .. _rst-tables:
175 175
176 176 Tables
177 177 ------
178 178
179 179 Two forms of tables are supported. For *grid tables* (:duref:`ref
180 180 &lt;grid-tables&gt;`), you have to "paint" the cell grid yourself. They look like
181 181 this::
182 182
183 183 +------------------------+------------+----------+----------+
184 184 | Header row, column 1 | Header 2 | Header 3 | Header 4 |
185 185 | (header rows optional) | | | |
186 186 +========================+============+==========+==========+
187 187 | body row 1, column 1 | column 2 | column 3 | column 4 |
188 188 +------------------------+------------+----------+----------+
189 189 | body row 2 | ... | ... | |
190 190 +------------------------+------------+----------+----------+
191 191
192 192 *Simple tables* (:duref:`ref &lt;simple-tables&gt;`) are easier to write, but
193 193 limited: they must contain more than one row, and the first column cannot
194 194 contain multiple lines. They look like this::
195 195
196 196 ===== ===== =======
197 197 A B A and B
198 198 ===== ===== =======
199 199 False False False
200 200 True False False
201 201 False True False
202 202 True True True
203 203 ===== ===== =======
204 204
205 205
206 206 Hyperlinks
207 207 ----------
208 208
209 209 External links
210 210 ^^^^^^^^^^^^^^
211 211
212 212 Use ```Link text &lt;http://example.com/&gt;`_`` for inline web links. If the link
213 213 text should be the web address, you don't need special markup at all, the parser
214 214 finds links and mail addresses in ordinary text.
215 215
216 216 You can also separate the link and the target definition (:duref:`ref
217 217 &lt;hyperlink-targets&gt;`), like this::
218 218
219 219 This is a paragraph that contains `a link`_.
220 220
221 221 .. _a link: http://example.com/
222 222
223 223
224 224 Internal links
225 225 ^^^^^^^^^^^^^^
226 226
227 227 Internal linking is done via a special reST role provided by Sphinx, see the
228 228 section on specific markup, :ref:`ref-role`.
229 229
230 230
231 231 Sections
232 232 --------
233 233
234 234 Section headers (:duref:`ref &lt;sections&gt;`) are created by underlining (and
235 235 optionally overlining) the section title with a punctuation character, at least
236 236 as long as the text::
237 237
238 238 =================
239 239 This is a heading
240 240 =================
241 241
242 242 Normally, there are no heading levels assigned to certain characters as the
243 243 structure is determined from the succession of headings. However, for the
244 244 Python documentation, this convention is used which you may follow:
245 245
246 246 * ``#`` with overline, for parts
247 247 * ``*`` with overline, for chapters
248 248 * ``=``, for sections
249 249 * ``-``, for subsections
250 250 * ``^``, for subsubsections
251 251 * ``"``, for paragraphs
252 252
253 253 Of course, you are free to use your own marker characters (see the reST
254 254 documentation), and use a deeper nesting level, but keep in mind that most
255 255 target formats (HTML, LaTeX) have a limited supported nesting depth.
256 256
257 257
258 258 Explicit Markup
259 259 ---------------
260 260
261 261 "Explicit markup" (:duref:`ref &lt;explicit-markup-blocks&gt;`) is used in reST for
262 262 most constructs that need special handling, such as footnotes,
263 263 specially-highlighted paragraphs, comments, and generic directives.
264 264
265 265 An explicit markup block begins with a line starting with ``..`` followed by
266 266 whitespace and is terminated by the next paragraph at the same level of
267 267 indentation. (There needs to be a blank line between explicit markup and normal
268 268 paragraphs. This may all sound a bit complicated, but it is intuitive enough
269 269 when you write it.)
270 270
271 271
272 272 .. _directives:
273 273
274 274 Directives
275 275 ----------
276 276
277 277 A directive (:duref:`ref &lt;directives&gt;`) is a generic block of explicit markup.
278 278 Besides roles, it is one of the extension mechanisms of reST, and Sphinx makes
279 279 heavy use of it.
280 280
281 281 Docutils supports the following directives:
282 282
283 283 * Admonitions: :dudir:`attention`, :dudir:`caution`, :dudir:`danger`,
284 284 :dudir:`error`, :dudir:`hint`, :dudir:`important`, :dudir:`note`,
285 285 :dudir:`tip`, :dudir:`warning` and the generic :dudir:`admonition`.
286 286 (Most themes style only "note" and "warning" specially.)
287 287
288 288 * Images:
289 289
290 290 - :dudir:`image` (see also Images_ below)
291 291 - :dudir:`figure` (an image with caption and optional legend)
292 292
293 293 * Additional body elements:
294 294
295 295 - :dudir:`contents` (a local, i.e. for the current file only, table of
296 296 contents)
297 297 - :dudir:`container` (a container with a custom class, useful to generate an
298 298 outer ``&lt;div&gt;`` in HTML)
299 299 - :dudir:`rubric` (a heading without relation to the document sectioning)
300 300 - :dudir:`topic`, :dudir:`sidebar` (special highlighted body elements)
301 301 - :dudir:`parsed-literal` (literal block that supports inline markup)
302 302 - :dudir:`epigraph` (a block quote with optional attribution line)
303 303 - :dudir:`highlights`, :dudir:`pull-quote` (block quotes with their own
304 304 class attribute)
305 305 - :dudir:`compound` (a compound paragraph)
306 306
307 307 * Special tables:
308 308
309 309 - :dudir:`table` (a table with title)
310 310 - :dudir:`csv-table` (a table generated from comma-separated values)
311 311 - :dudir:`list-table` (a table generated from a list of lists)
312 312
313 313 * Special directives:
314 314
315 315 - :dudir:`raw` (include raw target-format markup)
316 316 - :dudir:`include` (include reStructuredText from another file)
317 317 -- in Sphinx, when given an absolute include file path, this directive takes
318 318 it as relative to the source directory
319 319 - :dudir:`class` (assign a class attribute to the next element) [1]_
320 320
321 321 * HTML specifics:
322 322
323 323 - :dudir:`meta` (generation of HTML ``&lt;meta&gt;`` tags)
324 324 - :dudir:`title` (override document title)
325 325
326 326 * Influencing markup:
327 327
328 328 - :dudir:`default-role` (set a new default role)
329 329 - :dudir:`role` (create a new role)
330 330
331 331 Since these are only per-file, better use Sphinx' facilities for setting the
332 332 :confval:`default_role`.
333 333
334 334 Do *not* use the directives :dudir:`sectnum`, :dudir:`header` and
335 335 :dudir:`footer`.
336 336
337 337 Directives added by Sphinx are described in :ref:`sphinxmarkup`.
338 338
339 339 Basically, a directive consists of a name, arguments, options and content. (Keep
340 340 this terminology in mind, it is used in the next chapter describing custom
341 341 directives.) Looking at this example, ::
342 342
343 343 .. function:: foo(x)
344 344 foo(y, z)
345 345 :module: some.module.name
346 346
347 347 Return a line of text input from the user.
348 348
349 349 ``function`` is the directive name. It is given two arguments here, the
350 350 remainder of the first line and the second line, as well as one option
351 351 ``module`` (as you can see, options are given in the lines immediately following
352 352 the arguments and indicated by the colons). Options must be indented to the
353 353 same level as the directive content.
354 354
355 355 The directive content follows after a blank line and is indented relative to the
356 356 directive start.
357 357
358 358
359 359 Images
360 360 ------
361 361
362 362 reST supports an image directive (:dudir:`ref &lt;image&gt;`), used like so::
363 363
364 364 .. image:: gnu.png
365 365 (options)
366 366
367 367 When used within Sphinx, the file name given (here ``gnu.png``) must either be
368 368 relative to the source file, or absolute which means that they are relative to
369 369 the top source directory. For example, the file ``sketch/spam.rst`` could refer
370 370 to the image ``images/spam.png`` as ``../images/spam.png`` or
371 371 ``/images/spam.png``.
372 372
373 373 Sphinx will automatically copy image files over to a subdirectory of the output
374 374 directory on building (e.g. the ``_static`` directory for HTML output.)
375 375
376 376 Interpretation of image size options (``width`` and ``height``) is as follows:
377 377 if the size has no unit or the unit is pixels, the given size will only be
378 378 respected for output channels that support pixels (i.e. not in LaTeX output).
379 379 Other units (like ``pt`` for points) will be used for HTML and LaTeX output.
380 380
381 381 Sphinx extends the standard docutils behavior by allowing an asterisk for the
382 382 extension::
383 383
384 384 .. image:: gnu.*
385 385
386 386 Sphinx then searches for all images matching the provided pattern and determines
387 387 their type. Each builder then chooses the best image out of these candidates.
388 388 For instance, if the file name ``gnu.*`` was given and two files :file:`gnu.pdf`
389 389 and :file:`gnu.png` existed in the source tree, the LaTeX builder would choose
390 390 the former, while the HTML builder would prefer the latter.
391 391
392 392 .. versionchanged:: 0.4
393 393 Added the support for file names ending in an asterisk.
394 394
395 395 .. versionchanged:: 0.6
396 396 Image paths can now be absolute.
397 397
398 398
399 399 Footnotes
400 400 ---------
401 401
402 402 For footnotes (:duref:`ref &lt;footnotes&gt;`), use ``[#name]_`` to mark the footnote
403 403 location, and add the footnote body at the bottom of the document after a
404 404 "Footnotes" rubric heading, like so::
405 405
406 406 Lorem ipsum [#f1]_ dolor sit amet ... [#f2]_
407 407
408 408 .. rubric:: Footnotes
409 409
410 410 .. [#f1] Text of the first footnote.
411 411 .. [#f2] Text of the second footnote.
412 412
413 413 You can also explicitly number the footnotes (``[1]_``) or use auto-numbered
414 414 footnotes without names (``[#]_``).
415 415
416 416
417 417 Citations
418 418 ---------
419 419
420 420 Standard reST citations (:duref:`ref &lt;citations&gt;`) are supported, with the
421 421 additional feature that they are "global", i.e. all citations can be referenced
422 422 from all files. Use them like so::
423 423
424 424 Lorem ipsum [Ref]_ dolor sit amet.
425 425
426 426 .. [Ref] Book or article reference, URL or whatever.
427 427
428 428 Citation usage is similar to footnote usage, but with a label that is not
429 429 numeric or begins with ``#``.
430 430
431 431
432 432 Substitutions
433 433 -------------
434 434
435 435 reST supports "substitutions" (:duref:`ref &lt;substitution-definitions&gt;`), which
436 436 are pieces of text and/or markup referred to in the text by ``|name|``. They
437 437 are defined like footnotes with explicit markup blocks, like this::
438 438
439 439 .. |name| replace:: replacement *text*
440 440
441 441 or this::
442 442
443 443 .. |caution| image:: warning.png
444 444 :alt: Warning!
445 445
446 446 See the :duref:`reST reference for substitutions &lt;substitution-definitions&gt;`
447 447 for details.
448 448
449 449 If you want to use some substitutions for all documents, put them into
450 450 :confval:`rst_prolog` or put them into a separate file and include it into all
451 451 documents you want to use them in, using the :rst:dir:`include` directive. (Be
452 452 sure to give the include file a file name extension differing from that of other
453 453 source files, to avoid Sphinx finding it as a standalone document.)
454 454
455 455 Sphinx defines some default substitutions, see :ref:`default-substitutions`.
456 456
457 457
458 458 Comments
459 459 --------
460 460
461 461 Every explicit markup block which isn't a valid markup construct (like the
462 462 footnotes above) is regarded as a comment (:duref:`ref &lt;comments&gt;`). For
463 463 example::
464 464
465 465 .. This is a comment.
466 466
467 467 You can indent text after a comment start to form multiline comments::
468 468
469 469 ..
470 470 This whole indented block
471 471 is a comment.
472 472
473 473 Still in the comment.
474 474
475 475
476 476 Source encoding
477 477 ---------------
478 478
479 479 Since the easiest way to include special characters like em dashes or copyright
480 480 signs in reST is to directly write them as Unicode characters, one has to
481 481 specify an encoding. Sphinx assumes source files to be encoded in UTF-8 by
482 482 default; you can change this with the :confval:`source_encoding` config value.
483 483
484 484
485 485 Gotchas
486 486 -------
487 487
488 488 There are some problems one commonly runs into while authoring reST documents:
489 489
490 490 * **Separation of inline markup:** As said above, inline markup spans must be
491 491 separated from the surrounding text by non-word characters, you have to use a
492 492 backslash-escaped space to get around that. See `the reference
493 493 &lt;http://docutils.sf.net/docs/ref/rst/restructuredtext.html#inline-markup&gt;`_
494 494 for the details.
495 495
496 496 * **No nested inline markup:** Something like ``*see :func:`foo`*`` is not
497 497 possible.
498 498
499 499
500 500 .. rubric:: Footnotes
501 501
502 502 .. [1] When the default domain contains a :rst:dir:`class` directive, this directive
503 503 will be shadowed. Therefore, Sphinx re-exports it as :rst:dir:`rst-class`.
504 504 </textarea></form>
505 505
506 506 <script>
507 507 var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
508 508 lineNumbers: true,
509 509 });
510 510 </script>
511 511 <p>The reStructuredText mode supports one configuration parameter:</p>
512 512 <dl>
513 513 <dt><code>verbatim (string)</code></dt>
514 514 <dd>A name or MIME type of a mode that will be used for highlighting
515 515 verbatim blocks. By default, reStructuredText mode uses uniform color
516 516 for whole block of verbatim text if no mode is given.</dd>
517 517 </dl>
518 518 <p>If <code>python</code> mode is available (not a part of CodeMirror 2 yet),
519 519 it will be used for highlighting blocks containing Python/IPython terminal
520 520 sessions (blocks starting with <code>&gt;&gt;&gt;</code> (for Python) or
521 521 <code>In [num]:</code> (for IPython).
522 522
523 523 <p><strong>MIME types defined:</strong> <code>text/x-rst</code>.</p>
524 524 </body>
525 525 </html>
526
1 NO CONTENT: file renamed from IPython/frontend/html/notebook/static/codemirror-2.12/mode/rst/rst.css to IPython/frontend/html/notebook/static/codemirror/mode/rst/rst.css
1 NO CONTENT: file renamed from IPython/frontend/html/notebook/static/codemirror-2.12/mode/rst/rst.js to IPython/frontend/html/notebook/static/codemirror/mode/rst/rst.js
1 NO CONTENT: file renamed from IPython/frontend/html/notebook/static/codemirror-2.12/mode/xml/index.html to IPython/frontend/html/notebook/static/codemirror/mode/xml/index.html
1 NO CONTENT: file renamed from IPython/frontend/html/notebook/static/codemirror-2.12/mode/xml/xml.js to IPython/frontend/html/notebook/static/codemirror/mode/xml/xml.js
1 NO CONTENT: file renamed from IPython/frontend/html/notebook/static/codemirror-2.12/theme/default.css to IPython/frontend/html/notebook/static/codemirror/theme/default.css
1 NO CONTENT: file renamed from IPython/frontend/html/notebook/static/codemirror-2.12/theme/elegant.css to IPython/frontend/html/notebook/static/codemirror/theme/elegant.css
@@ -1,41 +1,40
1 1
2 2
3 3 .cm-s-ipython span.cm-keyword {color: #008000; font-weight: bold;}
4 4 .cm-s-ipython span.cm-number {color: #666666;}
5 5 .cm-s-ipython span.cm-operator {color: #AA22FF; font-weight: bold;}
6 6 .cm-s-ipython span.cm-meta {color: #AA22FF;}
7 7 .cm-s-ipython span.cm-comment {color: #408080; font-style: italic;}
8 8 .cm-s-ipython span.cm-string {color: #BA2121;}
9 9 .cm-s-ipython span.cm-error {color: #f00;}
10 10 .cm-s-ipython span.cm-builtin {color: #008000;}
11 11 .cm-s-ipython span.cm-variable {color: #000000;}
12 12
13 13 /* These classes are not currently used in the python.js mode */
14 14
15 15 /*.cm-s-ipython span.cm-atom {color: #219;}*/
16 16 /*.cm-s-ipython span.cm-def {color: #00f;}*/
17 17 /*.cm-s-ipython span.cm-variable-2 {color: #05a;}*/
18 18 /*.cm-s-ipython span.cm-variable-3 {color: #0a5;}*/
19 19 /*.cm-s-ipython span.cm-property {color: black;}*/
20 20 /*.cm-s-ipython span.cm-qualifier {color: #555;}*/
21 21 /*.cm-s-ipython span.cm-bracket {color: #cc7;}*/
22 22 /*.cm-s-ipython span.cm-tag {color: #170;}*/
23 23 /*.cm-s-ipython span.cm-attribute {color: #00c;}*/
24 24
25 25 /* These are the old styles for our pre-themed version */
26 26
27 27 /*span.py-delimiter {color: #666666;}*/
28 28 /*span.py-special {color: #666666;}*/
29 29 /*span.py-operator {color: #AA22FF; font-weight: bold;}*/
30 30 /*span.py-keyword {color: #008000; font-weight: bold;}*/
31 31 /*span.py-number {color: #666666;}*/
32 32 /*span.py-identifier {color: #000000;}*/
33 33 /*span.py-func {color: #000000;}*/
34 34 /*span.py-type {color: #008000;}*/
35 35 /*span.py-decorator {color: #AA22FF;}*/
36 36 /*span.py-comment {color: #408080; font-style: italic;}*/
37 37 /*span.py-string {color: #BA2121;}*/
38 38 /*span.py-bytes {color: #BA2121;}*/
39 39 /*span.py-raw {color: #BA2121;}*/
40 40 /*span.py-unicode {color: #BA2121;}*/
41
1 NO CONTENT: file renamed from IPython/frontend/html/notebook/static/codemirror-2.12/theme/neat.css to IPython/frontend/html/notebook/static/codemirror/theme/neat.css
1 NO CONTENT: file renamed from IPython/frontend/html/notebook/static/codemirror-2.12/theme/night.css to IPython/frontend/html/notebook/static/codemirror/theme/night.css
General Comments 0
You need to be logged in to leave comments. Login now