##// END OF EJS Templates
Updating to CodeMirror 2.2, latest stable release.
Brian Granger -
Show More
@@ -0,0 +1,23 b''
1 .CodeMirror-dialog {
2 position: relative;
3 }
4
5 .CodeMirror-dialog > div {
6 position: absolute;
7 top: 0; left: 0; right: 0;
8 background: white;
9 border-bottom: 1px solid #eee;
10 z-index: 15;
11 padding: .1em .8em;
12 overflow: hidden;
13 color: #333;
14 }
15
16 .CodeMirror-dialog input {
17 border: none;
18 outline: none;
19 background: transparent;
20 width: 20em;
21 color: inherit;
22 font-family: monospace;
23 }
@@ -0,0 +1,63 b''
1 // Open simple dialogs on top of an editor. Relies on dialog.css.
2
3 (function() {
4 function dialogDiv(cm, template) {
5 var wrap = cm.getWrapperElement();
6 var dialog = wrap.insertBefore(document.createElement("div"), wrap.firstChild);
7 dialog.className = "CodeMirror-dialog";
8 dialog.innerHTML = '<div>' + template + '</div>';
9 return dialog;
10 }
11
12 CodeMirror.defineExtension("openDialog", function(template, callback) {
13 var dialog = dialogDiv(this, template);
14 var closed = false, me = this;
15 function close() {
16 if (closed) return;
17 closed = true;
18 dialog.parentNode.removeChild(dialog);
19 }
20 var inp = dialog.getElementsByTagName("input")[0];
21 if (inp) {
22 CodeMirror.connect(inp, "keydown", function(e) {
23 if (e.keyCode == 13 || e.keyCode == 27) {
24 CodeMirror.e_stop(e);
25 close();
26 me.focus();
27 if (e.keyCode == 13) callback(inp.value);
28 }
29 });
30 inp.focus();
31 CodeMirror.connect(inp, "blur", close);
32 }
33 return close;
34 });
35
36 CodeMirror.defineExtension("openConfirm", function(template, callbacks) {
37 var dialog = dialogDiv(this, template);
38 var buttons = dialog.getElementsByTagName("button");
39 var closed = false, me = this, blurring = 1;
40 function close() {
41 if (closed) return;
42 closed = true;
43 dialog.parentNode.removeChild(dialog);
44 me.focus();
45 }
46 buttons[0].focus();
47 for (var i = 0; i < buttons.length; ++i) {
48 var b = buttons[i];
49 (function(callback) {
50 CodeMirror.connect(b, "click", function(e) {
51 CodeMirror.e_preventDefault(e);
52 close();
53 if (callback) callback(me);
54 });
55 })(callbacks[i]);
56 CodeMirror.connect(b, "blur", function() {
57 --blurring;
58 setTimeout(function() { if (blurring <= 0) close(); }, 200);
59 });
60 CodeMirror.connect(b, "focus", function() { ++blurring; });
61 }
62 });
63 })(); No newline at end of file
@@ -0,0 +1,66 b''
1 CodeMirror.braceRangeFinder = function(cm, line) {
2 var lineText = cm.getLine(line);
3 var startChar = lineText.lastIndexOf("{");
4 if (startChar < 0 || lineText.lastIndexOf("}") > startChar) return;
5 var tokenType = cm.getTokenAt({line: line, ch: startChar}).className;
6 var count = 1, lastLine = cm.lineCount(), end;
7 outer: for (var i = line + 1; i < lastLine; ++i) {
8 var text = cm.getLine(i), pos = 0;
9 for (;;) {
10 var nextOpen = text.indexOf("{", pos), nextClose = text.indexOf("}", pos);
11 if (nextOpen < 0) nextOpen = text.length;
12 if (nextClose < 0) nextClose = text.length;
13 pos = Math.min(nextOpen, nextClose);
14 if (pos == text.length) break;
15 if (cm.getTokenAt({line: i, ch: pos + 1}).className == tokenType) {
16 if (pos == nextOpen) ++count;
17 else if (!--count) { end = i; break outer; }
18 }
19 ++pos;
20 }
21 }
22 if (end == null || end == line + 1) return;
23 return end;
24 };
25
26
27 CodeMirror.newFoldFunction = function(rangeFinder, markText) {
28 var folded = [];
29 if (markText == null) markText = '<div style="position: absolute; left: 2px; color:#600">&#x25bc;</div>%N%';
30
31 function isFolded(cm, n) {
32 for (var i = 0; i < folded.length; ++i) {
33 var start = cm.lineInfo(folded[i].start);
34 if (!start) folded.splice(i--, 1);
35 else if (start.line == n) return {pos: i, region: folded[i]};
36 }
37 }
38
39 function expand(cm, region) {
40 cm.clearMarker(region.start);
41 for (var i = 0; i < region.hidden.length; ++i)
42 cm.showLine(region.hidden[i]);
43 }
44
45 return function(cm, line) {
46 cm.operation(function() {
47 var known = isFolded(cm, line);
48 if (known) {
49 folded.splice(known.pos, 1);
50 expand(cm, known.region);
51 } else {
52 var end = rangeFinder(cm, line);
53 if (end == null) return;
54 var hidden = [];
55 for (var i = line + 1; i < end; ++i) {
56 var handle = cm.hideLine(i);
57 if (handle) hidden.push(handle);
58 }
59 var first = cm.setMarker(line, markText);
60 var region = {start: first, hidden: hidden};
61 cm.onDeleteLine(first, function() { expand(cm, region); });
62 folded.push(region);
63 }
64 });
65 };
66 };
@@ -0,0 +1,291 b''
1 // ============== Formatting extensions ============================
2 // A common storage for all mode-specific formatting features
3 if (!CodeMirror.modeExtensions) CodeMirror.modeExtensions = {};
4
5 // Returns the extension of the editor's current mode
6 CodeMirror.defineExtension("getModeExt", function () {
7 return CodeMirror.modeExtensions[this.getOption("mode")];
8 });
9
10 // If the current mode is 'htmlmixed', returns the extension of a mode located at
11 // the specified position (can be htmlmixed, css or javascript). Otherwise, simply
12 // returns the extension of the editor's current mode.
13 CodeMirror.defineExtension("getModeExtAtPos", function (pos) {
14 var token = this.getTokenAt(pos);
15 if (token && token.state && token.state.mode)
16 return CodeMirror.modeExtensions[token.state.mode == "html" ? "htmlmixed" : token.state.mode];
17 else
18 return this.getModeExt();
19 });
20
21 // Comment/uncomment the specified range
22 CodeMirror.defineExtension("commentRange", function (isComment, from, to) {
23 var curMode = this.getModeExtAtPos(this.getCursor());
24 if (isComment) { // Comment range
25 var commentedText = this.getRange(from, to);
26 this.replaceRange(curMode.commentStart + this.getRange(from, to) + curMode.commentEnd
27 , from, to);
28 if (from.line == to.line && from.ch == to.ch) { // An empty comment inserted - put cursor inside
29 this.setCursor(from.line, from.ch + curMode.commentStart.length);
30 }
31 }
32 else { // Uncomment range
33 var selText = this.getRange(from, to);
34 var startIndex = selText.indexOf(curMode.commentStart);
35 var endIndex = selText.lastIndexOf(curMode.commentEnd);
36 if (startIndex > -1 && endIndex > -1 && endIndex > startIndex) {
37 // Take string till comment start
38 selText = selText.substr(0, startIndex)
39 // From comment start till comment end
40 + selText.substring(startIndex + curMode.commentStart.length, endIndex)
41 // From comment end till string end
42 + selText.substr(endIndex + curMode.commentEnd.length);
43 }
44 this.replaceRange(selText, from, to);
45 }
46 });
47
48 // Applies automatic mode-aware indentation to the specified range
49 CodeMirror.defineExtension("autoIndentRange", function (from, to) {
50 var cmInstance = this;
51 this.operation(function () {
52 for (var i = from.line; i <= to.line; i++) {
53 cmInstance.indentLine(i);
54 }
55 });
56 });
57
58 // Applies automatic formatting to the specified range
59 CodeMirror.defineExtension("autoFormatRange", function (from, to) {
60 var absStart = this.indexFromPos(from);
61 var absEnd = this.indexFromPos(to);
62 // Insert additional line breaks where necessary according to the
63 // mode's syntax
64 var res = this.getModeExt().autoFormatLineBreaks(this.getValue(), absStart, absEnd);
65 var cmInstance = this;
66
67 // Replace and auto-indent the range
68 this.operation(function () {
69 cmInstance.replaceRange(res, from, to);
70 var startLine = cmInstance.posFromIndex(absStart).line;
71 var endLine = cmInstance.posFromIndex(absStart + res.length).line;
72 for (var i = startLine; i <= endLine; i++) {
73 cmInstance.indentLine(i);
74 }
75 });
76 });
77
78 // Define extensions for a few modes
79
80 CodeMirror.modeExtensions["css"] = {
81 commentStart: "/*",
82 commentEnd: "*/",
83 wordWrapChars: [";", "\\{", "\\}"],
84 autoFormatLineBreaks: function (text) {
85 return text.replace(new RegExp("(;|\\{|\\})([^\r\n])", "g"), "$1\n$2");
86 }
87 };
88
89 CodeMirror.modeExtensions["javascript"] = {
90 commentStart: "/*",
91 commentEnd: "*/",
92 wordWrapChars: [";", "\\{", "\\}"],
93
94 getNonBreakableBlocks: function (text) {
95 var nonBreakableRegexes = [
96 new RegExp("for\\s*?\\(([\\s\\S]*?)\\)"),
97 new RegExp("'([\\s\\S]*?)('|$)"),
98 new RegExp("\"([\\s\\S]*?)(\"|$)"),
99 new RegExp("//.*([\r\n]|$)")
100 ];
101 var nonBreakableBlocks = new Array();
102 for (var i = 0; i < nonBreakableRegexes.length; i++) {
103 var curPos = 0;
104 while (curPos < text.length) {
105 var m = text.substr(curPos).match(nonBreakableRegexes[i]);
106 if (m != null) {
107 nonBreakableBlocks.push({
108 start: curPos + m.index,
109 end: curPos + m.index + m[0].length
110 });
111 curPos += m.index + Math.max(1, m[0].length);
112 }
113 else { // No more matches
114 break;
115 }
116 }
117 }
118 nonBreakableBlocks.sort(function (a, b) {
119 return a.start - b.start;
120 });
121
122 return nonBreakableBlocks;
123 },
124
125 autoFormatLineBreaks: function (text) {
126 var curPos = 0;
127 var reLinesSplitter = new RegExp("(;|\\{|\\})([^\r\n])", "g");
128 var nonBreakableBlocks = this.getNonBreakableBlocks(text);
129 if (nonBreakableBlocks != null) {
130 var res = "";
131 for (var i = 0; i < nonBreakableBlocks.length; i++) {
132 if (nonBreakableBlocks[i].start > curPos) { // Break lines till the block
133 res += text.substring(curPos, nonBreakableBlocks[i].start).replace(reLinesSplitter, "$1\n$2");
134 curPos = nonBreakableBlocks[i].start;
135 }
136 if (nonBreakableBlocks[i].start <= curPos
137 && nonBreakableBlocks[i].end >= curPos) { // Skip non-breakable block
138 res += text.substring(curPos, nonBreakableBlocks[i].end);
139 curPos = nonBreakableBlocks[i].end;
140 }
141 }
142 if (curPos < text.length - 1) {
143 res += text.substr(curPos).replace(reLinesSplitter, "$1\n$2");
144 }
145 return res;
146 }
147 else {
148 return text.replace(reLinesSplitter, "$1\n$2");
149 }
150 }
151 };
152
153 CodeMirror.modeExtensions["xml"] = {
154 commentStart: "<!--",
155 commentEnd: "-->",
156 wordWrapChars: [">"],
157
158 autoFormatLineBreaks: function (text) {
159 var lines = text.split("\n");
160 var reProcessedPortion = new RegExp("(^\\s*?<|^[^<]*?)(.+)(>\\s*?$|[^>]*?$)");
161 var reOpenBrackets = new RegExp("<", "g");
162 var reCloseBrackets = new RegExp("(>)([^\r\n])", "g");
163 for (var i = 0; i < lines.length; i++) {
164 var mToProcess = lines[i].match(reProcessedPortion);
165 if (mToProcess != null && mToProcess.length > 3) { // The line starts with whitespaces and ends with whitespaces
166 lines[i] = mToProcess[1]
167 + mToProcess[2].replace(reOpenBrackets, "\n$&").replace(reCloseBrackets, "$1\n$2")
168 + mToProcess[3];
169 continue;
170 }
171 }
172
173 return lines.join("\n");
174 }
175 };
176
177 CodeMirror.modeExtensions["htmlmixed"] = {
178 commentStart: "<!--",
179 commentEnd: "-->",
180 wordWrapChars: [">", ";", "\\{", "\\}"],
181
182 getModeInfos: function (text, absPos) {
183 var modeInfos = new Array();
184 modeInfos[0] =
185 {
186 pos: 0,
187 modeExt: CodeMirror.modeExtensions["xml"],
188 modeName: "xml"
189 };
190
191 var modeMatchers = new Array();
192 modeMatchers[0] =
193 {
194 regex: new RegExp("<style[^>]*>([\\s\\S]*?)(</style[^>]*>|$)", "i"),
195 modeExt: CodeMirror.modeExtensions["css"],
196 modeName: "css"
197 };
198 modeMatchers[1] =
199 {
200 regex: new RegExp("<script[^>]*>([\\s\\S]*?)(</script[^>]*>|$)", "i"),
201 modeExt: CodeMirror.modeExtensions["javascript"],
202 modeName: "javascript"
203 };
204
205 var lastCharPos = (typeof (absPos) !== "undefined" ? absPos : text.length - 1);
206 // Detect modes for the entire text
207 for (var i = 0; i < modeMatchers.length; i++) {
208 var curPos = 0;
209 while (curPos <= lastCharPos) {
210 var m = text.substr(curPos).match(modeMatchers[i].regex);
211 if (m != null) {
212 if (m.length > 1 && m[1].length > 0) {
213 // Push block begin pos
214 var blockBegin = curPos + m.index + m[0].indexOf(m[1]);
215 modeInfos.push(
216 {
217 pos: blockBegin,
218 modeExt: modeMatchers[i].modeExt,
219 modeName: modeMatchers[i].modeName
220 });
221 // Push block end pos
222 modeInfos.push(
223 {
224 pos: blockBegin + m[1].length,
225 modeExt: modeInfos[0].modeExt,
226 modeName: modeInfos[0].modeName
227 });
228 curPos += m.index + m[0].length;
229 continue;
230 }
231 else {
232 curPos += m.index + Math.max(m[0].length, 1);
233 }
234 }
235 else { // No more matches
236 break;
237 }
238 }
239 }
240 // Sort mode infos
241 modeInfos.sort(function sortModeInfo(a, b) {
242 return a.pos - b.pos;
243 });
244
245 return modeInfos;
246 },
247
248 autoFormatLineBreaks: function (text, startPos, endPos) {
249 var modeInfos = this.getModeInfos(text);
250 var reBlockStartsWithNewline = new RegExp("^\\s*?\n");
251 var reBlockEndsWithNewline = new RegExp("\n\\s*?$");
252 var res = "";
253 // Use modes info to break lines correspondingly
254 if (modeInfos.length > 1) { // Deal with multi-mode text
255 for (var i = 1; i <= modeInfos.length; i++) {
256 var selStart = modeInfos[i - 1].pos;
257 var selEnd = (i < modeInfos.length ? modeInfos[i].pos : endPos);
258
259 if (selStart >= endPos) { // The block starts later than the needed fragment
260 break;
261 }
262 if (selStart < startPos) {
263 if (selEnd <= startPos) { // The block starts earlier than the needed fragment
264 continue;
265 }
266 selStart = startPos;
267 }
268 if (selEnd > endPos) {
269 selEnd = endPos;
270 }
271 var textPortion = text.substring(selStart, selEnd);
272 if (modeInfos[i - 1].modeName != "xml") { // Starting a CSS or JavaScript block
273 if (!reBlockStartsWithNewline.test(textPortion)
274 && selStart > 0) { // The block does not start with a line break
275 textPortion = "\n" + textPortion;
276 }
277 if (!reBlockEndsWithNewline.test(textPortion)
278 && selEnd < text.length - 1) { // The block does not end with a line break
279 textPortion += "\n";
280 }
281 }
282 res += modeInfos[i - 1].modeExt.autoFormatLineBreaks(textPortion);
283 }
284 }
285 else { // Single-mode text
286 res = modeInfos[0].modeExt.autoFormatLineBreaks(text.substring(startPos, endPos));
287 }
288
289 return res;
290 }
291 };
@@ -0,0 +1,83 b''
1 (function () {
2 function forEach(arr, f) {
3 for (var i = 0, e = arr.length; i < e; ++i) f(arr[i]);
4 }
5
6 function arrayContains(arr, item) {
7 if (!Array.prototype.indexOf) {
8 var i = arr.length;
9 while (i--) {
10 if (arr[i] === item) {
11 return true;
12 }
13 }
14 return false;
15 }
16 return arr.indexOf(item) != -1;
17 }
18
19 CodeMirror.javascriptHint = function(editor) {
20 // Find the token at the cursor
21 var cur = editor.getCursor(), token = editor.getTokenAt(cur), tprop = token;
22 // If it's not a 'word-style' token, ignore the token.
23 if (!/^[\w$_]*$/.test(token.string)) {
24 token = tprop = {start: cur.ch, end: cur.ch, string: "", state: token.state,
25 className: token.string == "." ? "property" : null};
26 }
27 // If it is a property, find out what it is a property of.
28 while (tprop.className == "property") {
29 tprop = editor.getTokenAt({line: cur.line, ch: tprop.start});
30 if (tprop.string != ".") return;
31 tprop = editor.getTokenAt({line: cur.line, ch: tprop.start});
32 if (!context) var context = [];
33 context.push(tprop);
34 }
35 return {list: getCompletions(token, context),
36 from: {line: cur.line, ch: token.start},
37 to: {line: cur.line, ch: token.end}};
38 }
39
40 var stringProps = ("charAt charCodeAt indexOf lastIndexOf substring substr slice trim trimLeft trimRight " +
41 "toUpperCase toLowerCase split concat match replace search").split(" ");
42 var arrayProps = ("length concat join splice push pop shift unshift slice reverse sort indexOf " +
43 "lastIndexOf every some filter forEach map reduce reduceRight ").split(" ");
44 var funcProps = "prototype apply call bind".split(" ");
45 var keywords = ("break case catch continue debugger default delete do else false finally for function " +
46 "if in instanceof new null return switch throw true try typeof var void while with").split(" ");
47
48 function getCompletions(token, context) {
49 var found = [], start = token.string;
50 function maybeAdd(str) {
51 if (str.indexOf(start) == 0 && !arrayContains(found, str)) found.push(str);
52 }
53 function gatherCompletions(obj) {
54 if (typeof obj == "string") forEach(stringProps, maybeAdd);
55 else if (obj instanceof Array) forEach(arrayProps, maybeAdd);
56 else if (obj instanceof Function) forEach(funcProps, maybeAdd);
57 for (var name in obj) maybeAdd(name);
58 }
59
60 if (context) {
61 // If this is a property, see if it belongs to some object we can
62 // find in the current environment.
63 var obj = context.pop(), base;
64 if (obj.className == "variable")
65 base = window[obj.string];
66 else if (obj.className == "string")
67 base = "";
68 else if (obj.className == "atom")
69 base = 1;
70 while (base != null && context.length)
71 base = base[context.pop().string];
72 if (base != null) gatherCompletions(base);
73 }
74 else {
75 // If not, just look in the window object and any local scope
76 // (reading into JS mode internals to get at the local variables)
77 for (var v = token.state.localVars; v; v = v.next) maybeAdd(v.name);
78 gatherCompletions(window);
79 forEach(keywords, maybeAdd);
80 }
81 return found;
82 }
83 })();
@@ -0,0 +1,114 b''
1 // Define search commands. Depends on dialog.js or another
2 // implementation of the openDialog method.
3
4 // Replace works a little oddly -- it will do the replace on the next
5 // Ctrl-G (or whatever is bound to findNext) press. You prevent a
6 // replace by making sure the match is no longer selected when hitting
7 // Ctrl-G.
8
9 (function() {
10 function SearchState() {
11 this.posFrom = this.posTo = this.query = null;
12 this.marked = [];
13 }
14 function getSearchState(cm) {
15 return cm._searchState || (cm._searchState = new SearchState());
16 }
17 function dialog(cm, text, shortText, f) {
18 if (cm.openDialog) cm.openDialog(text, f);
19 else f(prompt(shortText, ""));
20 }
21 function confirmDialog(cm, text, shortText, fs) {
22 if (cm.openConfirm) cm.openConfirm(text, fs);
23 else if (confirm(shortText)) fs[0]();
24 }
25 function parseQuery(query) {
26 var isRE = query.match(/^\/(.*)\/$/);
27 return isRE ? new RegExp(isRE[1]) : query;
28 }
29 var queryDialog =
30 'Search: <input type="text" style="width: 10em"> <span style="color: #888">(Use /re/ syntax for regexp search)</span>';
31 function doSearch(cm, rev) {
32 var state = getSearchState(cm);
33 if (state.query) return findNext(cm, rev);
34 dialog(cm, queryDialog, "Search for:", function(query) {
35 cm.operation(function() {
36 if (!query || state.query) return;
37 state.query = parseQuery(query);
38 if (cm.lineCount() < 2000) { // This is too expensive on big documents.
39 for (var cursor = cm.getSearchCursor(query); cursor.findNext();)
40 state.marked.push(cm.markText(cursor.from(), cursor.to(), "CodeMirror-searching"));
41 }
42 state.posFrom = state.posTo = cm.getCursor();
43 findNext(cm, rev);
44 });
45 });
46 }
47 function findNext(cm, rev) {cm.operation(function() {
48 var state = getSearchState(cm);
49 var cursor = cm.getSearchCursor(state.query, rev ? state.posFrom : state.posTo);
50 if (!cursor.find(rev)) {
51 cursor = cm.getSearchCursor(state.query, rev ? {line: cm.lineCount() - 1} : {line: 0, ch: 0});
52 if (!cursor.find(rev)) return;
53 }
54 cm.setSelection(cursor.from(), cursor.to());
55 state.posFrom = cursor.from(); state.posTo = cursor.to();
56 })}
57 function clearSearch(cm) {cm.operation(function() {
58 var state = getSearchState(cm);
59 if (!state.query) return;
60 state.query = null;
61 for (var i = 0; i < state.marked.length; ++i) state.marked[i].clear();
62 state.marked.length = 0;
63 })}
64
65 var replaceQueryDialog =
66 'Replace: <input type="text" style="width: 10em"> <span style="color: #888">(Use /re/ syntax for regexp search)</span>';
67 var replacementQueryDialog = 'With: <input type="text" style="width: 10em">';
68 var doReplaceConfirm = "Replace? <button>Yes</button> <button>No</button> <button>Stop</button>";
69 function replace(cm, all) {
70 dialog(cm, replaceQueryDialog, "Replace:", function(query) {
71 if (!query) return;
72 query = parseQuery(query);
73 dialog(cm, replacementQueryDialog, "Replace with:", function(text) {
74 if (all) {
75 cm.operation(function() {
76 for (var cursor = cm.getSearchCursor(query); cursor.findNext();) {
77 if (typeof query != "string") {
78 var match = cm.getRange(cursor.from(), cursor.to()).match(query);
79 cursor.replace(text.replace(/\$(\d)/, function(w, i) {return match[i];}));
80 } else cursor.replace(text);
81 }
82 });
83 } else {
84 clearSearch(cm);
85 var cursor = cm.getSearchCursor(query, cm.getCursor());
86 function advance() {
87 var start = cursor.from(), match;
88 if (!(match = cursor.findNext())) {
89 cursor = cm.getSearchCursor(query);
90 if (!(match = cursor.findNext()) ||
91 (cursor.from().line == start.line && cursor.from().ch == start.ch)) return;
92 }
93 cm.setSelection(cursor.from(), cursor.to());
94 confirmDialog(cm, doReplaceConfirm, "Replace?",
95 [function() {doReplace(match);}, advance]);
96 }
97 function doReplace(match) {
98 cursor.replace(typeof query == "string" ? text :
99 text.replace(/\$(\d)/, function(w, i) {return match[i];}));
100 advance();
101 }
102 advance();
103 }
104 });
105 });
106 }
107
108 CodeMirror.commands.find = function(cm) {clearSearch(cm); doSearch(cm);};
109 CodeMirror.commands.findNext = doSearch;
110 CodeMirror.commands.findPrev = function(cm) {doSearch(cm, true);};
111 CodeMirror.commands.clearSearch = clearSearch;
112 CodeMirror.commands.replace = replace;
113 CodeMirror.commands.replaceAll = function(cm) {replace(cm, true);};
114 })();
@@ -0,0 +1,117 b''
1 (function(){
2 function SearchCursor(cm, query, pos, caseFold) {
3 this.atOccurrence = false; this.cm = cm;
4 if (caseFold == null) caseFold = typeof query == "string" && query == query.toLowerCase();
5
6 pos = pos ? cm.clipPos(pos) : {line: 0, ch: 0};
7 this.pos = {from: pos, to: pos};
8
9 // The matches method is filled in based on the type of query.
10 // It takes a position and a direction, and returns an object
11 // describing the next occurrence of the query, or null if no
12 // more matches were found.
13 if (typeof query != "string") // Regexp match
14 this.matches = function(reverse, pos) {
15 if (reverse) {
16 var line = cm.getLine(pos.line).slice(0, pos.ch), match = line.match(query), start = 0;
17 while (match) {
18 var ind = line.indexOf(match[0]);
19 start += ind;
20 line = line.slice(ind + 1);
21 var newmatch = line.match(query);
22 if (newmatch) match = newmatch;
23 else break;
24 start++;
25 }
26 }
27 else {
28 var line = cm.getLine(pos.line).slice(pos.ch), match = line.match(query),
29 start = match && pos.ch + line.indexOf(match[0]);
30 }
31 if (match)
32 return {from: {line: pos.line, ch: start},
33 to: {line: pos.line, ch: start + match[0].length},
34 match: match};
35 };
36 else { // String query
37 if (caseFold) query = query.toLowerCase();
38 var fold = caseFold ? function(str){return str.toLowerCase();} : function(str){return str;};
39 var target = query.split("\n");
40 // Different methods for single-line and multi-line queries
41 if (target.length == 1)
42 this.matches = function(reverse, pos) {
43 var line = fold(cm.getLine(pos.line)), len = query.length, match;
44 if (reverse ? (pos.ch >= len && (match = line.lastIndexOf(query, pos.ch - len)) != -1)
45 : (match = line.indexOf(query, pos.ch)) != -1)
46 return {from: {line: pos.line, ch: match},
47 to: {line: pos.line, ch: match + len}};
48 };
49 else
50 this.matches = function(reverse, pos) {
51 var ln = pos.line, idx = (reverse ? target.length - 1 : 0), match = target[idx], line = fold(cm.getLine(ln));
52 var offsetA = (reverse ? line.indexOf(match) + match.length : line.lastIndexOf(match));
53 if (reverse ? offsetA >= pos.ch || offsetA != match.length
54 : offsetA <= pos.ch || offsetA != line.length - match.length)
55 return;
56 for (;;) {
57 if (reverse ? !ln : ln == cm.lineCount() - 1) return;
58 line = fold(cm.getLine(ln += reverse ? -1 : 1));
59 match = target[reverse ? --idx : ++idx];
60 if (idx > 0 && idx < target.length - 1) {
61 if (line != match) return;
62 else continue;
63 }
64 var offsetB = (reverse ? line.lastIndexOf(match) : line.indexOf(match) + match.length);
65 if (reverse ? offsetB != line.length - match.length : offsetB != match.length)
66 return;
67 var start = {line: pos.line, ch: offsetA}, end = {line: ln, ch: offsetB};
68 return {from: reverse ? end : start, to: reverse ? start : end};
69 }
70 };
71 }
72 }
73
74 SearchCursor.prototype = {
75 findNext: function() {return this.find(false);},
76 findPrevious: function() {return this.find(true);},
77
78 find: function(reverse) {
79 var self = this, pos = this.cm.clipPos(reverse ? this.pos.from : this.pos.to);
80 function savePosAndFail(line) {
81 var pos = {line: line, ch: 0};
82 self.pos = {from: pos, to: pos};
83 self.atOccurrence = false;
84 return false;
85 }
86
87 for (;;) {
88 if (this.pos = this.matches(reverse, pos)) {
89 this.atOccurrence = true;
90 return this.pos.match || true;
91 }
92 if (reverse) {
93 if (!pos.line) return savePosAndFail(0);
94 pos = {line: pos.line-1, ch: this.cm.getLine(pos.line-1).length};
95 }
96 else {
97 var maxLine = this.cm.lineCount();
98 if (pos.line == maxLine - 1) return savePosAndFail(maxLine);
99 pos = {line: pos.line+1, ch: 0};
100 }
101 }
102 },
103
104 from: function() {if (this.atOccurrence) return this.pos.from;},
105 to: function() {if (this.atOccurrence) return this.pos.to;},
106
107 replace: function(newText) {
108 var self = this;
109 if (this.atOccurrence)
110 self.pos.to = this.cm.replaceRange(newText, self.pos.from, self.pos.to);
111 }
112 };
113
114 CodeMirror.defineExtension("getSearchCursor", function(query, pos, caseFold) {
115 return new SearchCursor(this, query, pos, caseFold);
116 });
117 })();
@@ -0,0 +1,16 b''
1 .CodeMirror-completions {
2 position: absolute;
3 z-index: 10;
4 overflow: hidden;
5 -webkit-box-shadow: 2px 3px 5px rgba(0,0,0,.2);
6 -moz-box-shadow: 2px 3px 5px rgba(0,0,0,.2);
7 box-shadow: 2px 3px 5px rgba(0,0,0,.2);
8 }
9 .CodeMirror-completions select {
10 background: #fafafa;
11 outline: none;
12 border: none;
13 padding: 0;
14 margin: 0;
15 font-family: monospace;
16 }
@@ -0,0 +1,66 b''
1 (function() {
2 CodeMirror.simpleHint = function(editor, getHints) {
3 // We want a single cursor position.
4 if (editor.somethingSelected()) return;
5 var result = getHints(editor);
6 if (!result || !result.list.length) return;
7 var completions = result.list;
8 function insert(str) {
9 editor.replaceRange(str, result.from, result.to);
10 }
11 // When there is only one completion, use it directly.
12 if (completions.length == 1) {insert(completions[0]); return true;}
13
14 // Build the select widget
15 var complete = document.createElement("div");
16 complete.className = "CodeMirror-completions";
17 var sel = complete.appendChild(document.createElement("select"));
18 // Opera doesn't move the selection when pressing up/down in a
19 // multi-select, but it does properly support the size property on
20 // single-selects, so no multi-select is necessary.
21 if (!window.opera) sel.multiple = true;
22 for (var i = 0; i < completions.length; ++i) {
23 var opt = sel.appendChild(document.createElement("option"));
24 opt.appendChild(document.createTextNode(completions[i]));
25 }
26 sel.firstChild.selected = true;
27 sel.size = Math.min(10, completions.length);
28 var pos = editor.cursorCoords();
29 complete.style.left = pos.x + "px";
30 complete.style.top = pos.yBot + "px";
31 document.body.appendChild(complete);
32 // Hack to hide the scrollbar.
33 if (completions.length <= 10)
34 complete.style.width = (sel.clientWidth - 1) + "px";
35
36 var done = false;
37 function close() {
38 if (done) return;
39 done = true;
40 complete.parentNode.removeChild(complete);
41 }
42 function pick() {
43 insert(completions[sel.selectedIndex]);
44 close();
45 setTimeout(function(){editor.focus();}, 50);
46 }
47 CodeMirror.connect(sel, "blur", close);
48 CodeMirror.connect(sel, "keydown", function(event) {
49 var code = event.keyCode;
50 // Enter
51 if (code == 13) {CodeMirror.e_stop(event); pick();}
52 // Escape
53 else if (code == 27) {CodeMirror.e_stop(event); close(); editor.focus();}
54 else if (code != 38 && code != 40) {
55 close(); editor.focus();
56 setTimeout(function(){CodeMirror.simpleHint(editor, getHints);}, 50);
57 }
58 });
59 CodeMirror.connect(sel, "dblclick", pick);
60
61 sel.focus();
62 // Opera sometimes ignores focusing a freshly created node
63 if (window.opera) setTimeout(function(){if (!done) sel.focus();}, 100);
64 return true;
65 };
66 })();
@@ -0,0 +1,18 b''
1 .cm-s-cobalt { background: #002240; color: white; }
2 .cm-s-cobalt span.CodeMirror-selected { background: #b36539 !important; }
3 .cm-s-cobalt .CodeMirror-gutter { background: #002240; border-right: 1px solid #aaa; }
4 .cm-s-cobalt .CodeMirror-gutter-text { color: #d0d0d0; }
5 .cm-s-cobalt .CodeMirror-cursor { border-left: 1px solid white !important; }
6
7 .cm-s-cobalt span.cm-comment { color: #08f; }
8 .cm-s-cobalt span.cm-atom { color: #845dc4; }
9 .cm-s-cobalt span.cm-number, .cm-s-cobalt span.cm-attribute { color: #ff80e1; }
10 .cm-s-cobalt span.cm-keyword { color: #ffee80; }
11 .cm-s-cobalt span.cm-string { color: #3ad900; }
12 .cm-s-cobalt span.cm-meta { color: #ff9d00; }
13 .cm-s-cobalt span.cm-variable-2, .cm-s-cobalt span.cm-tag { color: #9effff; }
14 .cm-s-cobalt span.cm-variable-3, .cm-s-cobalt span.cm-def { color: white; }
15 .cm-s-cobalt span.cm-error { color: #9d1e15; }
16 .cm-s-cobalt span.cm-bracket { color: #d8d8d8; }
17 .cm-s-cobalt span.cm-builtin, .cm-s-cobalt span.cm-special { color: #ff9e59; }
18 .cm-s-cobalt span.cm-link { color: #845dc4; }
@@ -0,0 +1,25 b''
1 .cm-s-eclipse span.cm-meta {color: #FF1717;}
2 .cm-s-eclipse span.cm-keyword { font-weight: bold; color: #7F0055; }
3 .cm-s-eclipse span.cm-atom {color: #219;}
4 .cm-s-eclipse span.cm-number {color: #164;}
5 .cm-s-eclipse span.cm-def {color: #00f;}
6 .cm-s-eclipse span.cm-variable {color: black;}
7 .cm-s-eclipse span.cm-variable-2 {color: #0000C0;}
8 .cm-s-eclipse span.cm-variable-3 {color: #0000C0;}
9 .cm-s-eclipse span.cm-property {color: black;}
10 .cm-s-eclipse span.cm-operator {color: black;}
11 .cm-s-eclipse span.cm-comment {color: #3F7F5F;}
12 .cm-s-eclipse span.cm-string {color: #2A00FF;}
13 .cm-s-eclipse span.cm-string-2 {color: #f50;}
14 .cm-s-eclipse span.cm-error {color: #f00;}
15 .cm-s-eclipse span.cm-qualifier {color: #555;}
16 .cm-s-eclipse span.cm-builtin {color: #30a;}
17 .cm-s-eclipse span.cm-bracket {color: #cc7;}
18 .cm-s-eclipse span.cm-tag {color: #170;}
19 .cm-s-eclipse span.cm-attribute {color: #00c;}
20 .cm-s-eclipse span.cm-link {color: #219;}
21
22 .cm-s-eclipse .CodeMirror-matchingbracket {
23 border:1px solid grey;
24 color:black !important;;
25 }
@@ -0,0 +1,28 b''
1 /* Based on Sublime Text's Monokai theme */
2
3 .cm-s-monokai {background: #272822; color: #f8f8f2;}
4 .cm-s-monokai span.CodeMirror-selected {background: #ffe792 !important;}
5 .cm-s-monokai .CodeMirror-gutter {background: #272822; border-right: 0px;}
6 .cm-s-monokai .CodeMirror-gutter-text {color: #d0d0d0;}
7 .cm-s-monokai .CodeMirror-cursor {border-left: 1px solid #f8f8f0 !important;}
8
9 .cm-s-monokai span.cm-comment {color: #75715e;}
10 .cm-s-monokai span.cm-atom {color: #ae81ff;}
11 .cm-s-monokai span.cm-number {color: #ae81ff;}
12
13 .cm-s-monokai span.cm-property, .cm-s-monokai span.cm-attribute {color: #a6e22e;}
14 .cm-s-monokai span.cm-keyword {color: #f92672;}
15 .cm-s-monokai span.cm-string {color: #e6db74;}
16
17 .cm-s-monokai span.cm-variable {color: #a6e22e;}
18 .cm-s-monokai span.cm-variable-2 {color: #9effff;}
19 .cm-s-monokai span.cm-def {color: #fd971f;}
20 .cm-s-monokai span.cm-error {background: #f92672; color: #f8f8f0;}
21 .cm-s-monokai span.cm-bracket {color: #f8f8f2;}
22 .cm-s-monokai span.cm-tag {color: #f92672;}
23 .cm-s-monokai span.cm-link {color: #ae81ff;}
24
25 .cm-s-monokai .CodeMirror-matchingbracket {
26 text-decoration: underline;
27 color: white !important;
28 }
@@ -0,0 +1,21 b''
1 .cm-s-rubyblue { font:13px/1.4em Trebuchet, Verdana, sans-serif; } /* - customized editor font - */
2
3 .cm-s-rubyblue { background: #112435; color: white; }
4 .cm-s-rubyblue span.CodeMirror-selected { background: #0000FF !important; }
5 .cm-s-rubyblue .CodeMirror-gutter { background: #1F4661; border-right: 7px solid #3E7087; min-width:2.5em; }
6 .cm-s-rubyblue .CodeMirror-gutter-text { color: white; }
7 .cm-s-rubyblue .CodeMirror-cursor { border-left: 1px solid white !important; }
8
9 .cm-s-rubyblue span.cm-comment { color: #999; font-style:italic; }
10 .cm-s-rubyblue span.cm-atom { color: #F4C20B; }
11 .cm-s-rubyblue span.cm-number, .cm-s-rubyblue span.cm-attribute { color: #82C6E0; }
12 .cm-s-rubyblue span.cm-keyword { color: #F0F; }
13 .cm-s-rubyblue span.cm-string { color: #F08047; }
14 .cm-s-rubyblue span.cm-meta { color: #F0F; }
15 .cm-s-rubyblue span.cm-variable-2, .cm-s-rubyblue span.cm-tag { color: #7BD827; }
16 .cm-s-rubyblue span.cm-variable-3, .cm-s-rubyblue span.cm-def { color: white; }
17 .cm-s-rubyblue span.cm-error { color: #AF2018; }
18 .cm-s-rubyblue span.cm-bracket { color: #F0F; }
19 .cm-s-rubyblue span.cm-link { color: #F4C20B; }
20 .cm-s-rubyblue span.CodeMirror-matchingbracket { color:#F0F !important; }
21 .cm-s-rubyblue span.cm-builtin, .cm-s-rubyblue span.cm-special { color: #FF9D00; }
@@ -1,33 +1,33 b''
1 =======================
1 =======================
2 CodeMirror in IPython
2 CodeMirror in IPython
3 =======================
3 =======================
4
4
5 We carry a mostly unmodified copy of CodeMirror. The current version we use
5 We carry a mostly unmodified copy of CodeMirror. The current version we use
6 is (*please update this information when updating versions*)::
6 is (*please update this information when updating versions*)::
7
7
8 CodeMirror 2.15
8 CodeMirror 2.2
9
9
10 The only changes we've applied so far are these::
10 The only changes we've applied so far are these::
11
11
12 diff --git a/IPython/frontend/html/notebook/static/codemirror/mode/python/python.js b/IPython/frontend/html/notebook/static/codemirror/mode/python/python.js
12 diff --git a/IPython/frontend/html/notebook/static/codemirror/mode/python/python.js b/IPython/frontend/html/notebook/static/codemirror/mode/python/python.js
13 index ca94e7a..fc9a503 100644
13 index ca94e7a..fc9a503 100644
14 --- a/IPython/frontend/html/notebook/static/codemirror/mode/python/python.js
14 --- a/IPython/frontend/html/notebook/static/codemirror/mode/python/python.js
15 +++ b/IPython/frontend/html/notebook/static/codemirror/mode/python/python.js
15 +++ b/IPython/frontend/html/notebook/static/codemirror/mode/python/python.js
16 @@ -5,7 +5,11 @@ CodeMirror.defineMode("python", function(conf, parserConf) {
16 @@ -5,7 +5,11 @@ CodeMirror.defineMode("python", function(conf, parserConf) {
17 return new RegExp("^((" + words.join(")|(") + "))\\b");
17 return new RegExp("^((" + words.join(")|(") + "))\\b");
18 }
18 }
19
19
20 - var singleOperators = new RegExp("^[\\+\\-\\*/%&|\\^~<>!]");
20 - var singleOperators = new RegExp("^[\\+\\-\\*/%&|\\^~<>!]");
21 + // IPython-specific changes: add '?' as recognized character.
21 + // IPython-specific changes: add '?' as recognized character.
22 + //var singleOperators = new RegExp("^[\\+\\-\\*/%&|\\^~<>!]");
22 + //var singleOperators = new RegExp("^[\\+\\-\\*/%&|\\^~<>!]");
23 + var singleOperators = new RegExp("^[\\+\\-\\*/%&|\\^~<>!\\?]");
23 + var singleOperators = new RegExp("^[\\+\\-\\*/%&|\\^~<>!\\?]");
24 + // End IPython changes.
24 + // End IPython changes.
25 +
25 +
26 var singleDelimiters = new RegExp('^[\\(\\)\\[\\]\\{\\}@,:`=;\\.]');
26 var singleDelimiters = new RegExp('^[\\(\\)\\[\\]\\{\\}@,:`=;\\.]');
27 var doubleOperators = new RegExp("^((==)|(!=)|(<=)|(>=)|(<>)|(<<)|(>>)|(//)|(\\*\\*))");
27 var doubleOperators = new RegExp("^((==)|(!=)|(<=)|(>=)|(<>)|(<<)|(>>)|(//)|(\\*\\*))");
28 var doubleDelimiters = new RegExp("^((\\+=)|(\\-=)|(\\*=)|(%=)|(/=)|(&=)|(\\|=)|(\\^=))");
28 var doubleDelimiters = new RegExp("^((\\+=)|(\\-=)|(\\*=)|(%=)|(/=)|(&=)|(\\|=)|(\\^=))");
29
29
30
30
31 In practice it's just a one-line change, adding `\\?` to singleOperators,
31 In practice it's just a one-line change, adding `\\?` to singleOperators,
32 surrounded by a comment. We'll turn this into a proper patchset if it ever
32 surrounded by a comment. We'll turn this into a proper patchset if it ever
33 gets more complicated than this, but for now this note should be enough.
33 gets more complicated than this, but for now this note should be enough.
@@ -1,6 +1,6 b''
1 # CodeMirror 2
1 # CodeMirror 2
2
2
3 CodeMirror 2 is a rewrite of [CodeMirror
3 CodeMirror 2 is a rewrite of [CodeMirror
4 1](http://github.com/marijnh/CodeMirror). The docs live
4 1](http://github.com/marijnh/CodeMirror). The docs live
5 [here](http://codemirror.net/manual.html), and the project page is
5 [here](http://codemirror.net/doc/manual.html), and the project page is
6 [http://codemirror.net/](http://codemirror.net/).
6 [http://codemirror.net/](http://codemirror.net/).
@@ -1,68 +1,104 b''
1 .CodeMirror {
1 .CodeMirror {
2 line-height: 1em;
2 line-height: 1em;
3 font-family: monospace;
3 font-family: monospace;
4 }
4 }
5
5
6 .CodeMirror-scroll {
6 .CodeMirror-scroll {
7 overflow: auto;
7 overflow: auto;
8 height: 300px;
8 height: 300px;
9 /* This is needed to prevent an IE[67] bug where the scrolled content
9 /* This is needed to prevent an IE[67] bug where the scrolled content
10 is visible outside of the scrolling box. */
10 is visible outside of the scrolling box. */
11 position: relative;
11 position: relative;
12 }
12 }
13
13
14 .CodeMirror-gutter {
14 .CodeMirror-gutter {
15 position: absolute; left: 0; top: 0;
15 position: absolute; left: 0; top: 0;
16 z-index: 10;
16 z-index: 10;
17 background-color: #f7f7f7;
17 background-color: #f7f7f7;
18 border-right: 1px solid #eee;
18 border-right: 1px solid #eee;
19 min-width: 2em;
19 min-width: 2em;
20 height: 100%;
20 height: 100%;
21 }
21 }
22 .CodeMirror-gutter-text {
22 .CodeMirror-gutter-text {
23 color: #aaa;
23 color: #aaa;
24 text-align: right;
24 text-align: right;
25 padding: .4em .2em .4em .4em;
25 padding: .4em .2em .4em .4em;
26 white-space: pre !important;
26 }
27 }
27 .CodeMirror-lines {
28 .CodeMirror-lines {
28 padding: .4em;
29 padding: .4em;
29 }
30 }
30
31
31 .CodeMirror pre {
32 .CodeMirror pre {
32 -moz-border-radius: 0;
33 -moz-border-radius: 0;
33 -webkit-border-radius: 0;
34 -webkit-border-radius: 0;
34 -o-border-radius: 0;
35 -o-border-radius: 0;
35 border-radius: 0;
36 border-radius: 0;
36 border-width: 0; margin: 0; padding: 0; background: transparent;
37 border-width: 0; margin: 0; padding: 0; background: transparent;
37 font-family: inherit;
38 font-family: inherit;
38 font-size: inherit;
39 font-size: inherit;
39 padding: 0; margin: 0;
40 padding: 0; margin: 0;
40 white-space: pre;
41 white-space: pre;
41 word-wrap: normal;
42 word-wrap: normal;
42 }
43 }
43
44
45 .CodeMirror-wrap pre {
46 word-wrap: break-word;
47 white-space: pre-wrap;
48 }
49 .CodeMirror-wrap .CodeMirror-scroll {
50 overflow-x: hidden;
51 }
52
44 .CodeMirror textarea {
53 .CodeMirror textarea {
45 font-family: inherit !important;
54 outline: none !important;
46 font-size: inherit !important;
47 }
55 }
48
56
49 .CodeMirror-cursor {
57 .CodeMirror pre.CodeMirror-cursor {
50 z-index: 10;
58 z-index: 10;
51 position: absolute;
59 position: absolute;
52 visibility: hidden;
60 visibility: hidden;
53 border-left: 1px solid black !important;
61 border-left: 1px solid black;
54 }
62 }
55 .CodeMirror-focused .CodeMirror-cursor {
63 .CodeMirror-focused pre.CodeMirror-cursor {
56 visibility: visible;
64 visibility: visible;
57 }
65 }
58
66
59 span.CodeMirror-selected {
67 span.CodeMirror-selected { background: #d9d9d9; }
60 background: #ccc !important;
68 .CodeMirror-focused span.CodeMirror-selected { background: #d2dcf8; }
61 color: HighlightText !important;
69
62 }
70 .CodeMirror-searching {background: #ffa;}
63 .CodeMirror-focused span.CodeMirror-selected {
71
64 background: Highlight !important;
72 /* Default theme */
65 }
73
74 .cm-s-default span.cm-keyword {color: #708;}
75 .cm-s-default span.cm-atom {color: #219;}
76 .cm-s-default span.cm-number {color: #164;}
77 .cm-s-default span.cm-def {color: #00f;}
78 .cm-s-default span.cm-variable {color: black;}
79 .cm-s-default span.cm-variable-2 {color: #05a;}
80 .cm-s-default span.cm-variable-3 {color: #085;}
81 .cm-s-default span.cm-property {color: black;}
82 .cm-s-default span.cm-operator {color: black;}
83 .cm-s-default span.cm-comment {color: #a50;}
84 .cm-s-default span.cm-string {color: #a11;}
85 .cm-s-default span.cm-string-2 {color: #f50;}
86 .cm-s-default span.cm-meta {color: #555;}
87 .cm-s-default span.cm-error {color: #f00;}
88 .cm-s-default span.cm-qualifier {color: #555;}
89 .cm-s-default span.cm-builtin {color: #30a;}
90 .cm-s-default span.cm-bracket {color: #cc7;}
91 .cm-s-default span.cm-tag {color: #170;}
92 .cm-s-default span.cm-attribute {color: #00c;}
93 .cm-s-default span.cm-header {color: #a0a;}
94 .cm-s-default span.cm-quote {color: #090;}
95 .cm-s-default span.cm-hr {color: #999;}
96 .cm-s-default span.cm-link {color: #00c;}
97
98 span.cm-header, span.cm-strong {font-weight: bold;}
99 span.cm-em {font-style: italic;}
100 span.cm-emstrong {font-style: italic; font-weight: bold;}
101 span.cm-link {text-decoration: underline;}
66
102
67 .CodeMirror-matchingbracket {color: #0f0 !important;}
103 div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
68 .CodeMirror-nonmatchingbracket {color: #f22 !important;}
104 div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
This diff has been collapsed as it changes many lines, (2116 lines changed) Show them Hide them
@@ -1,2197 +1,2761 b''
1 // CodeMirror version 2.2
2 //
1 // All functions that need access to the editor's state live inside
3 // All functions that need access to the editor's state live inside
2 // the CodeMirror function. Below that, at the bottom of the file,
4 // the CodeMirror function. Below that, at the bottom of the file,
3 // some utilities are defined.
5 // some utilities are defined.
4
6
5 // CodeMirror is the only global var we claim
7 // CodeMirror is the only global var we claim
6 var CodeMirror = (function() {
8 var CodeMirror = (function() {
7 // This is the function that produces an editor instance. It's
9 // This is the function that produces an editor instance. It's
8 // closure is used to store the editor state.
10 // closure is used to store the editor state.
9 function CodeMirror(place, givenOptions) {
11 function CodeMirror(place, givenOptions) {
10 // Determine effective options based on given values and defaults.
12 // Determine effective options based on given values and defaults.
11 var options = {}, defaults = CodeMirror.defaults;
13 var options = {}, defaults = CodeMirror.defaults;
12 for (var opt in defaults)
14 for (var opt in defaults)
13 if (defaults.hasOwnProperty(opt))
15 if (defaults.hasOwnProperty(opt))
14 options[opt] = (givenOptions && givenOptions.hasOwnProperty(opt) ? givenOptions : defaults)[opt];
16 options[opt] = (givenOptions && givenOptions.hasOwnProperty(opt) ? givenOptions : defaults)[opt];
15
17
16 var targetDocument = options["document"];
18 var targetDocument = options["document"];
17 // The element in which the editor lives.
19 // The element in which the editor lives.
18 var wrapper = targetDocument.createElement("div");
20 var wrapper = targetDocument.createElement("div");
19 wrapper.className = "CodeMirror";
21 wrapper.className = "CodeMirror" + (options.lineWrapping ? " CodeMirror-wrap" : "");
20 // This mess creates the base DOM structure for the editor.
22 // This mess creates the base DOM structure for the editor.
21 wrapper.innerHTML =
23 wrapper.innerHTML =
22 '<div style="overflow: hidden; position: relative; width: 1px; height: 0px;">' + // Wraps and hides input textarea
24 '<div style="overflow: hidden; position: relative; width: 3px; height: 0px;">' + // Wraps and hides input textarea
23 '<textarea style="position: absolute; width: 2px;" wrap="off" ' +
25 '<textarea style="position: absolute; padding: 0; width: 1px;" wrap="off" ' +
24 'autocorrect="off" autocapitalize="off"></textarea></div>' +
26 'autocorrect="off" autocapitalize="off"></textarea></div>' +
25 '<div class="CodeMirror-scroll cm-s-' + options.theme + '">' +
27 '<div class="CodeMirror-scroll" tabindex="-1">' +
26 '<div style="position: relative">' + // Set to the height of the text, causes scrolling
28 '<div style="position: relative">' + // Set to the height of the text, causes scrolling
27 '<div style="position: absolute; height: 0; width: 0; overflow: hidden;"></div>' +
28 '<div style="position: relative">' + // Moved around its parent to cover visible view
29 '<div style="position: relative">' + // Moved around its parent to cover visible view
29 '<div class="CodeMirror-gutter"><div class="CodeMirror-gutter-text"></div></div>' +
30 '<div class="CodeMirror-gutter"><div class="CodeMirror-gutter-text"></div></div>' +
30 // Provides positioning relative to (visible) text origin
31 // Provides positioning relative to (visible) text origin
31 '<div class="CodeMirror-lines"><div style="position: relative" draggable="true">' +
32 '<div class="CodeMirror-lines"><div style="position: relative">' +
33 '<div style="position: absolute; width: 100%; height: 0; overflow: hidden; visibility: hidden"></div>' +
32 '<pre class="CodeMirror-cursor">&#160;</pre>' + // Absolutely positioned blinky cursor
34 '<pre class="CodeMirror-cursor">&#160;</pre>' + // Absolutely positioned blinky cursor
33 '<div></div>' + // This DIV contains the actual code
35 '<div></div>' + // This DIV contains the actual code
34 '</div></div></div></div></div>';
36 '</div></div></div></div></div>';
35 if (place.appendChild) place.appendChild(wrapper); else place(wrapper);
37 if (place.appendChild) place.appendChild(wrapper); else place(wrapper);
36 // I've never seen more elegant code in my life.
38 // I've never seen more elegant code in my life.
37 var inputDiv = wrapper.firstChild, input = inputDiv.firstChild,
39 var inputDiv = wrapper.firstChild, input = inputDiv.firstChild,
38 scroller = wrapper.lastChild, code = scroller.firstChild,
40 scroller = wrapper.lastChild, code = scroller.firstChild,
39 measure = code.firstChild, mover = measure.nextSibling,
41 mover = code.firstChild, gutter = mover.firstChild, gutterText = gutter.firstChild,
40 gutter = mover.firstChild, gutterText = gutter.firstChild,
42 lineSpace = gutter.nextSibling.firstChild, measure = lineSpace.firstChild,
41 lineSpace = gutter.nextSibling.firstChild,
43 cursor = measure.nextSibling, lineDiv = cursor.nextSibling;
42 cursor = lineSpace.firstChild, lineDiv = cursor.nextSibling;
44 themeChanged();
43 if (options.tabindex != null) input.tabindex = options.tabindex;
45 // Needed to hide big blue blinking cursor on Mobile Safari
46 if (/AppleWebKit/.test(navigator.userAgent) && /Mobile\/\w+/.test(navigator.userAgent)) input.style.width = "0px";
47 if (!webkit) lineSpace.draggable = true;
48 if (options.tabindex != null) input.tabIndex = options.tabindex;
44 if (!options.gutter && !options.lineNumbers) gutter.style.display = "none";
49 if (!options.gutter && !options.lineNumbers) gutter.style.display = "none";
45
50
51 // Check for problem with IE innerHTML not working when we have a
52 // P (or similar) parent node.
53 try { stringWidth("x"); }
54 catch (e) {
55 if (e.message.match(/runtime/i))
56 e = new Error("A CodeMirror inside a P-style element does not work in Internet Explorer. (innerHTML bug)");
57 throw e;
58 }
59
46 // Delayed object wrap timeouts, making sure only one is active. blinker holds an interval.
60 // Delayed object wrap timeouts, making sure only one is active. blinker holds an interval.
47 var poll = new Delayed(), highlight = new Delayed(), blinker;
61 var poll = new Delayed(), highlight = new Delayed(), blinker;
48
62
49 // mode holds a mode API object. lines an array of Line objects
63 // mode holds a mode API object. doc is the tree of Line objects,
50 // (see Line constructor), work an array of lines that should be
64 // work an array of lines that should be parsed, and history the
51 // parsed, and history the undo history (instance of History
65 // undo history (instance of History constructor).
52 // constructor).
66 var mode, doc = new BranchChunk([new LeafChunk([new Line("")])]), work, focused;
53 var mode, lines = [new Line("")], work, history = new History(), focused;
54 loadMode();
67 loadMode();
55 // The selection. These are always maintained to point at valid
68 // The selection. These are always maintained to point at valid
56 // positions. Inverted is used to remember that the user is
69 // positions. Inverted is used to remember that the user is
57 // selecting bottom-to-top.
70 // selecting bottom-to-top.
58 var sel = {from: {line: 0, ch: 0}, to: {line: 0, ch: 0}, inverted: false};
71 var sel = {from: {line: 0, ch: 0}, to: {line: 0, ch: 0}, inverted: false};
59 // Selection-related flags. shiftSelecting obviously tracks
72 // Selection-related flags. shiftSelecting obviously tracks
60 // whether the user is holding shift. reducedSelection is a hack
73 // whether the user is holding shift.
61 // to get around the fact that we can't create inverted
74 var shiftSelecting, lastClick, lastDoubleClick, draggingText, overwrite = false;
62 // selections. See below.
63 var shiftSelecting, reducedSelection, lastClick, lastDoubleClick;
64 // Variables used by startOperation/endOperation to track what
75 // Variables used by startOperation/endOperation to track what
65 // happened during the operation.
76 // happened during the operation.
66 var updateInput, changes, textChanged, selectionChanged, leaveInputAlone, gutterDirty;
77 var updateInput, userSelChange, changes, textChanged, selectionChanged, leaveInputAlone,
78 gutterDirty, callbacks;
67 // Current visible range (may be bigger than the view window).
79 // Current visible range (may be bigger than the view window).
68 var showingFrom = 0, showingTo = 0, lastHeight = 0, curKeyId = null;
80 var displayOffset = 0, showingFrom = 0, showingTo = 0, lastSizeC = 0;
69 // editing will hold an object describing the things we put in the
70 // textarea, to help figure out whether something changed.
71 // bracketHighlighted is used to remember that a backet has been
81 // bracketHighlighted is used to remember that a backet has been
72 // marked.
82 // marked.
73 var editing, bracketHighlighted;
83 var bracketHighlighted;
74 // Tracks the maximum line length so that the horizontal scrollbar
84 // Tracks the maximum line length so that the horizontal scrollbar
75 // can be kept static when scrolling.
85 // can be kept static when scrolling.
76 var maxLine = "", maxWidth;
86 var maxLine = "", maxWidth, tabText = computeTabText();
77
87
78 // Initialize the content.
88 // Initialize the content.
79 operation(function(){setValue(options.value || ""); updateInput = false;})();
89 operation(function(){setValue(options.value || ""); updateInput = false;})();
90 var history = new History();
80
91
81 // Register our event handlers.
92 // Register our event handlers.
82 connect(scroller, "mousedown", operation(onMouseDown));
93 connect(scroller, "mousedown", operation(onMouseDown));
94 connect(scroller, "dblclick", operation(onDoubleClick));
83 connect(lineSpace, "dragstart", onDragStart);
95 connect(lineSpace, "dragstart", onDragStart);
96 connect(lineSpace, "selectstart", e_preventDefault);
84 // Gecko browsers fire contextmenu *after* opening the menu, at
97 // Gecko browsers fire contextmenu *after* opening the menu, at
85 // which point we can't mess with it anymore. Context menu is
98 // which point we can't mess with it anymore. Context menu is
86 // handled in onMouseDown for Gecko.
99 // handled in onMouseDown for Gecko.
87 if (!gecko) connect(scroller, "contextmenu", onContextMenu);
100 if (!gecko) connect(scroller, "contextmenu", onContextMenu);
88 connect(scroller, "scroll", function() {
101 connect(scroller, "scroll", function() {
89 updateDisplay([]);
102 updateDisplay([]);
90 if (options.fixedGutter) gutter.style.left = scroller.scrollLeft + "px";
103 if (options.fixedGutter) gutter.style.left = scroller.scrollLeft + "px";
91 if (options.onScroll) options.onScroll(instance);
104 if (options.onScroll) options.onScroll(instance);
92 });
105 });
93 connect(window, "resize", function() {updateDisplay(true);});
106 connect(window, "resize", function() {updateDisplay(true);});
94 connect(input, "keyup", operation(onKeyUp));
107 connect(input, "keyup", operation(onKeyUp));
108 connect(input, "input", fastPoll);
95 connect(input, "keydown", operation(onKeyDown));
109 connect(input, "keydown", operation(onKeyDown));
96 connect(input, "keypress", operation(onKeyPress));
110 connect(input, "keypress", operation(onKeyPress));
97 connect(input, "focus", onFocus);
111 connect(input, "focus", onFocus);
98 connect(input, "blur", onBlur);
112 connect(input, "blur", onBlur);
99
113
100 connect(scroller, "dragenter", e_stop);
114 connect(scroller, "dragenter", e_stop);
101 connect(scroller, "dragover", e_stop);
115 connect(scroller, "dragover", e_stop);
102 connect(scroller, "drop", operation(onDrop));
116 connect(scroller, "drop", operation(onDrop));
103 connect(scroller, "paste", function(){focusInput(); fastPoll();});
117 connect(scroller, "paste", function(){focusInput(); fastPoll();});
104 connect(input, "paste", function(){fastPoll();});
118 connect(input, "paste", fastPoll);
105 connect(input, "cut", function(){fastPoll();});
119 connect(input, "cut", operation(function(){replaceSelection("");}));
106
120
107 // IE throws unspecified error in certain cases, when
121 // IE throws unspecified error in certain cases, when
108 // trying to access activeElement before onload
122 // trying to access activeElement before onload
109 var hasFocus; try { hasFocus = (targetDocument.activeElement == input); } catch(e) { }
123 var hasFocus; try { hasFocus = (targetDocument.activeElement == input); } catch(e) { }
110 if (hasFocus) setTimeout(onFocus, 20);
124 if (hasFocus) setTimeout(onFocus, 20);
111 else onBlur();
125 else onBlur();
112
126
113 function isLine(l) {return l >= 0 && l < lines.length;}
127 function isLine(l) {return l >= 0 && l < doc.size;}
114 // The instance object that we'll return. Mostly calls out to
128 // The instance object that we'll return. Mostly calls out to
115 // local functions in the CodeMirror function. Some do some extra
129 // local functions in the CodeMirror function. Some do some extra
116 // range checking and/or clipping. operation is used to wrap the
130 // range checking and/or clipping. operation is used to wrap the
117 // call so that changes it makes are tracked, and the display is
131 // call so that changes it makes are tracked, and the display is
118 // updated afterwards.
132 // updated afterwards.
119 var instance = wrapper.CodeMirror = {
133 var instance = wrapper.CodeMirror = {
120 getValue: getValue,
134 getValue: getValue,
121 setValue: operation(setValue),
135 setValue: operation(setValue),
122 getSelection: getSelection,
136 getSelection: getSelection,
123 replaceSelection: operation(replaceSelection),
137 replaceSelection: operation(replaceSelection),
124 focus: function(){focusInput(); onFocus(); fastPoll();},
138 focus: function(){focusInput(); onFocus(); fastPoll();},
125 setOption: function(option, value) {
139 setOption: function(option, value) {
140 var oldVal = options[option];
126 options[option] = value;
141 options[option] = value;
127 if (option == "lineNumbers" || option == "gutter" || option == "firstLineNumber")
142 if (option == "mode" || option == "indentUnit") loadMode();
143 else if (option == "readOnly" && value) {onBlur(); input.blur();}
144 else if (option == "theme") themeChanged();
145 else if (option == "lineWrapping" && oldVal != value) operation(wrappingChanged)();
146 else if (option == "tabSize") operation(tabsChanged)();
147 if (option == "lineNumbers" || option == "gutter" || option == "firstLineNumber" || option == "theme")
128 operation(gutterChanged)();
148 operation(gutterChanged)();
129 else if (option == "mode" || option == "indentUnit") loadMode();
130 else if (option == "readOnly" && value == "nocursor") input.blur();
131 else if (option == "theme") scroller.className = scroller.className.replace(/cm-s-\w+/, "cm-s-" + value);
132 },
149 },
133 getOption: function(option) {return options[option];},
150 getOption: function(option) {return options[option];},
134 undo: operation(undo),
151 undo: operation(undo),
135 redo: operation(redo),
152 redo: operation(redo),
136 indentLine: operation(function(n, dir) {
153 indentLine: operation(function(n, dir) {
137 if (isLine(n)) indentLine(n, dir == null ? "smart" : dir ? "add" : "subtract");
154 if (isLine(n)) indentLine(n, dir == null ? "smart" : dir ? "add" : "subtract");
138 }),
155 }),
156 indentSelection: operation(indentSelected),
139 historySize: function() {return {undo: history.done.length, redo: history.undone.length};},
157 historySize: function() {return {undo: history.done.length, redo: history.undone.length};},
158 clearHistory: function() {history = new History();},
140 matchBrackets: operation(function(){matchBrackets(true);}),
159 matchBrackets: operation(function(){matchBrackets(true);}),
141 getTokenAt: function(pos) {
160 getTokenAt: operation(function(pos) {
142 pos = clipPos(pos);
161 pos = clipPos(pos);
143 return lines[pos.line].getTokenAt(mode, getStateBefore(pos.line), pos.ch);
162 return getLine(pos.line).getTokenAt(mode, getStateBefore(pos.line), pos.ch);
144 },
163 }),
145 getStateAfter: function(line) {
164 getStateAfter: function(line) {
146 line = clipLine(line == null ? lines.length - 1: line);
165 line = clipLine(line == null ? doc.size - 1: line);
147 return getStateBefore(line + 1);
166 return getStateBefore(line + 1);
148 },
167 },
149 cursorCoords: function(start){
168 cursorCoords: function(start){
150 if (start == null) start = sel.inverted;
169 if (start == null) start = sel.inverted;
151 return pageCoords(start ? sel.from : sel.to);
170 return pageCoords(start ? sel.from : sel.to);
152 },
171 },
153 charCoords: function(pos){return pageCoords(clipPos(pos));},
172 charCoords: function(pos){return pageCoords(clipPos(pos));},
154 coordsChar: function(coords) {
173 coordsChar: function(coords) {
155 var off = eltOffset(lineSpace);
174 var off = eltOffset(lineSpace);
156 var line = clipLine(Math.min(lines.length - 1, showingFrom + Math.floor((coords.y - off.top) / lineHeight())));
175 return coordsChar(coords.x - off.left, coords.y - off.top);
157 return clipPos({line: line, ch: charFromX(clipLine(line), coords.x - off.left)});
158 },
176 },
159 getSearchCursor: function(query, pos, caseFold) {return new SearchCursor(query, pos, caseFold);},
177 markText: operation(markText),
160 markText: operation(function(a, b, c){return operation(markText(a, b, c));}),
178 setBookmark: setBookmark,
161 setMarker: operation(addGutterMarker),
179 setMarker: operation(addGutterMarker),
162 clearMarker: operation(removeGutterMarker),
180 clearMarker: operation(removeGutterMarker),
163 setLineClass: operation(setLineClass),
181 setLineClass: operation(setLineClass),
182 hideLine: operation(function(h) {return setLineHidden(h, true);}),
183 showLine: operation(function(h) {return setLineHidden(h, false);}),
184 onDeleteLine: function(line, f) {
185 if (typeof line == "number") {
186 if (!isLine(line)) return null;
187 line = getLine(line);
188 }
189 (line.handlers || (line.handlers = [])).push(f);
190 return line;
191 },
164 lineInfo: lineInfo,
192 lineInfo: lineInfo,
165 addWidget: function(pos, node, scroll, vert, horiz) {
193 addWidget: function(pos, node, scroll, vert, horiz) {
166 pos = localCoords(clipPos(pos));
194 pos = localCoords(clipPos(pos));
167 var top = pos.yBot, left = pos.x;
195 var top = pos.yBot, left = pos.x;
168 node.style.position = "absolute";
196 node.style.position = "absolute";
169 code.appendChild(node);
197 code.appendChild(node);
170 if (vert == "over") top = pos.y;
198 if (vert == "over") top = pos.y;
171 else if (vert == "near") {
199 else if (vert == "near") {
172 var vspace = Math.max(scroller.offsetHeight, lines.length * lineHeight()),
200 var vspace = Math.max(scroller.offsetHeight, doc.height * textHeight()),
173 hspace = Math.max(code.clientWidth, lineSpace.clientWidth) - paddingLeft();
201 hspace = Math.max(code.clientWidth, lineSpace.clientWidth) - paddingLeft();
174 if (pos.yBot + node.offsetHeight > vspace && pos.y > node.offsetHeight)
202 if (pos.yBot + node.offsetHeight > vspace && pos.y > node.offsetHeight)
175 top = pos.y - node.offsetHeight;
203 top = pos.y - node.offsetHeight;
176 if (left + node.offsetWidth > hspace)
204 if (left + node.offsetWidth > hspace)
177 left = hspace - node.offsetWidth;
205 left = hspace - node.offsetWidth;
178 }
206 }
179 node.style.top = (top + paddingTop()) + "px";
207 node.style.top = (top + paddingTop()) + "px";
180 node.style.left = node.style.right = "";
208 node.style.left = node.style.right = "";
181 if (horiz == "right") {
209 if (horiz == "right") {
182 left = code.clientWidth - node.offsetWidth;
210 left = code.clientWidth - node.offsetWidth;
183 node.style.right = "0px";
211 node.style.right = "0px";
184 } else {
212 } else {
185 if (horiz == "left") left = 0;
213 if (horiz == "left") left = 0;
186 else if (horiz == "middle") left = (code.clientWidth - node.offsetWidth) / 2;
214 else if (horiz == "middle") left = (code.clientWidth - node.offsetWidth) / 2;
187 node.style.left = (left + paddingLeft()) + "px";
215 node.style.left = (left + paddingLeft()) + "px";
188 }
216 }
189 if (scroll)
217 if (scroll)
190 scrollIntoView(left, top, left + node.offsetWidth, top + node.offsetHeight);
218 scrollIntoView(left, top, left + node.offsetWidth, top + node.offsetHeight);
191 },
219 },
192
220
193 lineCount: function() {return lines.length;},
221 lineCount: function() {return doc.size;},
222 clipPos: clipPos,
194 getCursor: function(start) {
223 getCursor: function(start) {
195 if (start == null) start = sel.inverted;
224 if (start == null) start = sel.inverted;
196 return copyPos(start ? sel.from : sel.to);
225 return copyPos(start ? sel.from : sel.to);
197 },
226 },
198 somethingSelected: function() {return !posEq(sel.from, sel.to);},
227 somethingSelected: function() {return !posEq(sel.from, sel.to);},
199 setCursor: operation(function(line, ch) {
228 setCursor: operation(function(line, ch, user) {
200 if (ch == null && typeof line.line == "number") setCursor(line.line, line.ch);
229 if (ch == null && typeof line.line == "number") setCursor(line.line, line.ch, user);
201 else setCursor(line, ch);
230 else setCursor(line, ch, user);
231 }),
232 setSelection: operation(function(from, to, user) {
233 (user ? setSelectionUser : setSelection)(clipPos(from), clipPos(to || from));
202 }),
234 }),
203 setSelection: operation(function(from, to) {setSelection(clipPos(from), clipPos(to || from));}),
235 getLine: function(line) {if (isLine(line)) return getLine(line).text;},
204 getLine: function(line) {if (isLine(line)) return lines[line].text;},
236 getLineHandle: function(line) {if (isLine(line)) return getLine(line);},
205 setLine: operation(function(line, text) {
237 setLine: operation(function(line, text) {
206 if (isLine(line)) replaceRange(text, {line: line, ch: 0}, {line: line, ch: lines[line].text.length});
238 if (isLine(line)) replaceRange(text, {line: line, ch: 0}, {line: line, ch: getLine(line).text.length});
207 }),
239 }),
208 removeLine: operation(function(line) {
240 removeLine: operation(function(line) {
209 if (isLine(line)) replaceRange("", {line: line, ch: 0}, clipPos({line: line+1, ch: 0}));
241 if (isLine(line)) replaceRange("", {line: line, ch: 0}, clipPos({line: line+1, ch: 0}));
210 }),
242 }),
211 replaceRange: operation(replaceRange),
243 replaceRange: operation(replaceRange),
212 getRange: function(from, to) {return getRange(clipPos(from), clipPos(to));},
244 getRange: function(from, to) {return getRange(clipPos(from), clipPos(to));},
213
245
246 execCommand: function(cmd) {return commands[cmd](instance);},
247 // Stuff used by commands, probably not much use to outside code.
248 moveH: operation(moveH),
249 deleteH: operation(deleteH),
250 moveV: operation(moveV),
251 toggleOverwrite: function() {overwrite = !overwrite;},
252
253 posFromIndex: function(off) {
254 var lineNo = 0, ch;
255 doc.iter(0, doc.size, function(line) {
256 var sz = line.text.length + 1;
257 if (sz > off) { ch = off; return true; }
258 off -= sz;
259 ++lineNo;
260 });
261 return clipPos({line: lineNo, ch: ch});
262 },
263 indexFromPos: function (coords) {
264 if (coords.line < 0 || coords.ch < 0) return 0;
265 var index = coords.ch;
266 doc.iter(0, coords.line, function (line) {
267 index += line.text.length + 1;
268 });
269 return index;
270 },
271
214 operation: function(f){return operation(f)();},
272 operation: function(f){return operation(f)();},
215 refresh: function(){updateDisplay(true);},
273 refresh: function(){updateDisplay(true);},
216 getInputField: function(){return input;},
274 getInputField: function(){return input;},
217 getWrapperElement: function(){return wrapper;},
275 getWrapperElement: function(){return wrapper;},
218 getScrollerElement: function(){return scroller;},
276 getScrollerElement: function(){return scroller;},
219 getGutterElement: function(){return gutter;}
277 getGutterElement: function(){return gutter;}
220 };
278 };
221
279
280 function getLine(n) { return getLineAt(doc, n); }
281 function updateLineHeight(line, height) {
282 gutterDirty = true;
283 var diff = height - line.height;
284 for (var n = line; n; n = n.parent) n.height += diff;
285 }
286
222 function setValue(code) {
287 function setValue(code) {
223 history = null;
224 var top = {line: 0, ch: 0};
288 var top = {line: 0, ch: 0};
225 updateLines(top, {line: lines.length - 1, ch: lines[lines.length-1].text.length},
289 updateLines(top, {line: doc.size - 1, ch: getLine(doc.size-1).text.length},
226 splitLines(code), top, top);
290 splitLines(code), top, top);
227 history = new History();
228 updateInput = true;
291 updateInput = true;
229 }
292 }
230 function getValue(code) {
293 function getValue(code) {
231 var text = [];
294 var text = [];
232 for (var i = 0, l = lines.length; i < l; ++i)
295 doc.iter(0, doc.size, function(line) { text.push(line.text); });
233 text.push(lines[i].text);
234 return text.join("\n");
296 return text.join("\n");
235 }
297 }
236
298
237 function onMouseDown(e) {
299 function onMouseDown(e) {
300 setShift(e.shiftKey);
238 // Check whether this is a click in a widget
301 // Check whether this is a click in a widget
239 for (var n = e_target(e); n != wrapper; n = n.parentNode)
302 for (var n = e_target(e); n != wrapper; n = n.parentNode)
240 if (n.parentNode == code && n != mover) return;
303 if (n.parentNode == code && n != mover) return;
241
304
242 // First, see if this is a click in the gutter
305 // See if this is a click in the gutter
243 for (var n = e_target(e); n != wrapper; n = n.parentNode)
306 for (var n = e_target(e); n != wrapper; n = n.parentNode)
244 if (n.parentNode == gutterText) {
307 if (n.parentNode == gutterText) {
245 if (options.onGutterClick)
308 if (options.onGutterClick)
246 options.onGutterClick(instance, indexOf(gutterText.childNodes, n) + showingFrom, e);
309 options.onGutterClick(instance, indexOf(gutterText.childNodes, n) + showingFrom, e);
247 return e_preventDefault(e);
310 return e_preventDefault(e);
248 }
311 }
249
312
250 var start = posFromMouse(e);
313 var start = posFromMouse(e);
251
314
252 switch (e_button(e)) {
315 switch (e_button(e)) {
253 case 3:
316 case 3:
254 if (gecko && !mac) onContextMenu(e);
317 if (gecko && !mac) onContextMenu(e);
255 return;
318 return;
256 case 2:
319 case 2:
257 if (start) setCursor(start.line, start.ch, true);
320 if (start) setCursor(start.line, start.ch, true);
258 return;
321 return;
259 }
322 }
260 // For button 1, if it was clicked inside the editor
323 // For button 1, if it was clicked inside the editor
261 // (posFromMouse returning non-null), we have to adjust the
324 // (posFromMouse returning non-null), we have to adjust the
262 // selection.
325 // selection.
263 if (!start) {if (e_target(e) == scroller) e_preventDefault(e); return;}
326 if (!start) {if (e_target(e) == scroller) e_preventDefault(e); return;}
264
327
265 if (!focused) onFocus();
328 if (!focused) onFocus();
266
329
267 var now = +new Date;
330 var now = +new Date;
268 if (lastDoubleClick > now - 400) {
331 if (lastDoubleClick && lastDoubleClick.time > now - 400 && posEq(lastDoubleClick.pos, start)) {
269 e_preventDefault(e);
332 e_preventDefault(e);
333 setTimeout(focusInput, 20);
270 return selectLine(start.line);
334 return selectLine(start.line);
271 } else if (lastClick > now - 400) {
335 } else if (lastClick && lastClick.time > now - 400 && posEq(lastClick.pos, start)) {
272 lastDoubleClick = now;
336 lastDoubleClick = {time: now, pos: start};
273 e_preventDefault(e);
337 e_preventDefault(e);
274 return selectWordAt(start);
338 return selectWordAt(start);
275 } else { lastClick = now; }
339 } else { lastClick = {time: now, pos: start}; }
276
340
277 var last = start, going;
341 var last = start, going;
278 if (dragAndDrop && !posEq(sel.from, sel.to) &&
342 if (dragAndDrop && !posEq(sel.from, sel.to) &&
279 !posLess(start, sel.from) && !posLess(sel.to, start)) {
343 !posLess(start, sel.from) && !posLess(sel.to, start)) {
280 // Let the drag handler handle this.
344 // Let the drag handler handle this.
345 if (webkit) lineSpace.draggable = true;
346 var up = connect(targetDocument, "mouseup", operation(function(e2) {
347 if (webkit) lineSpace.draggable = false;
348 draggingText = false;
349 up();
350 if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) {
351 e_preventDefault(e2);
352 setCursor(start.line, start.ch, true);
353 focusInput();
354 }
355 }), true);
356 draggingText = true;
281 return;
357 return;
282 }
358 }
283 e_preventDefault(e);
359 e_preventDefault(e);
284 setCursor(start.line, start.ch, true);
360 setCursor(start.line, start.ch, true);
285
361
286 function extend(e) {
362 function extend(e) {
287 var cur = posFromMouse(e, true);
363 var cur = posFromMouse(e, true);
288 if (cur && !posEq(cur, last)) {
364 if (cur && !posEq(cur, last)) {
289 if (!focused) onFocus();
365 if (!focused) onFocus();
290 last = cur;
366 last = cur;
291 setSelectionUser(start, cur);
367 setSelectionUser(start, cur);
292 updateInput = false;
368 updateInput = false;
293 var visible = visibleLines();
369 var visible = visibleLines();
294 if (cur.line >= visible.to || cur.line < visible.from)
370 if (cur.line >= visible.to || cur.line < visible.from)
295 going = setTimeout(operation(function(){extend(e);}), 150);
371 going = setTimeout(operation(function(){extend(e);}), 150);
296 }
372 }
297 }
373 }
298
374
299 var move = connect(targetDocument, "mousemove", operation(function(e) {
375 var move = connect(targetDocument, "mousemove", operation(function(e) {
300 clearTimeout(going);
376 clearTimeout(going);
301 e_preventDefault(e);
377 e_preventDefault(e);
302 extend(e);
378 extend(e);
303 }), true);
379 }), true);
304 var up = connect(targetDocument, "mouseup", operation(function(e) {
380 var up = connect(targetDocument, "mouseup", operation(function(e) {
305 clearTimeout(going);
381 clearTimeout(going);
306 var cur = posFromMouse(e);
382 var cur = posFromMouse(e);
307 if (cur) setSelectionUser(start, cur);
383 if (cur) setSelectionUser(start, cur);
308 e_preventDefault(e);
384 e_preventDefault(e);
309 focusInput();
385 focusInput();
310 updateInput = true;
386 updateInput = true;
311 move(); up();
387 move(); up();
312 }), true);
388 }), true);
313 }
389 }
390 function onDoubleClick(e) {
391 for (var n = e_target(e); n != wrapper; n = n.parentNode)
392 if (n.parentNode == gutterText) return e_preventDefault(e);
393 var start = posFromMouse(e);
394 if (!start) return;
395 lastDoubleClick = {time: +new Date, pos: start};
396 e_preventDefault(e);
397 selectWordAt(start);
398 }
314 function onDrop(e) {
399 function onDrop(e) {
315 e.preventDefault();
400 e.preventDefault();
316 var pos = posFromMouse(e, true), files = e.dataTransfer.files;
401 var pos = posFromMouse(e, true), files = e.dataTransfer.files;
317 if (!pos || options.readOnly) return;
402 if (!pos || options.readOnly) return;
318 if (files && files.length && window.FileReader && window.File) {
403 if (files && files.length && window.FileReader && window.File) {
319 function loadFile(file, i) {
404 function loadFile(file, i) {
320 var reader = new FileReader;
405 var reader = new FileReader;
321 reader.onload = function() {
406 reader.onload = function() {
322 text[i] = reader.result;
407 text[i] = reader.result;
323 if (++read == n) replaceRange(text.join(""), clipPos(pos), clipPos(pos));
408 if (++read == n) {
409 pos = clipPos(pos);
410 operation(function() {
411 var end = replaceRange(text.join(""), pos, pos);
412 setSelectionUser(pos, end);
413 })();
414 }
324 };
415 };
325 reader.readAsText(file);
416 reader.readAsText(file);
326 }
417 }
327 var n = files.length, text = Array(n), read = 0;
418 var n = files.length, text = Array(n), read = 0;
328 for (var i = 0; i < n; ++i) loadFile(files[i], i);
419 for (var i = 0; i < n; ++i) loadFile(files[i], i);
329 }
420 }
330 else {
421 else {
331 try {
422 try {
332 var text = e.dataTransfer.getData("Text");
423 var text = e.dataTransfer.getData("Text");
333 if (text) replaceRange(text, pos, pos);
424 if (text) {
425 var end = replaceRange(text, pos, pos);
426 var curFrom = sel.from, curTo = sel.to;
427 setSelectionUser(pos, end);
428 if (draggingText) replaceRange("", curFrom, curTo);
429 focusInput();
430 }
334 }
431 }
335 catch(e){}
432 catch(e){}
336 }
433 }
337 }
434 }
338 function onDragStart(e) {
435 function onDragStart(e) {
339 var txt = getSelection();
436 var txt = getSelection();
340 // This will reset escapeElement
437 // This will reset escapeElement
341 htmlEscape(txt);
438 htmlEscape(txt);
342 e.dataTransfer.setDragImage(escapeElement, 0, 0);
439 e.dataTransfer.setDragImage(escapeElement, 0, 0);
343 e.dataTransfer.setData("Text", txt);
440 e.dataTransfer.setData("Text", txt);
344 }
441 }
442 function handleKeyBinding(e) {
443 var name = keyNames[e.keyCode], next = keyMap[options.keyMap].auto, bound, dropShift;
444 if (name == null || e.altGraphKey) {
445 if (next) options.keyMap = next;
446 return null;
447 }
448 if (e.altKey) name = "Alt-" + name;
449 if (e.ctrlKey) name = "Ctrl-" + name;
450 if (e.metaKey) name = "Cmd-" + name;
451 if (e.shiftKey && (bound = lookupKey("Shift-" + name, options.extraKeys, options.keyMap))) {
452 dropShift = true;
453 } else {
454 bound = lookupKey(name, options.extraKeys, options.keyMap);
455 }
456 if (typeof bound == "string") {
457 if (commands.propertyIsEnumerable(bound)) bound = commands[bound];
458 else bound = null;
459 }
460 if (next && (bound || !isModifierKey(e))) options.keyMap = next;
461 if (!bound) return false;
462 if (dropShift) {
463 var prevShift = shiftSelecting;
464 shiftSelecting = null;
465 bound(instance);
466 shiftSelecting = prevShift;
467 } else bound(instance);
468 e_preventDefault(e);
469 return true;
470 }
471 var lastStoppedKey = null;
345 function onKeyDown(e) {
472 function onKeyDown(e) {
346 if (!focused) onFocus();
473 if (!focused) onFocus();
347
348 var code = e.keyCode;
474 var code = e.keyCode;
349 // IE does strange things with escape.
475 // IE does strange things with escape.
350 if (ie && code == 27) { e.returnValue = false; }
476 if (ie && code == 27) { e.returnValue = false; }
351 // Tries to detect ctrl on non-mac, cmd on mac.
477 setShift(code == 16 || e.shiftKey);
352 var mod = (mac ? e.metaKey : e.ctrlKey) && !e.altKey, anyMod = e.ctrlKey || e.altKey || e.metaKey;
353 if (code == 16 || e.shiftKey) shiftSelecting = shiftSelecting || (sel.inverted ? sel.to : sel.from);
354 else shiftSelecting = null;
355 // First give onKeyEvent option a chance to handle this.
478 // First give onKeyEvent option a chance to handle this.
356 if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return;
479 if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return;
357
480 var handled = handleKeyBinding(e);
358 if (code == 33 || code == 34) {scrollPage(code == 34); return e_preventDefault(e);} // page up/down
481 if (window.opera) {
359 if (mod && ((code == 36 || code == 35) || // ctrl-home/end
482 lastStoppedKey = handled ? e.keyCode : null;
360 mac && (code == 38 || code == 40))) { // cmd-up/down
483 // Opera has no cut event... we try to at least catch the key combo
361 scrollEnd(code == 36 || code == 38); return e_preventDefault(e);
484 if (!handled && (mac ? e.metaKey : e.ctrlKey) && e.keyCode == 88)
362 }
485 replaceSelection("");
363 if (mod && code == 65) {selectAll(); return e_preventDefault(e);} // ctrl-a
364 if (!options.readOnly) {
365 if (!anyMod && code == 13) {return;} // enter
366 if (!anyMod && code == 9 && handleTab(e.shiftKey)) return e_preventDefault(e); // tab
367 if (mod && code == 90) {undo(); return e_preventDefault(e);} // ctrl-z
368 if (mod && ((e.shiftKey && code == 90) || code == 89)) {redo(); return e_preventDefault(e);} // ctrl-shift-z, ctrl-y
369 }
370 if (code == 36) { if (options.smartHome) { smartHome(); return e_preventDefault(e); } }
371
372 // Key id to use in the movementKeys map. We also pass it to
373 // fastPoll in order to 'self learn'. We need this because
374 // reducedSelection, the hack where we collapse the selection to
375 // its start when it is inverted and a movement key is pressed
376 // (and later restore it again), shouldn't be used for
377 // non-movement keys.
378 curKeyId = (mod ? "c" : "") + (e.altKey ? "a" : "") + code;
379 if (sel.inverted && movementKeys[curKeyId] === true) {
380 var range = selRange(input);
381 if (range) {
382 reducedSelection = {anchor: range.start};
383 setSelRange(input, range.start, range.start);
384 }
385 }
386 // Don't save the key as a movementkey unless it had a modifier
387 if (!mod && !e.altKey) curKeyId = null;
388 fastPoll(curKeyId);
389 }
486 }
390 function onKeyUp(e) {
391 if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return;
392 if (reducedSelection) {
393 reducedSelection = null;
394 updateInput = true;
395 }
396 if (e.keyCode == 16) shiftSelecting = null;
397 }
487 }
398 function onKeyPress(e) {
488 function onKeyPress(e) {
489 if (window.opera && e.keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;}
399 if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return;
490 if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return;
491 if (window.opera && !e.which && handleKeyBinding(e)) return;
400 if (options.electricChars && mode.electricChars) {
492 if (options.electricChars && mode.electricChars) {
401 var ch = String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode);
493 var ch = String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode);
402 if (mode.electricChars.indexOf(ch) > -1)
494 if (mode.electricChars.indexOf(ch) > -1)
403 setTimeout(operation(function() {indentLine(sel.to.line, "smart");}), 50);
495 setTimeout(operation(function() {indentLine(sel.to.line, "smart");}), 75);
404 }
496 }
405 var code = e.keyCode;
497 fastPoll();
406 // Re-stop tab and enter. Necessary on some browsers.
498 }
407 if (code == 13) {if (!options.readOnly) handleEnter(); e_preventDefault(e);}
499 function onKeyUp(e) {
408 else if (!e.ctrlKey && !e.altKey && !e.metaKey && code == 9 && options.tabMode != "default") e_preventDefault(e);
500 if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return;
409 else fastPoll(curKeyId);
501 if (e.keyCode == 16) shiftSelecting = null;
410 }
502 }
411
503
412 function onFocus() {
504 function onFocus() {
413 if (options.readOnly == "nocursor") return;
505 if (options.readOnly) return;
414 if (!focused) {
506 if (!focused) {
415 if (options.onFocus) options.onFocus(instance);
507 if (options.onFocus) options.onFocus(instance);
416 focused = true;
508 focused = true;
417 if (wrapper.className.search(/\bCodeMirror-focused\b/) == -1)
509 if (wrapper.className.search(/\bCodeMirror-focused\b/) == -1)
418 wrapper.className += " CodeMirror-focused";
510 wrapper.className += " CodeMirror-focused";
419 if (!leaveInputAlone) prepareInput();
511 if (!leaveInputAlone) resetInput(true);
420 }
512 }
421 slowPoll();
513 slowPoll();
422 restartBlink();
514 restartBlink();
423 }
515 }
424 function onBlur() {
516 function onBlur() {
425 if (focused) {
517 if (focused) {
426 if (options.onBlur) options.onBlur(instance);
518 if (options.onBlur) options.onBlur(instance);
427 focused = false;
519 focused = false;
428 wrapper.className = wrapper.className.replace(" CodeMirror-focused", "");
520 wrapper.className = wrapper.className.replace(" CodeMirror-focused", "");
429 }
521 }
430 clearInterval(blinker);
522 clearInterval(blinker);
431 setTimeout(function() {if (!focused) shiftSelecting = null;}, 150);
523 setTimeout(function() {if (!focused) shiftSelecting = null;}, 150);
432 }
524 }
433
525
434 // Replace the range from from to to by the strings in newText.
526 // Replace the range from from to to by the strings in newText.
435 // Afterwards, set the selection to selFrom, selTo.
527 // Afterwards, set the selection to selFrom, selTo.
436 function updateLines(from, to, newText, selFrom, selTo) {
528 function updateLines(from, to, newText, selFrom, selTo) {
437 if (history) {
529 if (history) {
438 var old = [];
530 var old = [];
439 for (var i = from.line, e = to.line + 1; i < e; ++i) old.push(lines[i].text);
531 doc.iter(from.line, to.line + 1, function(line) { old.push(line.text); });
440 history.addChange(from.line, newText.length, old);
532 history.addChange(from.line, newText.length, old);
441 while (history.done.length > options.undoDepth) history.done.shift();
533 while (history.done.length > options.undoDepth) history.done.shift();
442 }
534 }
443 updateLinesNoUndo(from, to, newText, selFrom, selTo);
535 updateLinesNoUndo(from, to, newText, selFrom, selTo);
444 }
536 }
445 function unredoHelper(from, to) {
537 function unredoHelper(from, to) {
446 var change = from.pop();
538 var change = from.pop();
447 if (change) {
539 if (change) {
448 var replaced = [], end = change.start + change.added;
540 var replaced = [], end = change.start + change.added;
449 for (var i = change.start; i < end; ++i) replaced.push(lines[i].text);
541 doc.iter(change.start, end, function(line) { replaced.push(line.text); });
450 to.push({start: change.start, added: change.old.length, old: replaced});
542 to.push({start: change.start, added: change.old.length, old: replaced});
451 var pos = clipPos({line: change.start + change.old.length - 1,
543 var pos = clipPos({line: change.start + change.old.length - 1,
452 ch: editEnd(replaced[replaced.length-1], change.old[change.old.length-1])});
544 ch: editEnd(replaced[replaced.length-1], change.old[change.old.length-1])});
453 updateLinesNoUndo({line: change.start, ch: 0}, {line: end - 1, ch: lines[end-1].text.length}, change.old, pos, pos);
545 updateLinesNoUndo({line: change.start, ch: 0}, {line: end - 1, ch: getLine(end-1).text.length}, change.old, pos, pos);
454 updateInput = true;
546 updateInput = true;
455 }
547 }
456 }
548 }
457 function undo() {unredoHelper(history.done, history.undone);}
549 function undo() {unredoHelper(history.done, history.undone);}
458 function redo() {unredoHelper(history.undone, history.done);}
550 function redo() {unredoHelper(history.undone, history.done);}
459
551
460 function updateLinesNoUndo(from, to, newText, selFrom, selTo) {
552 function updateLinesNoUndo(from, to, newText, selFrom, selTo) {
461 var recomputeMaxLength = false, maxLineLength = maxLine.length;
553 var recomputeMaxLength = false, maxLineLength = maxLine.length;
462 for (var i = from.line; i <= to.line; ++i) {
554 if (!options.lineWrapping)
463 if (lines[i].text.length == maxLineLength) {recomputeMaxLength = true; break;}
555 doc.iter(from.line, to.line, function(line) {
464 }
556 if (line.text.length == maxLineLength) {recomputeMaxLength = true; return true;}
557 });
558 if (from.line != to.line || newText.length > 1) gutterDirty = true;
465
559
466 var nlines = to.line - from.line, firstLine = lines[from.line], lastLine = lines[to.line];
560 var nlines = to.line - from.line, firstLine = getLine(from.line), lastLine = getLine(to.line);
467 // First adjust the line structure, taking some care to leave highlighting intact.
561 // First adjust the line structure, taking some care to leave highlighting intact.
468 if (firstLine == lastLine) {
562 if (from.ch == 0 && to.ch == 0 && newText[newText.length - 1] == "") {
563 // This is a whole-line replace. Treated specially to make
564 // sure line objects move the way they are supposed to.
565 var added = [], prevLine = null;
566 if (from.line) {
567 prevLine = getLine(from.line - 1);
568 prevLine.fixMarkEnds(lastLine);
569 } else lastLine.fixMarkStarts();
570 for (var i = 0, e = newText.length - 1; i < e; ++i)
571 added.push(Line.inheritMarks(newText[i], prevLine));
572 if (nlines) doc.remove(from.line, nlines, callbacks);
573 if (added.length) doc.insert(from.line, added);
574 } else if (firstLine == lastLine) {
469 if (newText.length == 1)
575 if (newText.length == 1)
470 firstLine.replace(from.ch, to.ch, newText[0]);
576 firstLine.replace(from.ch, to.ch, newText[0]);
471 else {
577 else {
472 lastLine = firstLine.split(to.ch, newText[newText.length-1]);
578 lastLine = firstLine.split(to.ch, newText[newText.length-1]);
473 var spliceargs = [from.line + 1, nlines];
579 firstLine.replace(from.ch, null, newText[0]);
474 firstLine.replace(from.ch, firstLine.text.length, newText[0]);
580 firstLine.fixMarkEnds(lastLine);
475 for (var i = 1, e = newText.length - 1; i < e; ++i) spliceargs.push(new Line(newText[i]));
581 var added = [];
476 spliceargs.push(lastLine);
582 for (var i = 1, e = newText.length - 1; i < e; ++i)
477 lines.splice.apply(lines, spliceargs);
583 added.push(Line.inheritMarks(newText[i], firstLine));
478 }
584 added.push(lastLine);
479 }
585 doc.insert(from.line + 1, added);
480 else if (newText.length == 1) {
586 }
481 firstLine.replace(from.ch, firstLine.text.length, newText[0] + lastLine.text.slice(to.ch));
587 } else if (newText.length == 1) {
482 lines.splice(from.line + 1, nlines);
588 firstLine.replace(from.ch, null, newText[0]);
483 }
589 lastLine.replace(null, to.ch, "");
484 else {
590 firstLine.append(lastLine);
485 var spliceargs = [from.line + 1, nlines - 1];
591 doc.remove(from.line + 1, nlines, callbacks);
486 firstLine.replace(from.ch, firstLine.text.length, newText[0]);
592 } else {
487 lastLine.replace(0, to.ch, newText[newText.length-1]);
593 var added = [];
488 for (var i = 1, e = newText.length - 1; i < e; ++i) spliceargs.push(new Line(newText[i]));
594 firstLine.replace(from.ch, null, newText[0]);
489 lines.splice.apply(lines, spliceargs);
595 lastLine.replace(null, to.ch, newText[newText.length-1]);
490 }
596 firstLine.fixMarkEnds(lastLine);
491
597 for (var i = 1, e = newText.length - 1; i < e; ++i)
492
598 added.push(Line.inheritMarks(newText[i], firstLine));
493 for (var i = from.line, e = i + newText.length; i < e; ++i) {
599 if (nlines > 1) doc.remove(from.line + 1, nlines - 1, callbacks);
494 var l = lines[i].text;
600 doc.insert(from.line + 1, added);
601 }
602 if (options.lineWrapping) {
603 var perLine = scroller.clientWidth / charWidth() - 3;
604 doc.iter(from.line, from.line + newText.length, function(line) {
605 if (line.hidden) return;
606 var guess = Math.ceil(line.text.length / perLine) || 1;
607 if (guess != line.height) updateLineHeight(line, guess);
608 });
609 } else {
610 doc.iter(from.line, i + newText.length, function(line) {
611 var l = line.text;
495 if (l.length > maxLineLength) {
612 if (l.length > maxLineLength) {
496 maxLine = l; maxLineLength = l.length; maxWidth = null;
613 maxLine = l; maxLineLength = l.length; maxWidth = null;
497 recomputeMaxLength = false;
614 recomputeMaxLength = false;
498 }
615 }
499 }
616 });
500 if (recomputeMaxLength) {
617 if (recomputeMaxLength) {
501 maxLineLength = 0; maxLine = ""; maxWidth = null;
618 maxLineLength = 0; maxLine = ""; maxWidth = null;
502 for (var i = 0, e = lines.length; i < e; ++i) {
619 doc.iter(0, doc.size, function(line) {
503 var l = lines[i].text;
620 var l = line.text;
504 if (l.length > maxLineLength) {
621 if (l.length > maxLineLength) {
505 maxLineLength = l.length; maxLine = l;
622 maxLineLength = l.length; maxLine = l;
506 }
623 }
624 });
507 }
625 }
508 }
626 }
509
627
510 // Add these lines to the work array, so that they will be
628 // Add these lines to the work array, so that they will be
511 // highlighted. Adjust work lines if lines were added/removed.
629 // highlighted. Adjust work lines if lines were added/removed.
512 var newWork = [], lendiff = newText.length - nlines - 1;
630 var newWork = [], lendiff = newText.length - nlines - 1;
513 for (var i = 0, l = work.length; i < l; ++i) {
631 for (var i = 0, l = work.length; i < l; ++i) {
514 var task = work[i];
632 var task = work[i];
515 if (task < from.line) newWork.push(task);
633 if (task < from.line) newWork.push(task);
516 else if (task > to.line) newWork.push(task + lendiff);
634 else if (task > to.line) newWork.push(task + lendiff);
517 }
635 }
518 if (newText.length < 5) {
636 var hlEnd = from.line + Math.min(newText.length, 500);
519 highlightLines(from.line, from.line + newText.length);
637 highlightLines(from.line, hlEnd);
520 newWork.push(from.line + newText.length);
638 newWork.push(hlEnd);
521 } else {
522 newWork.push(from.line);
523 }
524 work = newWork;
639 work = newWork;
525 startWorker(100);
640 startWorker(100);
526 // Remember that these lines changed, for updating the display
641 // Remember that these lines changed, for updating the display
527 changes.push({from: from.line, to: to.line + 1, diff: lendiff});
642 changes.push({from: from.line, to: to.line + 1, diff: lendiff});
528 textChanged = {from: from, to: to, text: newText};
643 var changeObj = {from: from, to: to, text: newText};
644 if (textChanged) {
645 for (var cur = textChanged; cur.next; cur = cur.next) {}
646 cur.next = changeObj;
647 } else textChanged = changeObj;
529
648
530 // Update the selection
649 // Update the selection
531 function updateLine(n) {return n <= Math.min(to.line, to.line + lendiff) ? n : n + lendiff;}
650 function updateLine(n) {return n <= Math.min(to.line, to.line + lendiff) ? n : n + lendiff;}
532 setSelection(selFrom, selTo, updateLine(sel.from.line), updateLine(sel.to.line));
651 setSelection(selFrom, selTo, updateLine(sel.from.line), updateLine(sel.to.line));
533
652
534 // Make sure the scroll-size div has the correct height.
653 // Make sure the scroll-size div has the correct height.
535 code.style.height = (lines.length * lineHeight() + 2 * paddingTop()) + "px";
654 code.style.height = (doc.height * textHeight() + 2 * paddingTop()) + "px";
536 }
655 }
537
656
538 function replaceRange(code, from, to) {
657 function replaceRange(code, from, to) {
539 from = clipPos(from);
658 from = clipPos(from);
540 if (!to) to = from; else to = clipPos(to);
659 if (!to) to = from; else to = clipPos(to);
541 code = splitLines(code);
660 code = splitLines(code);
542 function adjustPos(pos) {
661 function adjustPos(pos) {
543 if (posLess(pos, from)) return pos;
662 if (posLess(pos, from)) return pos;
544 if (!posLess(to, pos)) return end;
663 if (!posLess(to, pos)) return end;
545 var line = pos.line + code.length - (to.line - from.line) - 1;
664 var line = pos.line + code.length - (to.line - from.line) - 1;
546 var ch = pos.ch;
665 var ch = pos.ch;
547 if (pos.line == to.line)
666 if (pos.line == to.line)
548 ch += code[code.length-1].length - (to.ch - (to.line == from.line ? from.ch : 0));
667 ch += code[code.length-1].length - (to.ch - (to.line == from.line ? from.ch : 0));
549 return {line: line, ch: ch};
668 return {line: line, ch: ch};
550 }
669 }
551 var end;
670 var end;
552 replaceRange1(code, from, to, function(end1) {
671 replaceRange1(code, from, to, function(end1) {
553 end = end1;
672 end = end1;
554 return {from: adjustPos(sel.from), to: adjustPos(sel.to)};
673 return {from: adjustPos(sel.from), to: adjustPos(sel.to)};
555 });
674 });
556 return end;
675 return end;
557 }
676 }
558 function replaceSelection(code, collapse) {
677 function replaceSelection(code, collapse) {
559 replaceRange1(splitLines(code), sel.from, sel.to, function(end) {
678 replaceRange1(splitLines(code), sel.from, sel.to, function(end) {
560 if (collapse == "end") return {from: end, to: end};
679 if (collapse == "end") return {from: end, to: end};
561 else if (collapse == "start") return {from: sel.from, to: sel.from};
680 else if (collapse == "start") return {from: sel.from, to: sel.from};
562 else return {from: sel.from, to: end};
681 else return {from: sel.from, to: end};
563 });
682 });
564 }
683 }
565 function replaceRange1(code, from, to, computeSel) {
684 function replaceRange1(code, from, to, computeSel) {
566 var endch = code.length == 1 ? code[0].length + from.ch : code[code.length-1].length;
685 var endch = code.length == 1 ? code[0].length + from.ch : code[code.length-1].length;
567 var newSel = computeSel({line: from.line + code.length - 1, ch: endch});
686 var newSel = computeSel({line: from.line + code.length - 1, ch: endch});
568 updateLines(from, to, code, newSel.from, newSel.to);
687 updateLines(from, to, code, newSel.from, newSel.to);
569 }
688 }
570
689
571 function getRange(from, to) {
690 function getRange(from, to) {
572 var l1 = from.line, l2 = to.line;
691 var l1 = from.line, l2 = to.line;
573 if (l1 == l2) return lines[l1].text.slice(from.ch, to.ch);
692 if (l1 == l2) return getLine(l1).text.slice(from.ch, to.ch);
574 var code = [lines[l1].text.slice(from.ch)];
693 var code = [getLine(l1).text.slice(from.ch)];
575 for (var i = l1 + 1; i < l2; ++i) code.push(lines[i].text);
694 doc.iter(l1 + 1, l2, function(line) { code.push(line.text); });
576 code.push(lines[l2].text.slice(0, to.ch));
695 code.push(getLine(l2).text.slice(0, to.ch));
577 return code.join("\n");
696 return code.join("\n");
578 }
697 }
579 function getSelection() {
698 function getSelection() {
580 return getRange(sel.from, sel.to);
699 return getRange(sel.from, sel.to);
581 }
700 }
582
701
583 var pollingFast = false; // Ensures slowPoll doesn't cancel fastPoll
702 var pollingFast = false; // Ensures slowPoll doesn't cancel fastPoll
584 function slowPoll() {
703 function slowPoll() {
585 if (pollingFast) return;
704 if (pollingFast) return;
586 poll.set(2000, function() {
705 poll.set(options.pollInterval, function() {
587 startOperation();
706 startOperation();
588 readInput();
707 readInput();
589 if (focused) slowPoll();
708 if (focused) slowPoll();
590 endOperation();
709 endOperation();
591 });
710 });
592 }
711 }
593 function fastPoll(keyId) {
712 function fastPoll() {
594 var missed = false;
713 var missed = false;
595 pollingFast = true;
714 pollingFast = true;
596 function p() {
715 function p() {
597 startOperation();
716 startOperation();
598 var changed = readInput();
717 var changed = readInput();
599 if (changed && keyId) {
718 if (!changed && !missed) {missed = true; poll.set(60, p);}
600 if (changed == "moved" && movementKeys[keyId] == null) movementKeys[keyId] = true;
601 if (changed == "changed") movementKeys[keyId] = false;
602 }
603 if (!changed && !missed) {missed = true; poll.set(80, p);}
604 else {pollingFast = false; slowPoll();}
719 else {pollingFast = false; slowPoll();}
605 endOperation();
720 endOperation();
606 }
721 }
607 poll.set(20, p);
722 poll.set(20, p);
608 }
723 }
609
724
610 // Inspects the textarea, compares its state (content, selection)
725 // Previnput is a hack to work with IME. If we reset the textarea
611 // to the data in the editing variable, and updates the editor
726 // on every change, that breaks IME. So we look for changes
612 // content or cursor if something changed.
727 // compared to the previous content instead. (Modern browsers have
728 // events that indicate IME taking place, but these are not widely
729 // supported or compatible enough yet to rely on.)
730 var prevInput = "";
613 function readInput() {
731 function readInput() {
614 if (leaveInputAlone || !focused) return;
732 if (leaveInputAlone || !focused || hasSelection(input)) return false;
615 var changed = false, text = input.value, sr = selRange(input);
733 var text = input.value;
616 if (!sr) return false;
734 if (text == prevInput) return false;
617 var changed = editing.text != text, rs = reducedSelection;
735 shiftSelecting = null;
618 var moved = changed || sr.start != editing.start || sr.end != (rs ? editing.start : editing.end);
736 var same = 0, l = Math.min(prevInput.length, text.length);
619 if (!moved && !rs) return false;
737 while (same < l && prevInput[same] == text[same]) ++same;
620 if (changed) {
738 if (same < prevInput.length)
621 shiftSelecting = reducedSelection = null;
739 sel.from = {line: sel.from.line, ch: sel.from.ch - (prevInput.length - same)};
622 if (options.readOnly) {updateInput = true; return "changed";}
740 else if (overwrite && posEq(sel.from, sel.to))
623 }
741 sel.to = {line: sel.to.line, ch: Math.min(getLine(sel.to.line).text.length, sel.to.ch + (text.length - same))};
624
742 replaceSelection(text.slice(same), "end");
625 // Compute selection start and end based on start/end offsets in textarea
743 prevInput = text;
626 function computeOffset(n, startLine) {
744 return true;
627 var pos = 0;
628 for (;;) {
629 var found = text.indexOf("\n", pos);
630 if (found == -1 || (text.charAt(found-1) == "\r" ? found - 1 : found) >= n)
631 return {line: startLine, ch: n - pos};
632 ++startLine;
633 pos = found + 1;
634 }
635 }
636 var from = computeOffset(sr.start, editing.from),
637 to = computeOffset(sr.end, editing.from);
638 // Here we have to take the reducedSelection hack into account,
639 // so that you can, for example, press shift-up at the start of
640 // your selection and have the right thing happen.
641 if (rs) {
642 var head = sr.start == rs.anchor ? to : from;
643 var tail = shiftSelecting ? sel.to : sr.start == rs.anchor ? from : to;
644 if (sel.inverted = posLess(head, tail)) { from = head; to = tail; }
645 else { reducedSelection = null; from = tail; to = head; }
646 }
647
648 // In some cases (cursor on same line as before), we don't have
649 // to update the textarea content at all.
650 if (from.line == to.line && from.line == sel.from.line && from.line == sel.to.line && !shiftSelecting)
651 updateInput = false;
652
653 // Magic mess to extract precise edited range from the changed
654 // string.
655 if (changed) {
656 var start = 0, end = text.length, len = Math.min(end, editing.text.length);
657 var c, line = editing.from, nl = -1;
658 while (start < len && (c = text.charAt(start)) == editing.text.charAt(start)) {
659 ++start;
660 if (c == "\n") {line++; nl = start;}
661 }
662 var ch = nl > -1 ? start - nl : start, endline = editing.to - 1, edend = editing.text.length;
663 for (;;) {
664 c = editing.text.charAt(edend);
665 if (text.charAt(end) != c) {++end; ++edend; break;}
666 if (c == "\n") endline--;
667 if (edend <= start || end <= start) break;
668 --end; --edend;
669 }
670 var nl = editing.text.lastIndexOf("\n", edend - 1), endch = nl == -1 ? edend : edend - nl - 1;
671 updateLines({line: line, ch: ch}, {line: endline, ch: endch}, splitLines(text.slice(start, end)), from, to);
672 if (line != endline || from.line != line) updateInput = true;
673 }
745 }
674 else setSelection(from, to);
746 function resetInput(user) {
675
747 if (!posEq(sel.from, sel.to)) {
676 editing.text = text; editing.start = sr.start; editing.end = sr.end;
748 prevInput = "";
677 return changed ? "changed" : moved ? "moved" : false;
749 input.value = getSelection();
750 input.select();
751 } else if (user) prevInput = input.value = "";
678 }
752 }
679
753
680 // Set the textarea content and selection range to match the
681 // editor state.
682 function prepareInput() {
683 var text = [];
684 var from = Math.max(0, sel.from.line - 1), to = Math.min(lines.length, sel.to.line + 2);
685 for (var i = from; i < to; ++i) text.push(lines[i].text);
686 text = input.value = text.join(lineSep);
687 var startch = sel.from.ch, endch = sel.to.ch;
688 for (var i = from; i < sel.from.line; ++i)
689 startch += lineSep.length + lines[i].text.length;
690 for (var i = from; i < sel.to.line; ++i)
691 endch += lineSep.length + lines[i].text.length;
692 editing = {text: text, from: from, to: to, start: startch, end: endch};
693 setSelRange(input, startch, reducedSelection ? startch : endch);
694 }
695 function focusInput() {
754 function focusInput() {
696 if (options.readOnly != "nocursor") input.focus();
755 if (!options.readOnly) input.focus();
697 }
756 }
698
757
699 function scrollEditorIntoView() {
758 function scrollEditorIntoView() {
700 if (!cursor.getBoundingClientRect) return;
759 if (!cursor.getBoundingClientRect) return;
701 var rect = cursor.getBoundingClientRect();
760 var rect = cursor.getBoundingClientRect();
702 var winH = window.innerHeight || document.body.offsetHeight || document.documentElement.offsetHeight;
761 // IE returns bogus coordinates when the instance sits inside of an iframe and the cursor is hidden
762 if (ie && rect.top == rect.bottom) return;
763 var winH = window.innerHeight || Math.max(document.body.offsetHeight, document.documentElement.offsetHeight);
703 if (rect.top < 0 || rect.bottom > winH) cursor.scrollIntoView();
764 if (rect.top < 0 || rect.bottom > winH) cursor.scrollIntoView();
704 }
765 }
705 function scrollCursorIntoView() {
766 function scrollCursorIntoView() {
706 var cursor = localCoords(sel.inverted ? sel.from : sel.to);
767 var cursor = localCoords(sel.inverted ? sel.from : sel.to);
707 return scrollIntoView(cursor.x, cursor.y, cursor.x, cursor.yBot);
768 var x = options.lineWrapping ? Math.min(cursor.x, lineSpace.offsetWidth) : cursor.x;
769 return scrollIntoView(x, cursor.y, x, cursor.yBot);
708 }
770 }
709 function scrollIntoView(x1, y1, x2, y2) {
771 function scrollIntoView(x1, y1, x2, y2) {
710 var pl = paddingLeft(), pt = paddingTop(), lh = lineHeight();
772 var pl = paddingLeft(), pt = paddingTop(), lh = textHeight();
711 y1 += pt; y2 += pt; x1 += pl; x2 += pl;
773 y1 += pt; y2 += pt; x1 += pl; x2 += pl;
712 var screen = scroller.clientHeight, screentop = scroller.scrollTop, scrolled = false, result = true;
774 var screen = scroller.clientHeight, screentop = scroller.scrollTop, scrolled = false, result = true;
713 if (y1 < screentop) {scroller.scrollTop = Math.max(0, y1 - 2*lh); scrolled = true;}
775 if (y1 < screentop) {scroller.scrollTop = Math.max(0, y1 - 2*lh); scrolled = true;}
714 else if (y2 > screentop + screen) {scroller.scrollTop = y2 + lh - screen; scrolled = true;}
776 else if (y2 > screentop + screen) {scroller.scrollTop = y2 + lh - screen; scrolled = true;}
715
777
716 var screenw = scroller.clientWidth, screenleft = scroller.scrollLeft;
778 var screenw = scroller.clientWidth, screenleft = scroller.scrollLeft;
717 var gutterw = options.fixedGutter ? gutter.clientWidth : 0;
779 var gutterw = options.fixedGutter ? gutter.clientWidth : 0;
718 if (x1 < screenleft + gutterw) {
780 if (x1 < screenleft + gutterw) {
719 if (x1 < 50) x1 = 0;
781 if (x1 < 50) x1 = 0;
720 scroller.scrollLeft = Math.max(0, x1 - 10 - gutterw);
782 scroller.scrollLeft = Math.max(0, x1 - 10 - gutterw);
721 scrolled = true;
783 scrolled = true;
722 }
784 }
723 else if (x2 > screenw + screenleft) {
785 else if (x2 > screenw + screenleft - 3) {
724 scroller.scrollLeft = x2 + 10 - screenw;
786 scroller.scrollLeft = x2 + 10 - screenw;
725 scrolled = true;
787 scrolled = true;
726 if (x2 > code.clientWidth) result = false;
788 if (x2 > code.clientWidth) result = false;
727 }
789 }
728 if (scrolled && options.onScroll) options.onScroll(instance);
790 if (scrolled && options.onScroll) options.onScroll(instance);
729 return result;
791 return result;
730 }
792 }
731
793
732 function visibleLines() {
794 function visibleLines() {
733 var lh = lineHeight(), top = scroller.scrollTop - paddingTop();
795 var lh = textHeight(), top = scroller.scrollTop - paddingTop();
734 return {from: Math.min(lines.length, Math.max(0, Math.floor(top / lh))),
796 var from_height = Math.max(0, Math.floor(top / lh));
735 to: Math.min(lines.length, Math.ceil((top + scroller.clientHeight) / lh))};
797 var to_height = Math.ceil((top + scroller.clientHeight) / lh);
798 return {from: lineAtHeight(doc, from_height),
799 to: lineAtHeight(doc, to_height)};
736 }
800 }
737 // Uses a set of changes plus the current scroll position to
801 // Uses a set of changes plus the current scroll position to
738 // determine which DOM updates have to be made, and makes the
802 // determine which DOM updates have to be made, and makes the
739 // updates.
803 // updates.
740 function updateDisplay(changes) {
804 function updateDisplay(changes, suppressCallback) {
741 if (!scroller.clientWidth) {
805 if (!scroller.clientWidth) {
742 showingFrom = showingTo = 0;
806 showingFrom = showingTo = displayOffset = 0;
743 return;
807 return;
744 }
808 }
745 // First create a range of theoretically intact lines, and punch
809 // Compute the new visible window
746 // holes in that using the change info.
747 var intact = changes === true ? [] : [{from: showingFrom, to: showingTo, domStart: 0}];
748 for (var i = 0, l = changes.length || 0; i < l; ++i) {
749 var change = changes[i], intact2 = [], diff = change.diff || 0;
750 for (var j = 0, l2 = intact.length; j < l2; ++j) {
751 var range = intact[j];
752 if (change.to <= range.from)
753 intact2.push({from: range.from + diff, to: range.to + diff, domStart: range.domStart});
754 else if (range.to <= change.from)
755 intact2.push(range);
756 else {
757 if (change.from > range.from)
758 intact2.push({from: range.from, to: change.from, domStart: range.domStart})
759 if (change.to < range.to)
760 intact2.push({from: change.to + diff, to: range.to + diff,
761 domStart: range.domStart + (change.to - range.from)});
762 }
763 }
764 intact = intact2;
765 }
766
767 // Then, determine which lines we'd want to see, and which
768 // updates have to be made to get there.
769 var visible = visibleLines();
810 var visible = visibleLines();
770 var from = Math.min(showingFrom, Math.max(visible.from - 3, 0)),
811 // Bail out if the visible area is already rendered and nothing changed.
771 to = Math.min(lines.length, Math.max(showingTo, visible.to + 3)),
812 if (changes !== true && changes.length == 0 && visible.from >= showingFrom && visible.to <= showingTo) return;
772 updates = [], domPos = 0, domEnd = showingTo - showingFrom, pos = from, changedLines = 0;
813 var from = Math.max(visible.from - 100, 0), to = Math.min(doc.size, visible.to + 100);
773
814 if (showingFrom < from && from - showingFrom < 20) from = showingFrom;
774 for (var i = 0, l = intact.length; i < l; ++i) {
815 if (showingTo > to && showingTo - to < 20) to = Math.min(doc.size, showingTo);
816
817 // Create a range of theoretically intact lines, and punch holes
818 // in that using the change info.
819 var intact = changes === true ? [] :
820 computeIntact([{from: showingFrom, to: showingTo, domStart: 0}], changes);
821 // Clip off the parts that won't be visible
822 var intactLines = 0;
823 for (var i = 0; i < intact.length; ++i) {
775 var range = intact[i];
824 var range = intact[i];
776 if (range.to <= from) continue;
825 if (range.from < from) {range.domStart += (from - range.from); range.from = from;}
777 if (range.from >= to) break;
826 if (range.to > to) range.to = to;
778 if (range.domStart > domPos || range.from > pos) {
827 if (range.from >= range.to) intact.splice(i--, 1);
779 updates.push({from: pos, to: range.from, domSize: range.domStart - domPos, domStart: domPos});
828 else intactLines += range.to - range.from;
780 changedLines += range.from - pos;
829 }
781 }
830 if (intactLines == to - from) return;
782 pos = range.to;
831 intact.sort(function(a, b) {return a.domStart - b.domStart;});
783 domPos = range.domStart + (range.to - range.from);
832
784 }
833 var th = textHeight(), gutterDisplay = gutter.style.display;
785 if (domPos != domEnd || pos != to) {
834 lineDiv.style.display = gutter.style.display = "none";
786 changedLines += Math.abs(to - pos);
835 patchDisplay(from, to, intact);
787 updates.push({from: pos, to: to, domSize: domEnd - domPos, domStart: domPos});
788 if (to - pos != domEnd - domPos) gutterDirty = true;
789 }
790
791 if (!updates.length) return;
792 lineDiv.style.display = "none";
793 // If more than 30% of the screen needs update, just do a full
794 // redraw (which is quicker than patching)
795 if (changedLines > (visible.to - visible.from) * .3)
796 refreshDisplay(from = Math.max(visible.from - 10, 0), to = Math.min(visible.to + 7, lines.length));
797 // Otherwise, only update the stuff that needs updating.
798 else
799 patchDisplay(updates);
800 lineDiv.style.display = "";
836 lineDiv.style.display = "";
801
837
802 // Position the mover div to align with the lines it's supposed
838 // Position the mover div to align with the lines it's supposed
803 // to be showing (which will cover the visible display)
839 // to be showing (which will cover the visible display)
804 var different = from != showingFrom || to != showingTo || lastHeight != scroller.clientHeight;
840 var different = from != showingFrom || to != showingTo || lastSizeC != scroller.clientHeight + th;
841 // This is just a bogus formula that detects when the editor is
842 // resized or the font size changes.
843 if (different) lastSizeC = scroller.clientHeight + th;
805 showingFrom = from; showingTo = to;
844 showingFrom = from; showingTo = to;
806 mover.style.top = (from * lineHeight()) + "px";
845 displayOffset = heightAtLine(doc, from);
807 if (different) {
846 mover.style.top = (displayOffset * th) + "px";
808 lastHeight = scroller.clientHeight;
847 code.style.height = (doc.height * th + 2 * paddingTop()) + "px";
809 code.style.height = (lines.length * lineHeight() + 2 * paddingTop()) + "px";
810 }
811 if (different || gutterDirty) updateGutter();
812
848
849 // Since this is all rather error prone, it is honoured with the
850 // only assertion in the whole file.
851 if (lineDiv.childNodes.length != showingTo - showingFrom)
852 throw new Error("BAD PATCH! " + JSON.stringify(intact) + " size=" + (showingTo - showingFrom) +
853 " nodes=" + lineDiv.childNodes.length);
854
855 if (options.lineWrapping) {
856 maxWidth = scroller.clientWidth;
857 var curNode = lineDiv.firstChild;
858 doc.iter(showingFrom, showingTo, function(line) {
859 if (!line.hidden) {
860 var height = Math.round(curNode.offsetHeight / th) || 1;
861 if (line.height != height) {updateLineHeight(line, height); gutterDirty = true;}
862 }
863 curNode = curNode.nextSibling;
864 });
865 } else {
813 if (maxWidth == null) maxWidth = stringWidth(maxLine);
866 if (maxWidth == null) maxWidth = stringWidth(maxLine);
814 if (maxWidth > scroller.clientWidth) {
867 if (maxWidth > scroller.clientWidth) {
815 lineSpace.style.width = maxWidth + "px";
868 lineSpace.style.width = maxWidth + "px";
816 // Needed to prevent odd wrapping/hiding of widgets placed in here.
869 // Needed to prevent odd wrapping/hiding of widgets placed in here.
817 code.style.width = "";
870 code.style.width = "";
818 code.style.width = scroller.scrollWidth + "px";
871 code.style.width = scroller.scrollWidth + "px";
819 } else {
872 } else {
820 lineSpace.style.width = code.style.width = "";
873 lineSpace.style.width = code.style.width = "";
821 }
874 }
822
875 }
823 // Since this is all rather error prone, it is honoured with the
876 gutter.style.display = gutterDisplay;
824 // only assertion in the whole file.
877 if (different || gutterDirty) updateGutter();
825 if (lineDiv.childNodes.length != showingTo - showingFrom)
826 throw new Error("BAD PATCH! " + JSON.stringify(updates) + " size=" + (showingTo - showingFrom) +
827 " nodes=" + lineDiv.childNodes.length);
828 updateCursor();
878 updateCursor();
879 if (!suppressCallback && options.onUpdate) options.onUpdate(instance);
880 return true;
829 }
881 }
830
882
831 function refreshDisplay(from, to) {
883 function computeIntact(intact, changes) {
832 var html = [], start = {line: from, ch: 0}, inSel = posLess(sel.from, start) && !posLess(sel.to, start);
884 for (var i = 0, l = changes.length || 0; i < l; ++i) {
833 for (var i = from; i < to; ++i) {
885 var change = changes[i], intact2 = [], diff = change.diff || 0;
834 var ch1 = null, ch2 = null;
886 for (var j = 0, l2 = intact.length; j < l2; ++j) {
835 if (inSel) {
887 var range = intact[j];
836 ch1 = 0;
888 if (change.to <= range.from && change.diff)
837 if (sel.to.line == i) {inSel = false; ch2 = sel.to.ch;}
889 intact2.push({from: range.from + diff, to: range.to + diff,
890 domStart: range.domStart});
891 else if (change.to <= range.from || change.from >= range.to)
892 intact2.push(range);
893 else {
894 if (change.from > range.from)
895 intact2.push({from: range.from, to: change.from, domStart: range.domStart});
896 if (change.to < range.to)
897 intact2.push({from: change.to + diff, to: range.to + diff,
898 domStart: range.domStart + (change.to - range.from)});
838 }
899 }
839 else if (sel.from.line == i) {
840 if (sel.to.line == i) {ch1 = sel.from.ch; ch2 = sel.to.ch;}
841 else {inSel = true; ch1 = sel.from.ch;}
842 }
900 }
843 html.push(lines[i].getHTML(ch1, ch2, true));
901 intact = intact2;
844 }
902 }
845 lineDiv.innerHTML = html.join("");
903 return intact;
846 }
904 }
847 function patchDisplay(updates) {
905
848 // Slightly different algorithm for IE (badInnerHTML), since
906 function patchDisplay(from, to, intact) {
849 // there .innerHTML on PRE nodes is dumb, and discards
907 // The first pass removes the DOM nodes that aren't intact.
850 // whitespace.
908 if (!intact.length) lineDiv.innerHTML = "";
851 var sfrom = sel.from.line, sto = sel.to.line, off = 0,
909 else {
852 scratch = badInnerHTML && targetDocument.createElement("div");
910 function killNode(node) {
853 for (var i = 0, e = updates.length; i < e; ++i) {
911 var tmp = node.nextSibling;
854 var rec = updates[i];
912 node.parentNode.removeChild(node);
855 var extra = (rec.to - rec.from) - rec.domSize;
913 return tmp;
856 var nodeAfter = lineDiv.childNodes[rec.domStart + rec.domSize + off] || null;
914 }
857 if (badInnerHTML)
915 var domPos = 0, curNode = lineDiv.firstChild, n;
858 for (var j = Math.max(-extra, rec.domSize); j > 0; --j)
916 for (var i = 0; i < intact.length; ++i) {
859 lineDiv.removeChild(nodeAfter ? nodeAfter.previousSibling : lineDiv.lastChild);
917 var cur = intact[i];
860 else if (extra) {
918 while (cur.domStart > domPos) {curNode = killNode(curNode); domPos++;}
861 for (var j = Math.max(0, extra); j > 0; --j)
919 for (var j = 0, e = cur.to - cur.from; j < e; ++j) {curNode = curNode.nextSibling; domPos++;}
862 lineDiv.insertBefore(targetDocument.createElement("pre"), nodeAfter);
920 }
863 for (var j = Math.max(0, -extra); j > 0; --j)
921 while (curNode) curNode = killNode(curNode);
864 lineDiv.removeChild(nodeAfter ? nodeAfter.previousSibling : lineDiv.lastChild);
922 }
865 }
923 // This pass fills in the lines that actually changed.
866 var node = lineDiv.childNodes[rec.domStart + off], inSel = sfrom < rec.from && sto >= rec.from;
924 var nextIntact = intact.shift(), curNode = lineDiv.firstChild, j = from;
867 for (var j = rec.from; j < rec.to; ++j) {
925 var sfrom = sel.from.line, sto = sel.to.line, inSel = sfrom < from && sto >= from;
926 var scratch = targetDocument.createElement("div"), newElt;
927 doc.iter(from, to, function(line) {
868 var ch1 = null, ch2 = null;
928 var ch1 = null, ch2 = null;
869 if (inSel) {
929 if (inSel) {
870 ch1 = 0;
930 ch1 = 0;
871 if (sto == j) {inSel = false; ch2 = sel.to.ch;}
931 if (sto == j) {inSel = false; ch2 = sel.to.ch;}
872 }
932 } else if (sfrom == j) {
873 else if (sfrom == j) {
874 if (sto == j) {ch1 = sel.from.ch; ch2 = sel.to.ch;}
933 if (sto == j) {ch1 = sel.from.ch; ch2 = sel.to.ch;}
875 else {inSel = true; ch1 = sel.from.ch;}
934 else {inSel = true; ch1 = sel.from.ch;}
876 }
935 }
877 if (badInnerHTML) {
936 if (nextIntact && nextIntact.to == j) nextIntact = intact.shift();
878 scratch.innerHTML = lines[j].getHTML(ch1, ch2, true);
937 if (!nextIntact || nextIntact.from > j) {
879 lineDiv.insertBefore(scratch.firstChild, nodeAfter);
938 if (line.hidden) scratch.innerHTML = "<pre></pre>";
880 }
939 else scratch.innerHTML = line.getHTML(ch1, ch2, true, tabText);
881 else {
940 lineDiv.insertBefore(scratch.firstChild, curNode);
882 node.innerHTML = lines[j].getHTML(ch1, ch2, false);
941 } else {
883 node.className = lines[j].className || "";
942 curNode = curNode.nextSibling;
884 node = node.nextSibling;
885 }
886 }
887 off += extra;
888 }
943 }
944 ++j;
945 });
889 }
946 }
890
947
891 function updateGutter() {
948 function updateGutter() {
892 if (!options.gutter && !options.lineNumbers) return;
949 if (!options.gutter && !options.lineNumbers) return;
893 var hText = mover.offsetHeight, hEditor = scroller.clientHeight;
950 var hText = mover.offsetHeight, hEditor = scroller.clientHeight;
894 gutter.style.height = (hText - hEditor < 2 ? hEditor : hText) + "px";
951 gutter.style.height = (hText - hEditor < 2 ? hEditor : hText) + "px";
895 var html = [];
952 var html = [], i = showingFrom;
896 for (var i = showingFrom; i < Math.max(showingTo, showingFrom + 1); ++i) {
953 doc.iter(showingFrom, Math.max(showingTo, showingFrom + 1), function(line) {
897 var marker = lines[i].gutterMarker;
954 if (line.hidden) {
955 html.push("<pre></pre>");
956 } else {
957 var marker = line.gutterMarker;
898 var text = options.lineNumbers ? i + options.firstLineNumber : null;
958 var text = options.lineNumbers ? i + options.firstLineNumber : null;
899 if (marker && marker.text)
959 if (marker && marker.text)
900 text = marker.text.replace("%N%", text != null ? text : "");
960 text = marker.text.replace("%N%", text != null ? text : "");
901 else if (text == null)
961 else if (text == null)
902 text = "\u00a0";
962 text = "\u00a0";
903 html.push((marker && marker.style ? '<pre class="' + marker.style + '">' : "<pre>"), text, "</pre>");
963 html.push((marker && marker.style ? '<pre class="' + marker.style + '">' : "<pre>"), text);
964 for (var j = 1; j < line.height; ++j) html.push("<br/>&#160;");
965 html.push("</pre>");
904 }
966 }
967 ++i;
968 });
905 gutter.style.display = "none";
969 gutter.style.display = "none";
906 gutterText.innerHTML = html.join("");
970 gutterText.innerHTML = html.join("");
907 var minwidth = String(lines.length).length, firstNode = gutterText.firstChild, val = eltText(firstNode), pad = "";
971 var minwidth = String(doc.size).length, firstNode = gutterText.firstChild, val = eltText(firstNode), pad = "";
908 while (val.length + pad.length < minwidth) pad += "\u00a0";
972 while (val.length + pad.length < minwidth) pad += "\u00a0";
909 if (pad) firstNode.insertBefore(targetDocument.createTextNode(pad), firstNode.firstChild);
973 if (pad) firstNode.insertBefore(targetDocument.createTextNode(pad), firstNode.firstChild);
910 gutter.style.display = "";
974 gutter.style.display = "";
911 lineSpace.style.marginLeft = gutter.offsetWidth + "px";
975 lineSpace.style.marginLeft = gutter.offsetWidth + "px";
912 gutterDirty = false;
976 gutterDirty = false;
913 }
977 }
914 function updateCursor() {
978 function updateCursor() {
915 var head = sel.inverted ? sel.from : sel.to, lh = lineHeight();
979 var head = sel.inverted ? sel.from : sel.to, lh = textHeight();
916 var x = charX(head.line, head.ch);
980 var pos = localCoords(head, true);
917 var top = head.line * lh - scroller.scrollTop;
981 var wrapOff = eltOffset(wrapper), lineOff = eltOffset(lineDiv);
918 inputDiv.style.top = Math.max(Math.min(top, scroller.offsetHeight), 0) + "px";
982 inputDiv.style.top = (pos.y + lineOff.top - wrapOff.top) + "px";
919 inputDiv.style.left = (x - scroller.scrollLeft) + "px";
983 inputDiv.style.left = (pos.x + lineOff.left - wrapOff.left) + "px";
920 if (posEq(sel.from, sel.to)) {
984 if (posEq(sel.from, sel.to)) {
921 cursor.style.top = (head.line - showingFrom) * lh + "px";
985 cursor.style.top = pos.y + "px";
922 cursor.style.left = x + "px";
986 cursor.style.left = (options.lineWrapping ? Math.min(pos.x, lineSpace.offsetWidth) : pos.x) + "px";
923 cursor.style.display = "";
987 cursor.style.display = "";
924 }
988 }
925 else cursor.style.display = "none";
989 else cursor.style.display = "none";
926 }
990 }
927
991
992 function setShift(val) {
993 if (val) shiftSelecting = shiftSelecting || (sel.inverted ? sel.to : sel.from);
994 else shiftSelecting = null;
995 }
928 function setSelectionUser(from, to) {
996 function setSelectionUser(from, to) {
929 var sh = shiftSelecting && clipPos(shiftSelecting);
997 var sh = shiftSelecting && clipPos(shiftSelecting);
930 if (sh) {
998 if (sh) {
931 if (posLess(sh, from)) from = sh;
999 if (posLess(sh, from)) from = sh;
932 else if (posLess(to, sh)) to = sh;
1000 else if (posLess(to, sh)) to = sh;
933 }
1001 }
934 setSelection(from, to);
1002 setSelection(from, to);
1003 userSelChange = true;
935 }
1004 }
936 // Update the selection. Last two args are only used by
1005 // Update the selection. Last two args are only used by
937 // updateLines, since they have to be expressed in the line
1006 // updateLines, since they have to be expressed in the line
938 // numbers before the update.
1007 // numbers before the update.
939 function setSelection(from, to, oldFrom, oldTo) {
1008 function setSelection(from, to, oldFrom, oldTo) {
1009 goalColumn = null;
1010 if (oldFrom == null) {oldFrom = sel.from.line; oldTo = sel.to.line;}
940 if (posEq(sel.from, from) && posEq(sel.to, to)) return;
1011 if (posEq(sel.from, from) && posEq(sel.to, to)) return;
941 if (posLess(to, from)) {var tmp = to; to = from; from = tmp;}
1012 if (posLess(to, from)) {var tmp = to; to = from; from = tmp;}
942
1013
1014 // Skip over hidden lines.
1015 if (from.line != oldFrom) from = skipHidden(from, oldFrom, sel.from.ch);
1016 if (to.line != oldTo) to = skipHidden(to, oldTo, sel.to.ch);
1017
943 if (posEq(from, to)) sel.inverted = false;
1018 if (posEq(from, to)) sel.inverted = false;
944 else if (posEq(from, sel.to)) sel.inverted = false;
1019 else if (posEq(from, sel.to)) sel.inverted = false;
945 else if (posEq(to, sel.from)) sel.inverted = true;
1020 else if (posEq(to, sel.from)) sel.inverted = true;
946
1021
947 // Some ugly logic used to only mark the lines that actually did
1022 // Some ugly logic used to only mark the lines that actually did
948 // see a change in selection as changed, rather than the whole
1023 // see a change in selection as changed, rather than the whole
949 // selected range.
1024 // selected range.
950 if (oldFrom == null) {oldFrom = sel.from.line; oldTo = sel.to.line;}
951 if (posEq(from, to)) {
1025 if (posEq(from, to)) {
952 if (!posEq(sel.from, sel.to))
1026 if (!posEq(sel.from, sel.to))
953 changes.push({from: oldFrom, to: oldTo + 1});
1027 changes.push({from: oldFrom, to: oldTo + 1});
954 }
1028 }
955 else if (posEq(sel.from, sel.to)) {
1029 else if (posEq(sel.from, sel.to)) {
956 changes.push({from: from.line, to: to.line + 1});
1030 changes.push({from: from.line, to: to.line + 1});
957 }
1031 }
958 else {
1032 else {
959 if (!posEq(from, sel.from)) {
1033 if (!posEq(from, sel.from)) {
960 if (from.line < oldFrom)
1034 if (from.line < oldFrom)
961 changes.push({from: from.line, to: Math.min(to.line, oldFrom) + 1});
1035 changes.push({from: from.line, to: Math.min(to.line, oldFrom) + 1});
962 else
1036 else
963 changes.push({from: oldFrom, to: Math.min(oldTo, from.line) + 1});
1037 changes.push({from: oldFrom, to: Math.min(oldTo, from.line) + 1});
964 }
1038 }
965 if (!posEq(to, sel.to)) {
1039 if (!posEq(to, sel.to)) {
966 if (to.line < oldTo)
1040 if (to.line < oldTo)
967 changes.push({from: Math.max(oldFrom, from.line), to: oldTo + 1});
1041 changes.push({from: Math.max(oldFrom, from.line), to: oldTo + 1});
968 else
1042 else
969 changes.push({from: Math.max(from.line, oldTo), to: to.line + 1});
1043 changes.push({from: Math.max(from.line, oldTo), to: to.line + 1});
970 }
1044 }
971 }
1045 }
972 sel.from = from; sel.to = to;
1046 sel.from = from; sel.to = to;
973 selectionChanged = true;
1047 selectionChanged = true;
974 }
1048 }
1049 function skipHidden(pos, oldLine, oldCh) {
1050 function getNonHidden(dir) {
1051 var lNo = pos.line + dir, end = dir == 1 ? doc.size : -1;
1052 while (lNo != end) {
1053 var line = getLine(lNo);
1054 if (!line.hidden) {
1055 var ch = pos.ch;
1056 if (ch > oldCh || ch > line.text.length) ch = line.text.length;
1057 return {line: lNo, ch: ch};
1058 }
1059 lNo += dir;
1060 }
1061 }
1062 var line = getLine(pos.line);
1063 if (!line.hidden) return pos;
1064 if (pos.line >= oldLine) return getNonHidden(1) || getNonHidden(-1);
1065 else return getNonHidden(-1) || getNonHidden(1);
1066 }
975 function setCursor(line, ch, user) {
1067 function setCursor(line, ch, user) {
976 var pos = clipPos({line: line, ch: ch || 0});
1068 var pos = clipPos({line: line, ch: ch || 0});
977 (user ? setSelectionUser : setSelection)(pos, pos);
1069 (user ? setSelectionUser : setSelection)(pos, pos);
978 }
1070 }
979
1071
980 function clipLine(n) {return Math.max(0, Math.min(n, lines.length-1));}
1072 function clipLine(n) {return Math.max(0, Math.min(n, doc.size-1));}
981 function clipPos(pos) {
1073 function clipPos(pos) {
982 if (pos.line < 0) return {line: 0, ch: 0};
1074 if (pos.line < 0) return {line: 0, ch: 0};
983 if (pos.line >= lines.length) return {line: lines.length-1, ch: lines[lines.length-1].text.length};
1075 if (pos.line >= doc.size) return {line: doc.size-1, ch: getLine(doc.size-1).text.length};
984 var ch = pos.ch, linelen = lines[pos.line].text.length;
1076 var ch = pos.ch, linelen = getLine(pos.line).text.length;
985 if (ch == null || ch > linelen) return {line: pos.line, ch: linelen};
1077 if (ch == null || ch > linelen) return {line: pos.line, ch: linelen};
986 else if (ch < 0) return {line: pos.line, ch: 0};
1078 else if (ch < 0) return {line: pos.line, ch: 0};
987 else return pos;
1079 else return pos;
988 }
1080 }
989
1081
990 function scrollPage(down) {
1082 function findPosH(dir, unit) {
991 var linesPerPage = Math.floor(scroller.clientHeight / lineHeight()), head = sel.inverted ? sel.from : sel.to;
1083 var end = sel.inverted ? sel.from : sel.to, line = end.line, ch = end.ch;
992 setCursor(head.line + (Math.max(linesPerPage - 1, 1) * (down ? 1 : -1)), head.ch, true);
1084 var lineObj = getLine(line);
1085 function findNextLine() {
1086 for (var l = line + dir, e = dir < 0 ? -1 : doc.size; l != e; l += dir) {
1087 var lo = getLine(l);
1088 if (!lo.hidden) { line = l; lineObj = lo; return true; }
993 }
1089 }
994 function scrollEnd(top) {
995 var pos = top ? {line: 0, ch: 0} : {line: lines.length - 1, ch: lines[lines.length-1].text.length};
996 setSelectionUser(pos, pos);
997 }
1090 }
998 function selectAll() {
1091 function moveOnce(boundToLine) {
999 var endLine = lines.length - 1;
1092 if (ch == (dir < 0 ? 0 : lineObj.text.length)) {
1000 setSelection({line: 0, ch: 0}, {line: endLine, ch: lines[endLine].text.length});
1093 if (!boundToLine && findNextLine()) ch = dir < 0 ? lineObj.text.length : 0;
1094 else return false;
1095 } else ch += dir;
1096 return true;
1001 }
1097 }
1098 if (unit == "char") moveOnce();
1099 else if (unit == "column") moveOnce(true);
1100 else if (unit == "word") {
1101 var sawWord = false;
1102 for (;;) {
1103 if (dir < 0) if (!moveOnce()) break;
1104 if (isWordChar(lineObj.text.charAt(ch))) sawWord = true;
1105 else if (sawWord) {if (dir < 0) {dir = 1; moveOnce();} break;}
1106 if (dir > 0) if (!moveOnce()) break;
1107 }
1108 }
1109 return {line: line, ch: ch};
1110 }
1111 function moveH(dir, unit) {
1112 var pos = dir < 0 ? sel.from : sel.to;
1113 if (shiftSelecting || posEq(sel.from, sel.to)) pos = findPosH(dir, unit);
1114 setCursor(pos.line, pos.ch, true);
1115 }
1116 function deleteH(dir, unit) {
1117 if (!posEq(sel.from, sel.to)) replaceRange("", sel.from, sel.to);
1118 else if (dir < 0) replaceRange("", findPosH(dir, unit), sel.to);
1119 else replaceRange("", sel.from, findPosH(dir, unit));
1120 userSelChange = true;
1121 }
1122 var goalColumn = null;
1123 function moveV(dir, unit) {
1124 var dist = 0, pos = localCoords(sel.inverted ? sel.from : sel.to, true);
1125 if (goalColumn != null) pos.x = goalColumn;
1126 if (unit == "page") dist = scroller.clientHeight;
1127 else if (unit == "line") dist = textHeight();
1128 var target = coordsChar(pos.x, pos.y + dist * dir + 2);
1129 setCursor(target.line, target.ch, true);
1130 goalColumn = pos.x;
1131 }
1132
1002 function selectWordAt(pos) {
1133 function selectWordAt(pos) {
1003 var line = lines[pos.line].text;
1134 var line = getLine(pos.line).text;
1004 var start = pos.ch, end = pos.ch;
1135 var start = pos.ch, end = pos.ch;
1005 while (start > 0 && /\w/.test(line.charAt(start - 1))) --start;
1136 while (start > 0 && isWordChar(line.charAt(start - 1))) --start;
1006 while (end < line.length && /\w/.test(line.charAt(end))) ++end;
1137 while (end < line.length && isWordChar(line.charAt(end))) ++end;
1007 setSelectionUser({line: pos.line, ch: start}, {line: pos.line, ch: end});
1138 setSelectionUser({line: pos.line, ch: start}, {line: pos.line, ch: end});
1008 }
1139 }
1009 function selectLine(line) {
1140 function selectLine(line) {
1010 setSelectionUser({line: line, ch: 0}, {line: line, ch: lines[line].text.length});
1141 setSelectionUser({line: line, ch: 0}, {line: line, ch: getLine(line).text.length});
1011 }
1012 function handleEnter() {
1013 replaceSelection("\n", "end");
1014 if (options.enterMode != "flat")
1015 indentLine(sel.from.line, options.enterMode == "keep" ? "prev" : "smart");
1016 }
1142 }
1017 function handleTab(shift) {
1018 function indentSelected(mode) {
1143 function indentSelected(mode) {
1019 if (posEq(sel.from, sel.to)) return indentLine(sel.from.line, mode);
1144 if (posEq(sel.from, sel.to)) return indentLine(sel.from.line, mode);
1020 var e = sel.to.line - (sel.to.ch ? 0 : 1);
1145 var e = sel.to.line - (sel.to.ch ? 0 : 1);
1021 for (var i = sel.from.line; i <= e; ++i) indentLine(i, mode);
1146 for (var i = sel.from.line; i <= e; ++i) indentLine(i, mode);
1022 }
1147 }
1023 shiftSelecting = null;
1024 switch (options.tabMode) {
1025 case "default":
1026 return false;
1027 case "indent":
1028 indentSelected("smart");
1029 break;
1030 case "classic":
1031 if (posEq(sel.from, sel.to)) {
1032 if (shift) indentLine(sel.from.line, "smart");
1033 else replaceSelection("\t", "end");
1034 break;
1035 }
1036 case "shift":
1037 indentSelected(shift ? "subtract" : "add");
1038 break;
1039 }
1040 return true;
1041 }
1042 function smartHome() {
1043 var firstNonWS = Math.max(0, lines[sel.from.line].text.search(/\S/));
1044 setCursor(sel.from.line, sel.from.ch <= firstNonWS && sel.from.ch ? 0 : firstNonWS, true);
1045 }
1046
1148
1047 function indentLine(n, how) {
1149 function indentLine(n, how) {
1150 if (!how) how = "add";
1048 if (how == "smart") {
1151 if (how == "smart") {
1049 if (!mode.indent) how = "prev";
1152 if (!mode.indent) how = "prev";
1050 else var state = getStateBefore(n);
1153 else var state = getStateBefore(n);
1051 }
1154 }
1052
1155
1053 var line = lines[n], curSpace = line.indentation(), curSpaceString = line.text.match(/^\s*/)[0], indentation;
1156 var line = getLine(n), curSpace = line.indentation(options.tabSize),
1157 curSpaceString = line.text.match(/^\s*/)[0], indentation;
1054 if (how == "prev") {
1158 if (how == "prev") {
1055 if (n) indentation = lines[n-1].indentation();
1159 if (n) indentation = getLine(n-1).indentation(options.tabSize);
1056 else indentation = 0;
1160 else indentation = 0;
1057 }
1161 }
1058 else if (how == "smart") indentation = mode.indent(state, line.text.slice(curSpaceString.length));
1162 else if (how == "smart") indentation = mode.indent(state, line.text.slice(curSpaceString.length), line.text);
1059 else if (how == "add") indentation = curSpace + options.indentUnit;
1163 else if (how == "add") indentation = curSpace + options.indentUnit;
1060 else if (how == "subtract") indentation = curSpace - options.indentUnit;
1164 else if (how == "subtract") indentation = curSpace - options.indentUnit;
1061 indentation = Math.max(0, indentation);
1165 indentation = Math.max(0, indentation);
1062 var diff = indentation - curSpace;
1166 var diff = indentation - curSpace;
1063
1167
1064 if (!diff) {
1168 if (!diff) {
1065 if (sel.from.line != n && sel.to.line != n) return;
1169 if (sel.from.line != n && sel.to.line != n) return;
1066 var indentString = curSpaceString;
1170 var indentString = curSpaceString;
1067 }
1171 }
1068 else {
1172 else {
1069 var indentString = "", pos = 0;
1173 var indentString = "", pos = 0;
1070 if (options.indentWithTabs)
1174 if (options.indentWithTabs)
1071 for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";}
1175 for (var i = Math.floor(indentation / options.tabSize); i; --i) {pos += options.tabSize; indentString += "\t";}
1072 while (pos < indentation) {++pos; indentString += " ";}
1176 while (pos < indentation) {++pos; indentString += " ";}
1073 }
1177 }
1074
1178
1075 replaceRange(indentString, {line: n, ch: 0}, {line: n, ch: curSpaceString.length});
1179 replaceRange(indentString, {line: n, ch: 0}, {line: n, ch: curSpaceString.length});
1076 }
1180 }
1077
1181
1078 function loadMode() {
1182 function loadMode() {
1079 mode = CodeMirror.getMode(options, options.mode);
1183 mode = CodeMirror.getMode(options, options.mode);
1080 for (var i = 0, l = lines.length; i < l; ++i)
1184 doc.iter(0, doc.size, function(line) { line.stateAfter = null; });
1081 lines[i].stateAfter = null;
1082 work = [0];
1185 work = [0];
1083 startWorker();
1186 startWorker();
1084 }
1187 }
1085 function gutterChanged() {
1188 function gutterChanged() {
1086 var visible = options.gutter || options.lineNumbers;
1189 var visible = options.gutter || options.lineNumbers;
1087 gutter.style.display = visible ? "" : "none";
1190 gutter.style.display = visible ? "" : "none";
1088 if (visible) gutterDirty = true;
1191 if (visible) gutterDirty = true;
1089 else lineDiv.parentNode.style.marginLeft = 0;
1192 else lineDiv.parentNode.style.marginLeft = 0;
1090 }
1193 }
1194 function wrappingChanged(from, to) {
1195 if (options.lineWrapping) {
1196 wrapper.className += " CodeMirror-wrap";
1197 var perLine = scroller.clientWidth / charWidth() - 3;
1198 doc.iter(0, doc.size, function(line) {
1199 if (line.hidden) return;
1200 var guess = Math.ceil(line.text.length / perLine) || 1;
1201 if (guess != 1) updateLineHeight(line, guess);
1202 });
1203 lineSpace.style.width = code.style.width = "";
1204 } else {
1205 wrapper.className = wrapper.className.replace(" CodeMirror-wrap", "");
1206 maxWidth = null; maxLine = "";
1207 doc.iter(0, doc.size, function(line) {
1208 if (line.height != 1 && !line.hidden) updateLineHeight(line, 1);
1209 if (line.text.length > maxLine.length) maxLine = line.text;
1210 });
1211 }
1212 changes.push({from: 0, to: doc.size});
1213 }
1214 function computeTabText() {
1215 for (var str = '<span class="cm-tab">', i = 0; i < options.tabSize; ++i) str += " ";
1216 return str + "</span>";
1217 }
1218 function tabsChanged() {
1219 tabText = computeTabText();
1220 updateDisplay(true);
1221 }
1222 function themeChanged() {
1223 scroller.className = scroller.className.replace(/\s*cm-s-\w+/g, "") +
1224 options.theme.replace(/(^|\s)\s*/g, " cm-s-");
1225 }
1226
1227 function TextMarker() { this.set = []; }
1228 TextMarker.prototype.clear = operation(function() {
1229 var min = Infinity, max = -Infinity;
1230 for (var i = 0, e = this.set.length; i < e; ++i) {
1231 var line = this.set[i], mk = line.marked;
1232 if (!mk || !line.parent) continue;
1233 var lineN = lineNo(line);
1234 min = Math.min(min, lineN); max = Math.max(max, lineN);
1235 for (var j = 0; j < mk.length; ++j)
1236 if (mk[j].set == this.set) mk.splice(j--, 1);
1237 }
1238 if (min != Infinity)
1239 changes.push({from: min, to: max + 1});
1240 });
1241 TextMarker.prototype.find = function() {
1242 var from, to;
1243 for (var i = 0, e = this.set.length; i < e; ++i) {
1244 var line = this.set[i], mk = line.marked;
1245 for (var j = 0; j < mk.length; ++j) {
1246 var mark = mk[j];
1247 if (mark.set == this.set) {
1248 if (mark.from != null || mark.to != null) {
1249 var found = lineNo(line);
1250 if (found != null) {
1251 if (mark.from != null) from = {line: found, ch: mark.from};
1252 if (mark.to != null) to = {line: found, ch: mark.to};
1253 }
1254 }
1255 }
1256 }
1257 }
1258 return {from: from, to: to};
1259 };
1091
1260
1092 function markText(from, to, className) {
1261 function markText(from, to, className) {
1093 from = clipPos(from); to = clipPos(to);
1262 from = clipPos(from); to = clipPos(to);
1094 var accum = [];
1263 var tm = new TextMarker();
1095 function add(line, from, to, className) {
1264 function add(line, from, to, className) {
1096 var line = lines[line], mark = line.addMark(from, to, className);
1265 getLine(line).addMark(new MarkedText(from, to, className, tm.set));
1097 mark.line = line;
1098 accum.push(mark);
1099 }
1266 }
1100 if (from.line == to.line) add(from.line, from.ch, to.ch, className);
1267 if (from.line == to.line) add(from.line, from.ch, to.ch, className);
1101 else {
1268 else {
1102 add(from.line, from.ch, null, className);
1269 add(from.line, from.ch, null, className);
1103 for (var i = from.line + 1, e = to.line; i < e; ++i)
1270 for (var i = from.line + 1, e = to.line; i < e; ++i)
1104 add(i, 0, null, className);
1271 add(i, null, null, className);
1105 add(to.line, 0, to.ch, className);
1272 add(to.line, null, to.ch, className);
1106 }
1273 }
1107 changes.push({from: from.line, to: to.line + 1});
1274 changes.push({from: from.line, to: to.line + 1});
1108 return function() {
1275 return tm;
1109 var start, end;
1110 for (var i = 0; i < accum.length; ++i) {
1111 var mark = accum[i], found = indexOf(lines, mark.line);
1112 mark.line.removeMark(mark);
1113 if (found > -1) {
1114 if (start == null) start = found;
1115 end = found;
1116 }
1276 }
1117 }
1277
1118 if (start != null) changes.push({from: start, to: end + 1});
1278 function setBookmark(pos) {
1119 };
1279 pos = clipPos(pos);
1280 var bm = new Bookmark(pos.ch);
1281 getLine(pos.line).addMark(bm);
1282 return bm;
1120 }
1283 }
1121
1284
1122 function addGutterMarker(line, text, className) {
1285 function addGutterMarker(line, text, className) {
1123 if (typeof line == "number") line = lines[clipLine(line)];
1286 if (typeof line == "number") line = getLine(clipLine(line));
1124 line.gutterMarker = {text: text, style: className};
1287 line.gutterMarker = {text: text, style: className};
1125 gutterDirty = true;
1288 gutterDirty = true;
1126 return line;
1289 return line;
1127 }
1290 }
1128 function removeGutterMarker(line) {
1291 function removeGutterMarker(line) {
1129 if (typeof line == "number") line = lines[clipLine(line)];
1292 if (typeof line == "number") line = getLine(clipLine(line));
1130 line.gutterMarker = null;
1293 line.gutterMarker = null;
1131 gutterDirty = true;
1294 gutterDirty = true;
1132 }
1295 }
1133 function setLineClass(line, className) {
1296
1134 if (typeof line == "number") {
1297 function changeLine(handle, op) {
1135 var no = line;
1298 var no = handle, line = handle;
1136 line = lines[clipLine(line)];
1299 if (typeof handle == "number") line = getLine(clipLine(handle));
1137 }
1300 else no = lineNo(handle);
1138 else {
1301 if (no == null) return null;
1139 var no = indexOf(lines, line);
1302 if (op(line, no)) changes.push({from: no, to: no + 1});
1140 if (no == -1) return null;
1303 else return null;
1304 return line;
1141 }
1305 }
1306 function setLineClass(handle, className) {
1307 return changeLine(handle, function(line) {
1142 if (line.className != className) {
1308 if (line.className != className) {
1143 line.className = className;
1309 line.className = className;
1144 changes.push({from: no, to: no + 1});
1310 return true;
1145 }
1311 }
1146 return line;
1312 });
1313 }
1314 function setLineHidden(handle, hidden) {
1315 return changeLine(handle, function(line, no) {
1316 if (line.hidden != hidden) {
1317 line.hidden = hidden;
1318 updateLineHeight(line, hidden ? 0 : 1);
1319 if (hidden && (sel.from.line == no || sel.to.line == no))
1320 setSelection(skipHidden(sel.from, sel.from.line, sel.from.ch),
1321 skipHidden(sel.to, sel.to.line, sel.to.ch));
1322 return (gutterDirty = true);
1323 }
1324 });
1147 }
1325 }
1148
1326
1149 function lineInfo(line) {
1327 function lineInfo(line) {
1150 if (typeof line == "number") {
1328 if (typeof line == "number") {
1329 if (!isLine(line)) return null;
1151 var n = line;
1330 var n = line;
1152 line = lines[line];
1331 line = getLine(line);
1153 if (!line) return null;
1332 if (!line) return null;
1154 }
1333 }
1155 else {
1334 else {
1156 var n = indexOf(lines, line);
1335 var n = lineNo(line);
1157 if (n == -1) return null;
1336 if (n == null) return null;
1158 }
1337 }
1159 var marker = line.gutterMarker;
1338 var marker = line.gutterMarker;
1160 return {line: n, text: line.text, markerText: marker && marker.text, markerClass: marker && marker.style};
1339 return {line: n, handle: line, text: line.text, markerText: marker && marker.text,
1340 markerClass: marker && marker.style, lineClass: line.className};
1161 }
1341 }
1162
1342
1163 function stringWidth(str) {
1343 function stringWidth(str) {
1164 measure.innerHTML = "<pre><span>x</span></pre>";
1344 measure.innerHTML = "<pre><span>x</span></pre>";
1165 measure.firstChild.firstChild.firstChild.nodeValue = str;
1345 measure.firstChild.firstChild.firstChild.nodeValue = str;
1166 return measure.firstChild.firstChild.offsetWidth || 10;
1346 return measure.firstChild.firstChild.offsetWidth || 10;
1167 }
1347 }
1168 // These are used to go from pixel positions to character
1348 // These are used to go from pixel positions to character
1169 // positions, taking varying character widths into account.
1349 // positions, taking varying character widths into account.
1170 function charX(line, pos) {
1171 if (pos == 0) return 0;
1172 measure.innerHTML = "<pre><span>" + lines[line].getHTML(null, null, false, pos) + "</span></pre>";
1173 return measure.firstChild.firstChild.offsetWidth;
1174 }
1175 function charFromX(line, x) {
1350 function charFromX(line, x) {
1176 if (x <= 0) return 0;
1351 if (x <= 0) return 0;
1177 var lineObj = lines[line], text = lineObj.text;
1352 var lineObj = getLine(line), text = lineObj.text;
1178 function getX(len) {
1353 function getX(len) {
1179 measure.innerHTML = "<pre><span>" + lineObj.getHTML(null, null, false, len) + "</span></pre>";
1354 measure.innerHTML = "<pre><span>" + lineObj.getHTML(null, null, false, tabText, len) + "</span></pre>";
1180 return measure.firstChild.firstChild.offsetWidth;
1355 return measure.firstChild.firstChild.offsetWidth;
1181 }
1356 }
1182 var from = 0, fromX = 0, to = text.length, toX;
1357 var from = 0, fromX = 0, to = text.length, toX;
1183 // Guess a suitable upper bound for our search.
1358 // Guess a suitable upper bound for our search.
1184 var estimated = Math.min(to, Math.ceil(x / stringWidth("x")));
1359 var estimated = Math.min(to, Math.ceil(x / charWidth()));
1185 for (;;) {
1360 for (;;) {
1186 var estX = getX(estimated);
1361 var estX = getX(estimated);
1187 if (estX <= x && estimated < to) estimated = Math.min(to, Math.ceil(estimated * 1.2));
1362 if (estX <= x && estimated < to) estimated = Math.min(to, Math.ceil(estimated * 1.2));
1188 else {toX = estX; to = estimated; break;}
1363 else {toX = estX; to = estimated; break;}
1189 }
1364 }
1190 if (x > toX) return to;
1365 if (x > toX) return to;
1191 // Try to guess a suitable lower bound as well.
1366 // Try to guess a suitable lower bound as well.
1192 estimated = Math.floor(to * 0.8); estX = getX(estimated);
1367 estimated = Math.floor(to * 0.8); estX = getX(estimated);
1193 if (estX < x) {from = estimated; fromX = estX;}
1368 if (estX < x) {from = estimated; fromX = estX;}
1194 // Do a binary search between these bounds.
1369 // Do a binary search between these bounds.
1195 for (;;) {
1370 for (;;) {
1196 if (to - from <= 1) return (toX - x > x - fromX) ? from : to;
1371 if (to - from <= 1) return (toX - x > x - fromX) ? from : to;
1197 var middle = Math.ceil((from + to) / 2), middleX = getX(middle);
1372 var middle = Math.ceil((from + to) / 2), middleX = getX(middle);
1198 if (middleX > x) {to = middle; toX = middleX;}
1373 if (middleX > x) {to = middle; toX = middleX;}
1199 else {from = middle; fromX = middleX;}
1374 else {from = middle; fromX = middleX;}
1200 }
1375 }
1201 }
1376 }
1202
1377
1378 var tempId = Math.floor(Math.random() * 0xffffff).toString(16);
1379 function measureLine(line, ch) {
1380 var extra = "";
1381 // Include extra text at the end to make sure the measured line is wrapped in the right way.
1382 if (options.lineWrapping) {
1383 var end = line.text.indexOf(" ", ch + 2);
1384 extra = htmlEscape(line.text.slice(ch + 1, end < 0 ? line.text.length : end + (ie ? 5 : 0)));
1385 }
1386 measure.innerHTML = "<pre>" + line.getHTML(null, null, false, tabText, ch) +
1387 '<span id="CodeMirror-temp-' + tempId + '">' + htmlEscape(line.text.charAt(ch) || " ") + "</span>" +
1388 extra + "</pre>";
1389 var elt = document.getElementById("CodeMirror-temp-" + tempId);
1390 var top = elt.offsetTop, left = elt.offsetLeft;
1391 // Older IEs report zero offsets for spans directly after a wrap
1392 if (ie && ch && top == 0 && left == 0) {
1393 var backup = document.createElement("span");
1394 backup.innerHTML = "x";
1395 elt.parentNode.insertBefore(backup, elt.nextSibling);
1396 top = backup.offsetTop;
1397 }
1398 return {top: top, left: left};
1399 }
1203 function localCoords(pos, inLineWrap) {
1400 function localCoords(pos, inLineWrap) {
1204 var lh = lineHeight(), line = pos.line - (inLineWrap ? showingFrom : 0);
1401 var x, lh = textHeight(), y = lh * (heightAtLine(doc, pos.line) - (inLineWrap ? displayOffset : 0));
1205 return {x: charX(pos.line, pos.ch), y: line * lh, yBot: (line + 1) * lh};
1402 if (pos.ch == 0) x = 0;
1403 else {
1404 var sp = measureLine(getLine(pos.line), pos.ch);
1405 x = sp.left;
1406 if (options.lineWrapping) y += Math.max(0, sp.top);
1407 }
1408 return {x: x, y: y, yBot: y + lh};
1409 }
1410 // Coords must be lineSpace-local
1411 function coordsChar(x, y) {
1412 if (y < 0) y = 0;
1413 var th = textHeight(), cw = charWidth(), heightPos = displayOffset + Math.floor(y / th);
1414 var lineNo = lineAtHeight(doc, heightPos);
1415 if (lineNo >= doc.size) return {line: doc.size - 1, ch: getLine(doc.size - 1).text.length};
1416 var lineObj = getLine(lineNo), text = lineObj.text;
1417 var tw = options.lineWrapping, innerOff = tw ? heightPos - heightAtLine(doc, lineNo) : 0;
1418 if (x <= 0 && innerOff == 0) return {line: lineNo, ch: 0};
1419 function getX(len) {
1420 var sp = measureLine(lineObj, len);
1421 if (tw) {
1422 var off = Math.round(sp.top / th);
1423 return Math.max(0, sp.left + (off - innerOff) * scroller.clientWidth);
1424 }
1425 return sp.left;
1426 }
1427 var from = 0, fromX = 0, to = text.length, toX;
1428 // Guess a suitable upper bound for our search.
1429 var estimated = Math.min(to, Math.ceil((x + innerOff * scroller.clientWidth * .9) / cw));
1430 for (;;) {
1431 var estX = getX(estimated);
1432 if (estX <= x && estimated < to) estimated = Math.min(to, Math.ceil(estimated * 1.2));
1433 else {toX = estX; to = estimated; break;}
1434 }
1435 if (x > toX) return {line: lineNo, ch: to};
1436 // Try to guess a suitable lower bound as well.
1437 estimated = Math.floor(to * 0.8); estX = getX(estimated);
1438 if (estX < x) {from = estimated; fromX = estX;}
1439 // Do a binary search between these bounds.
1440 for (;;) {
1441 if (to - from <= 1) return {line: lineNo, ch: (toX - x > x - fromX) ? from : to};
1442 var middle = Math.ceil((from + to) / 2), middleX = getX(middle);
1443 if (middleX > x) {to = middle; toX = middleX;}
1444 else {from = middle; fromX = middleX;}
1445 }
1206 }
1446 }
1207 function pageCoords(pos) {
1447 function pageCoords(pos) {
1208 var local = localCoords(pos, true), off = eltOffset(lineSpace);
1448 var local = localCoords(pos, true), off = eltOffset(lineSpace);
1209 return {x: off.left + local.x, y: off.top + local.y, yBot: off.top + local.yBot};
1449 return {x: off.left + local.x, y: off.top + local.y, yBot: off.top + local.yBot};
1210 }
1450 }
1211
1451
1212 function lineHeight() {
1452 var cachedHeight, cachedHeightFor, measureText;
1213 var nlines = lineDiv.childNodes.length;
1453 function textHeight() {
1214 if (nlines) return (lineDiv.offsetHeight / nlines) || 1;
1454 if (measureText == null) {
1215 measure.innerHTML = "<pre>x</pre>";
1455 measureText = "<pre>";
1216 return measure.firstChild.offsetHeight || 1;
1456 for (var i = 0; i < 49; ++i) measureText += "x<br/>";
1457 measureText += "x</pre>";
1458 }
1459 var offsetHeight = lineDiv.clientHeight;
1460 if (offsetHeight == cachedHeightFor) return cachedHeight;
1461 cachedHeightFor = offsetHeight;
1462 measure.innerHTML = measureText;
1463 cachedHeight = measure.firstChild.offsetHeight / 50 || 1;
1464 measure.innerHTML = "";
1465 return cachedHeight;
1466 }
1467 var cachedWidth, cachedWidthFor = 0;
1468 function charWidth() {
1469 if (scroller.clientWidth == cachedWidthFor) return cachedWidth;
1470 cachedWidthFor = scroller.clientWidth;
1471 return (cachedWidth = stringWidth("x"));
1217 }
1472 }
1218 function paddingTop() {return lineSpace.offsetTop;}
1473 function paddingTop() {return lineSpace.offsetTop;}
1219 function paddingLeft() {return lineSpace.offsetLeft;}
1474 function paddingLeft() {return lineSpace.offsetLeft;}
1220
1475
1221 function posFromMouse(e, liberal) {
1476 function posFromMouse(e, liberal) {
1222 var offW = eltOffset(scroller, true), x, y;
1477 var offW = eltOffset(scroller, true), x, y;
1223 // Fails unpredictably on IE[67] when mouse is dragged around quickly.
1478 // Fails unpredictably on IE[67] when mouse is dragged around quickly.
1224 try { x = e.clientX; y = e.clientY; } catch (e) { return null; }
1479 try { x = e.clientX; y = e.clientY; } catch (e) { return null; }
1225 // This is a mess of a heuristic to try and determine whether a
1480 // This is a mess of a heuristic to try and determine whether a
1226 // scroll-bar was clicked or not, and to return null if one was
1481 // scroll-bar was clicked or not, and to return null if one was
1227 // (and !liberal).
1482 // (and !liberal).
1228 if (!liberal && (x - offW.left > scroller.clientWidth || y - offW.top > scroller.clientHeight))
1483 if (!liberal && (x - offW.left > scroller.clientWidth || y - offW.top > scroller.clientHeight))
1229 return null;
1484 return null;
1230 var offL = eltOffset(lineSpace, true);
1485 var offL = eltOffset(lineSpace, true);
1231 var line = showingFrom + Math.floor((y - offL.top) / lineHeight());
1486 return coordsChar(x - offL.left, y - offL.top);
1232 return clipPos({line: line, ch: charFromX(clipLine(line), x - offL.left)});
1233 }
1487 }
1234 function onContextMenu(e) {
1488 function onContextMenu(e) {
1235 var pos = posFromMouse(e);
1489 var pos = posFromMouse(e);
1236 if (!pos || window.opera) return; // Opera is difficult.
1490 if (!pos || window.opera) return; // Opera is difficult.
1237 if (posEq(sel.from, sel.to) || posLess(pos, sel.from) || !posLess(pos, sel.to))
1491 if (posEq(sel.from, sel.to) || posLess(pos, sel.from) || !posLess(pos, sel.to))
1238 operation(setCursor)(pos.line, pos.ch);
1492 operation(setCursor)(pos.line, pos.ch);
1239
1493
1240 var oldCSS = input.style.cssText;
1494 var oldCSS = input.style.cssText;
1241 inputDiv.style.position = "absolute";
1495 inputDiv.style.position = "absolute";
1242 input.style.cssText = "position: fixed; width: 30px; height: 30px; top: " + (e.clientY - 5) +
1496 input.style.cssText = "position: fixed; width: 30px; height: 30px; top: " + (e.clientY - 5) +
1243 "px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: white; " +
1497 "px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: white; " +
1244 "border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);";
1498 "border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);";
1245 leaveInputAlone = true;
1499 leaveInputAlone = true;
1246 var val = input.value = getSelection();
1500 var val = input.value = getSelection();
1247 focusInput();
1501 focusInput();
1248 setSelRange(input, 0, input.value.length);
1502 input.select();
1249 function rehide() {
1503 function rehide() {
1250 var newVal = splitLines(input.value).join("\n");
1504 var newVal = splitLines(input.value).join("\n");
1251 if (newVal != val) operation(replaceSelection)(newVal, "end");
1505 if (newVal != val) operation(replaceSelection)(newVal, "end");
1252 inputDiv.style.position = "relative";
1506 inputDiv.style.position = "relative";
1253 input.style.cssText = oldCSS;
1507 input.style.cssText = oldCSS;
1254 leaveInputAlone = false;
1508 leaveInputAlone = false;
1255 prepareInput();
1509 resetInput(true);
1256 slowPoll();
1510 slowPoll();
1257 }
1511 }
1258
1512
1259 if (gecko) {
1513 if (gecko) {
1260 e_stop(e);
1514 e_stop(e);
1261 var mouseup = connect(window, "mouseup", function() {
1515 var mouseup = connect(window, "mouseup", function() {
1262 mouseup();
1516 mouseup();
1263 setTimeout(rehide, 20);
1517 setTimeout(rehide, 20);
1264 }, true);
1518 }, true);
1265 }
1519 }
1266 else {
1520 else {
1267 setTimeout(rehide, 50);
1521 setTimeout(rehide, 50);
1268 }
1522 }
1269 }
1523 }
1270
1524
1271 // Cursor-blinking
1525 // Cursor-blinking
1272 function restartBlink() {
1526 function restartBlink() {
1273 clearInterval(blinker);
1527 clearInterval(blinker);
1274 var on = true;
1528 var on = true;
1275 cursor.style.visibility = "";
1529 cursor.style.visibility = "";
1276 blinker = setInterval(function() {
1530 blinker = setInterval(function() {
1277 cursor.style.visibility = (on = !on) ? "" : "hidden";
1531 cursor.style.visibility = (on = !on) ? "" : "hidden";
1278 }, 650);
1532 }, 650);
1279 }
1533 }
1280
1534
1281 var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<"};
1535 var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<"};
1282 function matchBrackets(autoclear) {
1536 function matchBrackets(autoclear) {
1283 var head = sel.inverted ? sel.from : sel.to, line = lines[head.line], pos = head.ch - 1;
1537 var head = sel.inverted ? sel.from : sel.to, line = getLine(head.line), pos = head.ch - 1;
1284 var match = (pos >= 0 && matching[line.text.charAt(pos)]) || matching[line.text.charAt(++pos)];
1538 var match = (pos >= 0 && matching[line.text.charAt(pos)]) || matching[line.text.charAt(++pos)];
1285 if (!match) return;
1539 if (!match) return;
1286 var ch = match.charAt(0), forward = match.charAt(1) == ">", d = forward ? 1 : -1, st = line.styles;
1540 var ch = match.charAt(0), forward = match.charAt(1) == ">", d = forward ? 1 : -1, st = line.styles;
1287 for (var off = pos + 1, i = 0, e = st.length; i < e; i+=2)
1541 for (var off = pos + 1, i = 0, e = st.length; i < e; i+=2)
1288 if ((off -= st[i].length) <= 0) {var style = st[i+1]; break;}
1542 if ((off -= st[i].length) <= 0) {var style = st[i+1]; break;}
1289
1543
1290 var stack = [line.text.charAt(pos)], re = /[(){}[\]]/;
1544 var stack = [line.text.charAt(pos)], re = /[(){}[\]]/;
1291 function scan(line, from, to) {
1545 function scan(line, from, to) {
1292 if (!line.text) return;
1546 if (!line.text) return;
1293 var st = line.styles, pos = forward ? 0 : line.text.length - 1, cur;
1547 var st = line.styles, pos = forward ? 0 : line.text.length - 1, cur;
1294 for (var i = forward ? 0 : st.length - 2, e = forward ? st.length : -2; i != e; i += 2*d) {
1548 for (var i = forward ? 0 : st.length - 2, e = forward ? st.length : -2; i != e; i += 2*d) {
1295 var text = st[i];
1549 var text = st[i];
1296 if (st[i+1] != null && st[i+1] != style) {pos += d * text.length; continue;}
1550 if (st[i+1] != null && st[i+1] != style) {pos += d * text.length; continue;}
1297 for (var j = forward ? 0 : text.length - 1, te = forward ? text.length : -1; j != te; j += d, pos+=d) {
1551 for (var j = forward ? 0 : text.length - 1, te = forward ? text.length : -1; j != te; j += d, pos+=d) {
1298 if (pos >= from && pos < to && re.test(cur = text.charAt(j))) {
1552 if (pos >= from && pos < to && re.test(cur = text.charAt(j))) {
1299 var match = matching[cur];
1553 var match = matching[cur];
1300 if (match.charAt(1) == ">" == forward) stack.push(cur);
1554 if (match.charAt(1) == ">" == forward) stack.push(cur);
1301 else if (stack.pop() != match.charAt(0)) return {pos: pos, match: false};
1555 else if (stack.pop() != match.charAt(0)) return {pos: pos, match: false};
1302 else if (!stack.length) return {pos: pos, match: true};
1556 else if (!stack.length) return {pos: pos, match: true};
1303 }
1557 }
1304 }
1558 }
1305 }
1559 }
1306 }
1560 }
1307 for (var i = head.line, e = forward ? Math.min(i + 100, lines.length) : Math.max(-1, i - 100); i != e; i+=d) {
1561 for (var i = head.line, e = forward ? Math.min(i + 100, doc.size) : Math.max(-1, i - 100); i != e; i+=d) {
1308 var line = lines[i], first = i == head.line;
1562 var line = getLine(i), first = i == head.line;
1309 var found = scan(line, first && forward ? pos + 1 : 0, first && !forward ? pos : line.text.length);
1563 var found = scan(line, first && forward ? pos + 1 : 0, first && !forward ? pos : line.text.length);
1310 if (found) break;
1564 if (found) break;
1311 }
1565 }
1312 if (!found) found = {pos: null, match: false};
1566 if (!found) found = {pos: null, match: false};
1313 var style = found.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket";
1567 var style = found.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket";
1314 var one = markText({line: head.line, ch: pos}, {line: head.line, ch: pos+1}, style),
1568 var one = markText({line: head.line, ch: pos}, {line: head.line, ch: pos+1}, style),
1315 two = found.pos != null
1569 two = found.pos != null && markText({line: i, ch: found.pos}, {line: i, ch: found.pos + 1}, style);
1316 ? markText({line: i, ch: found.pos}, {line: i, ch: found.pos + 1}, style)
1570 var clear = operation(function(){one.clear(); two && two.clear();});
1317 : function() {};
1318 var clear = operation(function(){one(); two();});
1319 if (autoclear) setTimeout(clear, 800);
1571 if (autoclear) setTimeout(clear, 800);
1320 else bracketHighlighted = clear;
1572 else bracketHighlighted = clear;
1321 }
1573 }
1322
1574
1323 // Finds the line to start with when starting a parse. Tries to
1575 // Finds the line to start with when starting a parse. Tries to
1324 // find a line with a stateAfter, so that it can start with a
1576 // find a line with a stateAfter, so that it can start with a
1325 // valid state. If that fails, it returns the line with the
1577 // valid state. If that fails, it returns the line with the
1326 // smallest indentation, which tends to need the least context to
1578 // smallest indentation, which tends to need the least context to
1327 // parse correctly.
1579 // parse correctly.
1328 function findStartLine(n) {
1580 function findStartLine(n) {
1329 var minindent, minline;
1581 var minindent, minline;
1330 for (var search = n, lim = n - 40; search > lim; --search) {
1582 for (var search = n, lim = n - 40; search > lim; --search) {
1331 if (search == 0) return 0;
1583 if (search == 0) return 0;
1332 var line = lines[search-1];
1584 var line = getLine(search-1);
1333 if (line.stateAfter) return search;
1585 if (line.stateAfter) return search;
1334 var indented = line.indentation();
1586 var indented = line.indentation(options.tabSize);
1335 if (minline == null || minindent > indented) {
1587 if (minline == null || minindent > indented) {
1336 minline = search - 1;
1588 minline = search - 1;
1337 minindent = indented;
1589 minindent = indented;
1338 }
1590 }
1339 }
1591 }
1340 return minline;
1592 return minline;
1341 }
1593 }
1342 function getStateBefore(n) {
1594 function getStateBefore(n) {
1343 var start = findStartLine(n), state = start && lines[start-1].stateAfter;
1595 var start = findStartLine(n), state = start && getLine(start-1).stateAfter;
1344 if (!state) state = startState(mode);
1596 if (!state) state = startState(mode);
1345 else state = copyState(mode, state);
1597 else state = copyState(mode, state);
1346 for (var i = start; i < n; ++i) {
1598 doc.iter(start, n, function(line) {
1347 var line = lines[i];
1599 line.highlight(mode, state, options.tabSize);
1348 line.highlight(mode, state);
1349 line.stateAfter = copyState(mode, state);
1600 line.stateAfter = copyState(mode, state);
1350 }
1601 });
1351 if (n < lines.length && !lines[n].stateAfter) work.push(n);
1602 if (start < n) changes.push({from: start, to: n});
1603 if (n < doc.size && !getLine(n).stateAfter) work.push(n);
1352 return state;
1604 return state;
1353 }
1605 }
1354 function highlightLines(start, end) {
1606 function highlightLines(start, end) {
1355 var state = getStateBefore(start);
1607 var state = getStateBefore(start);
1356 for (var i = start; i < end; ++i) {
1608 doc.iter(start, end, function(line) {
1357 var line = lines[i];
1609 line.highlight(mode, state, options.tabSize);
1358 line.highlight(mode, state);
1359 line.stateAfter = copyState(mode, state);
1610 line.stateAfter = copyState(mode, state);
1360 }
1611 });
1361 }
1612 }
1362 function highlightWorker() {
1613 function highlightWorker() {
1363 var end = +new Date + options.workTime;
1614 var end = +new Date + options.workTime;
1364 var foundWork = work.length;
1615 var foundWork = work.length;
1365 while (work.length) {
1616 while (work.length) {
1366 if (!lines[showingFrom].stateAfter) var task = showingFrom;
1617 if (!getLine(showingFrom).stateAfter) var task = showingFrom;
1367 else var task = work.pop();
1618 else var task = work.pop();
1368 if (task >= lines.length) continue;
1619 if (task >= doc.size) continue;
1369 var start = findStartLine(task), state = start && lines[start-1].stateAfter;
1620 var start = findStartLine(task), state = start && getLine(start-1).stateAfter;
1370 if (state) state = copyState(mode, state);
1621 if (state) state = copyState(mode, state);
1371 else state = startState(mode);
1622 else state = startState(mode);
1372
1623
1373 var unchanged = 0, compare = mode.compareStates, realChange = false;
1624 var unchanged = 0, compare = mode.compareStates, realChange = false,
1374 for (var i = start, l = lines.length; i < l; ++i) {
1625 i = start, bail = false;
1375 var line = lines[i], hadState = line.stateAfter;
1626 doc.iter(i, doc.size, function(line) {
1627 var hadState = line.stateAfter;
1376 if (+new Date > end) {
1628 if (+new Date > end) {
1377 work.push(i);
1629 work.push(i);
1378 startWorker(options.workDelay);
1630 startWorker(options.workDelay);
1379 if (realChange) changes.push({from: task, to: i + 1});
1631 if (realChange) changes.push({from: task, to: i + 1});
1380 return;
1632 return (bail = true);
1381 }
1633 }
1382 var changed = line.highlight(mode, state);
1634 var changed = line.highlight(mode, state, options.tabSize);
1383 if (changed) realChange = true;
1635 if (changed) realChange = true;
1384 line.stateAfter = copyState(mode, state);
1636 line.stateAfter = copyState(mode, state);
1385 if (compare) {
1637 if (compare) {
1386 if (hadState && compare(hadState, state)) break;
1638 if (hadState && compare(hadState, state)) return true;
1387 } else {
1639 } else {
1388 if (changed !== false || !hadState) unchanged = 0;
1640 if (changed !== false || !hadState) unchanged = 0;
1389 else if (++unchanged > 3) break;
1641 else if (++unchanged > 3 && (!mode.indent || mode.indent(hadState, "") == mode.indent(state, "")))
1390 }
1642 return true;
1391 }
1643 }
1644 ++i;
1645 });
1646 if (bail) return;
1392 if (realChange) changes.push({from: task, to: i + 1});
1647 if (realChange) changes.push({from: task, to: i + 1});
1393 }
1648 }
1394 if (foundWork && options.onHighlightComplete)
1649 if (foundWork && options.onHighlightComplete)
1395 options.onHighlightComplete(instance);
1650 options.onHighlightComplete(instance);
1396 }
1651 }
1397 function startWorker(time) {
1652 function startWorker(time) {
1398 if (!work.length) return;
1653 if (!work.length) return;
1399 highlight.set(time, operation(highlightWorker));
1654 highlight.set(time, operation(highlightWorker));
1400 }
1655 }
1401
1656
1402 // Operations are used to wrap changes in such a way that each
1657 // Operations are used to wrap changes in such a way that each
1403 // change won't have to update the cursor and display (which would
1658 // change won't have to update the cursor and display (which would
1404 // be awkward, slow, and error-prone), but instead updates are
1659 // be awkward, slow, and error-prone), but instead updates are
1405 // batched and then all combined and executed at once.
1660 // batched and then all combined and executed at once.
1406 function startOperation() {
1661 function startOperation() {
1407 updateInput = null; changes = []; textChanged = selectionChanged = false;
1662 updateInput = userSelChange = textChanged = null;
1663 changes = []; selectionChanged = false; callbacks = [];
1408 }
1664 }
1409 function endOperation() {
1665 function endOperation() {
1410 var reScroll = false;
1666 var reScroll = false, updated;
1411 if (selectionChanged) reScroll = !scrollCursorIntoView();
1667 if (selectionChanged) reScroll = !scrollCursorIntoView();
1412 if (changes.length) updateDisplay(changes);
1668 if (changes.length) updated = updateDisplay(changes, true);
1413 else {
1669 else {
1414 if (selectionChanged) updateCursor();
1670 if (selectionChanged) updateCursor();
1415 if (gutterDirty) updateGutter();
1671 if (gutterDirty) updateGutter();
1416 }
1672 }
1417 if (reScroll) scrollCursorIntoView();
1673 if (reScroll) scrollCursorIntoView();
1418 if (selectionChanged) {scrollEditorIntoView(); restartBlink();}
1674 if (selectionChanged) {scrollEditorIntoView(); restartBlink();}
1419
1675
1420 // updateInput can be set to a boolean value to force/prevent an
1421 // update.
1422 if (focused && !leaveInputAlone &&
1676 if (focused && !leaveInputAlone &&
1423 (updateInput === true || (updateInput !== false && selectionChanged)))
1677 (updateInput === true || (updateInput !== false && selectionChanged)))
1424 prepareInput();
1678 resetInput(userSelChange);
1425
1679
1426 if (selectionChanged && options.matchBrackets)
1680 if (selectionChanged && options.matchBrackets)
1427 setTimeout(operation(function() {
1681 setTimeout(operation(function() {
1428 if (bracketHighlighted) {bracketHighlighted(); bracketHighlighted = null;}
1682 if (bracketHighlighted) {bracketHighlighted(); bracketHighlighted = null;}
1429 matchBrackets(false);
1683 if (posEq(sel.from, sel.to)) matchBrackets(false);
1430 }), 20);
1684 }), 20);
1431 var tc = textChanged; // textChanged can be reset by cursoractivity callback
1685 var tc = textChanged, cbs = callbacks; // these can be reset by callbacks
1432 if (selectionChanged && options.onCursorActivity)
1686 if (selectionChanged && options.onCursorActivity)
1433 options.onCursorActivity(instance);
1687 options.onCursorActivity(instance);
1434 if (tc && options.onChange && instance)
1688 if (tc && options.onChange && instance)
1435 options.onChange(instance, tc);
1689 options.onChange(instance, tc);
1690 for (var i = 0; i < cbs.length; ++i) cbs[i](instance);
1691 if (updated && options.onUpdate) options.onUpdate(instance);
1436 }
1692 }
1437 var nestedOperation = 0;
1693 var nestedOperation = 0;
1438 function operation(f) {
1694 function operation(f) {
1439 return function() {
1695 return function() {
1440 if (!nestedOperation++) startOperation();
1696 if (!nestedOperation++) startOperation();
1441 try {var result = f.apply(this, arguments);}
1697 try {var result = f.apply(this, arguments);}
1442 finally {if (!--nestedOperation) endOperation();}
1698 finally {if (!--nestedOperation) endOperation();}
1443 return result;
1699 return result;
1444 };
1700 };
1445 }
1701 }
1446
1702
1447 function SearchCursor(query, pos, caseFold) {
1448 this.atOccurrence = false;
1449 if (caseFold == null) caseFold = typeof query == "string" && query == query.toLowerCase();
1450
1451 if (pos && typeof pos == "object") pos = clipPos(pos);
1452 else pos = {line: 0, ch: 0};
1453 this.pos = {from: pos, to: pos};
1454
1455 // The matches method is filled in based on the type of query.
1456 // It takes a position and a direction, and returns an object
1457 // describing the next occurrence of the query, or null if no
1458 // more matches were found.
1459 if (typeof query != "string") // Regexp match
1460 this.matches = function(reverse, pos) {
1461 if (reverse) {
1462 var line = lines[pos.line].text.slice(0, pos.ch), match = line.match(query), start = 0;
1463 while (match) {
1464 var ind = line.indexOf(match[0]);
1465 start += ind;
1466 line = line.slice(ind + 1);
1467 var newmatch = line.match(query);
1468 if (newmatch) match = newmatch;
1469 else break;
1470 start++;
1471 }
1472 }
1473 else {
1474 var line = lines[pos.line].text.slice(pos.ch), match = line.match(query),
1475 start = match && pos.ch + line.indexOf(match[0]);
1476 }
1477 if (match)
1478 return {from: {line: pos.line, ch: start},
1479 to: {line: pos.line, ch: start + match[0].length},
1480 match: match};
1481 };
1482 else { // String query
1483 if (caseFold) query = query.toLowerCase();
1484 var fold = caseFold ? function(str){return str.toLowerCase();} : function(str){return str;};
1485 var target = query.split("\n");
1486 // Different methods for single-line and multi-line queries
1487 if (target.length == 1)
1488 this.matches = function(reverse, pos) {
1489 var line = fold(lines[pos.line].text), len = query.length, match;
1490 if (reverse ? (pos.ch >= len && (match = line.lastIndexOf(query, pos.ch - len)) != -1)
1491 : (match = line.indexOf(query, pos.ch)) != -1)
1492 return {from: {line: pos.line, ch: match},
1493 to: {line: pos.line, ch: match + len}};
1494 };
1495 else
1496 this.matches = function(reverse, pos) {
1497 var ln = pos.line, idx = (reverse ? target.length - 1 : 0), match = target[idx], line = fold(lines[ln].text);
1498 var offsetA = (reverse ? line.indexOf(match) + match.length : line.lastIndexOf(match));
1499 if (reverse ? offsetA >= pos.ch || offsetA != match.length
1500 : offsetA <= pos.ch || offsetA != line.length - match.length)
1501 return;
1502 for (;;) {
1503 if (reverse ? !ln : ln == lines.length - 1) return;
1504 line = fold(lines[ln += reverse ? -1 : 1].text);
1505 match = target[reverse ? --idx : ++idx];
1506 if (idx > 0 && idx < target.length - 1) {
1507 if (line != match) return;
1508 else continue;
1509 }
1510 var offsetB = (reverse ? line.lastIndexOf(match) : line.indexOf(match) + match.length);
1511 if (reverse ? offsetB != line.length - match.length : offsetB != match.length)
1512 return;
1513 var start = {line: pos.line, ch: offsetA}, end = {line: ln, ch: offsetB};
1514 return {from: reverse ? end : start, to: reverse ? start : end};
1515 }
1516 };
1517 }
1518 }
1519
1520 SearchCursor.prototype = {
1521 findNext: function() {return this.find(false);},
1522 findPrevious: function() {return this.find(true);},
1523
1524 find: function(reverse) {
1525 var self = this, pos = clipPos(reverse ? this.pos.from : this.pos.to);
1526 function savePosAndFail(line) {
1527 var pos = {line: line, ch: 0};
1528 self.pos = {from: pos, to: pos};
1529 self.atOccurrence = false;
1530 return false;
1531 }
1532
1533 for (;;) {
1534 if (this.pos = this.matches(reverse, pos)) {
1535 this.atOccurrence = true;
1536 return this.pos.match || true;
1537 }
1538 if (reverse) {
1539 if (!pos.line) return savePosAndFail(0);
1540 pos = {line: pos.line-1, ch: lines[pos.line-1].text.length};
1541 }
1542 else {
1543 if (pos.line == lines.length - 1) return savePosAndFail(lines.length);
1544 pos = {line: pos.line+1, ch: 0};
1545 }
1546 }
1547 },
1548
1549 from: function() {if (this.atOccurrence) return copyPos(this.pos.from);},
1550 to: function() {if (this.atOccurrence) return copyPos(this.pos.to);},
1551
1552 replace: function(newText) {
1553 var self = this;
1554 if (this.atOccurrence)
1555 operation(function() {
1556 self.pos.to = replaceRange(newText, self.pos.from, self.pos.to);
1557 })();
1558 }
1559 };
1560
1561 for (var ext in extensions)
1703 for (var ext in extensions)
1562 if (extensions.propertyIsEnumerable(ext) &&
1704 if (extensions.propertyIsEnumerable(ext) &&
1563 !instance.propertyIsEnumerable(ext))
1705 !instance.propertyIsEnumerable(ext))
1564 instance[ext] = extensions[ext];
1706 instance[ext] = extensions[ext];
1565 return instance;
1707 return instance;
1566 } // (end of function CodeMirror)
1708 } // (end of function CodeMirror)
1567
1709
1568 // The default configuration options.
1710 // The default configuration options.
1569 CodeMirror.defaults = {
1711 CodeMirror.defaults = {
1570 value: "",
1712 value: "",
1571 mode: null,
1713 mode: null,
1572 theme: "default",
1714 theme: "default",
1573 indentUnit: 2,
1715 indentUnit: 2,
1574 indentWithTabs: false,
1716 indentWithTabs: false,
1575 tabMode: "classic",
1717 tabSize: 4,
1576 enterMode: "indent",
1718 keyMap: "default",
1719 extraKeys: null,
1577 electricChars: true,
1720 electricChars: true,
1578 onKeyEvent: null,
1721 onKeyEvent: null,
1722 lineWrapping: false,
1579 lineNumbers: false,
1723 lineNumbers: false,
1580 gutter: false,
1724 gutter: false,
1581 fixedGutter: false,
1725 fixedGutter: false,
1582 firstLineNumber: 1,
1726 firstLineNumber: 1,
1583 readOnly: false,
1727 readOnly: false,
1584 smartHome: true,
1585 onChange: null,
1728 onChange: null,
1586 onCursorActivity: null,
1729 onCursorActivity: null,
1587 onGutterClick: null,
1730 onGutterClick: null,
1588 onHighlightComplete: null,
1731 onHighlightComplete: null,
1732 onUpdate: null,
1589 onFocus: null, onBlur: null, onScroll: null,
1733 onFocus: null, onBlur: null, onScroll: null,
1590 matchBrackets: false,
1734 matchBrackets: false,
1591 workTime: 100,
1735 workTime: 100,
1592 workDelay: 200,
1736 workDelay: 200,
1737 pollInterval: 100,
1593 undoDepth: 40,
1738 undoDepth: 40,
1594 tabindex: null,
1739 tabindex: null,
1595 document: window.document
1740 document: window.document
1596 };
1741 };
1597
1742
1743 var mac = /Mac/.test(navigator.platform);
1744 var win = /Win/.test(navigator.platform);
1745
1598 // Known modes, by name and by MIME
1746 // Known modes, by name and by MIME
1599 var modes = {}, mimeModes = {};
1747 var modes = {}, mimeModes = {};
1600 CodeMirror.defineMode = function(name, mode) {
1748 CodeMirror.defineMode = function(name, mode) {
1601 if (!CodeMirror.defaults.mode && name != "null") CodeMirror.defaults.mode = name;
1749 if (!CodeMirror.defaults.mode && name != "null") CodeMirror.defaults.mode = name;
1602 modes[name] = mode;
1750 modes[name] = mode;
1603 };
1751 };
1604 CodeMirror.defineMIME = function(mime, spec) {
1752 CodeMirror.defineMIME = function(mime, spec) {
1605 mimeModes[mime] = spec;
1753 mimeModes[mime] = spec;
1606 };
1754 };
1607 CodeMirror.getMode = function(options, spec) {
1755 CodeMirror.getMode = function(options, spec) {
1608 if (typeof spec == "string" && mimeModes.hasOwnProperty(spec))
1756 if (typeof spec == "string" && mimeModes.hasOwnProperty(spec))
1609 spec = mimeModes[spec];
1757 spec = mimeModes[spec];
1610 if (typeof spec == "string")
1758 if (typeof spec == "string")
1611 var mname = spec, config = {};
1759 var mname = spec, config = {};
1612 else if (spec != null)
1760 else if (spec != null)
1613 var mname = spec.name, config = spec;
1761 var mname = spec.name, config = spec;
1614 var mfactory = modes[mname];
1762 var mfactory = modes[mname];
1615 if (!mfactory) {
1763 if (!mfactory) {
1616 if (window.console) console.warn("No mode " + mname + " found, falling back to plain text.");
1764 if (window.console) console.warn("No mode " + mname + " found, falling back to plain text.");
1617 return CodeMirror.getMode(options, "text/plain");
1765 return CodeMirror.getMode(options, "text/plain");
1618 }
1766 }
1619 return mfactory(options, config || {});
1767 return mfactory(options, config || {});
1620 };
1768 };
1621 CodeMirror.listModes = function() {
1769 CodeMirror.listModes = function() {
1622 var list = [];
1770 var list = [];
1623 for (var m in modes)
1771 for (var m in modes)
1624 if (modes.propertyIsEnumerable(m)) list.push(m);
1772 if (modes.propertyIsEnumerable(m)) list.push(m);
1625 return list;
1773 return list;
1626 };
1774 };
1627 CodeMirror.listMIMEs = function() {
1775 CodeMirror.listMIMEs = function() {
1628 var list = [];
1776 var list = [];
1629 for (var m in mimeModes)
1777 for (var m in mimeModes)
1630 if (mimeModes.propertyIsEnumerable(m)) list.push({mime: m, mode: mimeModes[m]});
1778 if (mimeModes.propertyIsEnumerable(m)) list.push({mime: m, mode: mimeModes[m]});
1631 return list;
1779 return list;
1632 };
1780 };
1633
1781
1634 var extensions = {};
1782 var extensions = CodeMirror.extensions = {};
1635 CodeMirror.defineExtension = function(name, func) {
1783 CodeMirror.defineExtension = function(name, func) {
1636 extensions[name] = func;
1784 extensions[name] = func;
1637 };
1785 };
1638
1786
1787 var commands = CodeMirror.commands = {
1788 selectAll: function(cm) {cm.setSelection({line: 0, ch: 0}, {line: cm.lineCount() - 1});},
1789 killLine: function(cm) {
1790 var from = cm.getCursor(true), to = cm.getCursor(false), sel = !posEq(from, to);
1791 if (!sel && cm.getLine(from.line).length == from.ch) cm.replaceRange("", from, {line: from.line + 1, ch: 0});
1792 else cm.replaceRange("", from, sel ? to : {line: from.line});
1793 },
1794 deleteLine: function(cm) {var l = cm.getCursor().line; cm.replaceRange("", {line: l, ch: 0}, {line: l});},
1795 undo: function(cm) {cm.undo();},
1796 redo: function(cm) {cm.redo();},
1797 goDocStart: function(cm) {cm.setCursor(0, 0, true);},
1798 goDocEnd: function(cm) {cm.setSelection({line: cm.lineCount() - 1}, null, true);},
1799 goLineStart: function(cm) {cm.setCursor(cm.getCursor().line, 0, true);},
1800 goLineStartSmart: function(cm) {
1801 var cur = cm.getCursor();
1802 var text = cm.getLine(cur.line), firstNonWS = Math.max(0, text.search(/\S/));
1803 cm.setCursor(cur.line, cur.ch <= firstNonWS && cur.ch ? 0 : firstNonWS, true);
1804 },
1805 goLineEnd: function(cm) {cm.setSelection({line: cm.getCursor().line}, null, true);},
1806 goLineUp: function(cm) {cm.moveV(-1, "line");},
1807 goLineDown: function(cm) {cm.moveV(1, "line");},
1808 goPageUp: function(cm) {cm.moveV(-1, "page");},
1809 goPageDown: function(cm) {cm.moveV(1, "page");},
1810 goCharLeft: function(cm) {cm.moveH(-1, "char");},
1811 goCharRight: function(cm) {cm.moveH(1, "char");},
1812 goColumnLeft: function(cm) {cm.moveH(-1, "column");},
1813 goColumnRight: function(cm) {cm.moveH(1, "column");},
1814 goWordLeft: function(cm) {cm.moveH(-1, "word");},
1815 goWordRight: function(cm) {cm.moveH(1, "word");},
1816 delCharLeft: function(cm) {cm.deleteH(-1, "char");},
1817 delCharRight: function(cm) {cm.deleteH(1, "char");},
1818 delWordLeft: function(cm) {cm.deleteH(-1, "word");},
1819 delWordRight: function(cm) {cm.deleteH(1, "word");},
1820 indentAuto: function(cm) {cm.indentSelection("smart");},
1821 indentMore: function(cm) {cm.indentSelection("add");},
1822 indentLess: function(cm) {cm.indentSelection("subtract");},
1823 insertTab: function(cm) {cm.replaceSelection("\t", "end");},
1824 transposeChars: function(cm) {
1825 var cur = cm.getCursor(), line = cm.getLine(cur.line);
1826 if (cur.ch > 0 && cur.ch < line.length - 1)
1827 cm.replaceRange(line.charAt(cur.ch) + line.charAt(cur.ch - 1),
1828 {line: cur.line, ch: cur.ch - 1}, {line: cur.line, ch: cur.ch + 1});
1829 },
1830 newlineAndIndent: function(cm) {
1831 cm.replaceSelection("\n", "end");
1832 cm.indentLine(cm.getCursor().line);
1833 },
1834 toggleOverwrite: function(cm) {cm.toggleOverwrite();}
1835 };
1836
1837 var keyMap = CodeMirror.keyMap = {};
1838 keyMap.basic = {
1839 "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
1840 "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
1841 "Delete": "delCharRight", "Backspace": "delCharLeft", "Tab": "indentMore", "Shift-Tab": "indentLess",
1842 "Enter": "newlineAndIndent", "Insert": "toggleOverwrite"
1843 };
1844 // Note that the save and find-related commands aren't defined by
1845 // default. Unknown commands are simply ignored.
1846 keyMap.pcDefault = {
1847 "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
1848 "Ctrl-Home": "goDocStart", "Alt-Up": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Down": "goDocEnd",
1849 "Ctrl-Left": "goWordLeft", "Ctrl-Right": "goWordRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
1850 "Ctrl-Backspace": "delWordLeft", "Ctrl-Delete": "delWordRight", "Ctrl-S": "save", "Ctrl-F": "find",
1851 "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
1852 fallthrough: "basic"
1853 };
1854 keyMap.macDefault = {
1855 "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
1856 "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goWordLeft",
1857 "Alt-Right": "goWordRight", "Cmd-Left": "goLineStart", "Cmd-Right": "goLineEnd", "Alt-Backspace": "delWordLeft",
1858 "Ctrl-Alt-Backspace": "delWordRight", "Alt-Delete": "delWordRight", "Cmd-S": "save", "Cmd-F": "find",
1859 "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
1860 fallthrough: ["basic", "emacsy"]
1861 };
1862 keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault;
1863 keyMap.emacsy = {
1864 "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
1865 "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
1866 "Ctrl-V": "goPageUp", "Shift-Ctrl-V": "goPageDown", "Ctrl-D": "delCharRight", "Ctrl-H": "delCharLeft",
1867 "Alt-D": "delWordRight", "Alt-Backspace": "delWordLeft", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars"
1868 };
1869
1870 function lookupKey(name, extraMap, map) {
1871 function lookup(name, map, ft) {
1872 var found = map[name];
1873 if (found != null) return found;
1874 if (ft == null) ft = map.fallthrough;
1875 if (ft == null) return map.catchall;
1876 if (typeof ft == "string") return lookup(name, keyMap[ft]);
1877 for (var i = 0, e = ft.length; i < e; ++i) {
1878 found = lookup(name, keyMap[ft[i]]);
1879 if (found != null) return found;
1880 }
1881 return null;
1882 }
1883 return extraMap ? lookup(name, extraMap, map) : lookup(name, keyMap[map]);
1884 }
1885 function isModifierKey(event) {
1886 var name = keyNames[event.keyCode];
1887 return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod";
1888 }
1889
1639 CodeMirror.fromTextArea = function(textarea, options) {
1890 CodeMirror.fromTextArea = function(textarea, options) {
1640 if (!options) options = {};
1891 if (!options) options = {};
1641 options.value = textarea.value;
1892 options.value = textarea.value;
1642 if (!options.tabindex && textarea.tabindex)
1893 if (!options.tabindex && textarea.tabindex)
1643 options.tabindex = textarea.tabindex;
1894 options.tabindex = textarea.tabindex;
1644
1895
1645 function save() {textarea.value = instance.getValue();}
1896 function save() {textarea.value = instance.getValue();}
1646 if (textarea.form) {
1897 if (textarea.form) {
1647 // Deplorable hack to make the submit method do the right thing.
1898 // Deplorable hack to make the submit method do the right thing.
1648 var rmSubmit = connect(textarea.form, "submit", save, true);
1899 var rmSubmit = connect(textarea.form, "submit", save, true);
1649 if (typeof textarea.form.submit == "function") {
1900 if (typeof textarea.form.submit == "function") {
1650 var realSubmit = textarea.form.submit;
1901 var realSubmit = textarea.form.submit;
1651 function wrappedSubmit() {
1902 function wrappedSubmit() {
1652 save();
1903 save();
1653 textarea.form.submit = realSubmit;
1904 textarea.form.submit = realSubmit;
1654 textarea.form.submit();
1905 textarea.form.submit();
1655 textarea.form.submit = wrappedSubmit;
1906 textarea.form.submit = wrappedSubmit;
1656 }
1907 }
1657 textarea.form.submit = wrappedSubmit;
1908 textarea.form.submit = wrappedSubmit;
1658 }
1909 }
1659 }
1910 }
1660
1911
1661 textarea.style.display = "none";
1912 textarea.style.display = "none";
1662 var instance = CodeMirror(function(node) {
1913 var instance = CodeMirror(function(node) {
1663 textarea.parentNode.insertBefore(node, textarea.nextSibling);
1914 textarea.parentNode.insertBefore(node, textarea.nextSibling);
1664 }, options);
1915 }, options);
1665 instance.save = save;
1916 instance.save = save;
1917 instance.getTextArea = function() { return textarea; };
1666 instance.toTextArea = function() {
1918 instance.toTextArea = function() {
1667 save();
1919 save();
1668 textarea.parentNode.removeChild(instance.getWrapperElement());
1920 textarea.parentNode.removeChild(instance.getWrapperElement());
1669 textarea.style.display = "";
1921 textarea.style.display = "";
1670 if (textarea.form) {
1922 if (textarea.form) {
1671 rmSubmit();
1923 rmSubmit();
1672 if (typeof textarea.form.submit == "function")
1924 if (typeof textarea.form.submit == "function")
1673 textarea.form.submit = realSubmit;
1925 textarea.form.submit = realSubmit;
1674 }
1926 }
1675 };
1927 };
1676 return instance;
1928 return instance;
1677 };
1929 };
1678
1930
1679 // Utility functions for working with state. Exported because modes
1931 // Utility functions for working with state. Exported because modes
1680 // sometimes need to do this.
1932 // sometimes need to do this.
1681 function copyState(mode, state) {
1933 function copyState(mode, state) {
1682 if (state === true) return state;
1934 if (state === true) return state;
1683 if (mode.copyState) return mode.copyState(state);
1935 if (mode.copyState) return mode.copyState(state);
1684 var nstate = {};
1936 var nstate = {};
1685 for (var n in state) {
1937 for (var n in state) {
1686 var val = state[n];
1938 var val = state[n];
1687 if (val instanceof Array) val = val.concat([]);
1939 if (val instanceof Array) val = val.concat([]);
1688 nstate[n] = val;
1940 nstate[n] = val;
1689 }
1941 }
1690 return nstate;
1942 return nstate;
1691 }
1943 }
1692 CodeMirror.startState = startState;
1944 CodeMirror.copyState = copyState;
1693 function startState(mode, a1, a2) {
1945 function startState(mode, a1, a2) {
1694 return mode.startState ? mode.startState(a1, a2) : true;
1946 return mode.startState ? mode.startState(a1, a2) : true;
1695 }
1947 }
1696 CodeMirror.copyState = copyState;
1948 CodeMirror.startState = startState;
1697
1949
1698 // The character stream used by a mode's parser.
1950 // The character stream used by a mode's parser.
1699 function StringStream(string) {
1951 function StringStream(string, tabSize) {
1700 this.pos = this.start = 0;
1952 this.pos = this.start = 0;
1701 this.string = string;
1953 this.string = string;
1954 this.tabSize = tabSize || 8;
1702 }
1955 }
1703 StringStream.prototype = {
1956 StringStream.prototype = {
1704 eol: function() {return this.pos >= this.string.length;},
1957 eol: function() {return this.pos >= this.string.length;},
1705 sol: function() {return this.pos == 0;},
1958 sol: function() {return this.pos == 0;},
1706 peek: function() {return this.string.charAt(this.pos);},
1959 peek: function() {return this.string.charAt(this.pos);},
1707 next: function() {
1960 next: function() {
1708 if (this.pos < this.string.length)
1961 if (this.pos < this.string.length)
1709 return this.string.charAt(this.pos++);
1962 return this.string.charAt(this.pos++);
1710 },
1963 },
1711 eat: function(match) {
1964 eat: function(match) {
1712 var ch = this.string.charAt(this.pos);
1965 var ch = this.string.charAt(this.pos);
1713 if (typeof match == "string") var ok = ch == match;
1966 if (typeof match == "string") var ok = ch == match;
1714 else var ok = ch && (match.test ? match.test(ch) : match(ch));
1967 else var ok = ch && (match.test ? match.test(ch) : match(ch));
1715 if (ok) {++this.pos; return ch;}
1968 if (ok) {++this.pos; return ch;}
1716 },
1969 },
1717 eatWhile: function(match) {
1970 eatWhile: function(match) {
1718 var start = this.pos;
1971 var start = this.pos;
1719 while (this.eat(match)){}
1972 while (this.eat(match)){}
1720 return this.pos > start;
1973 return this.pos > start;
1721 },
1974 },
1722 eatSpace: function() {
1975 eatSpace: function() {
1723 var start = this.pos;
1976 var start = this.pos;
1724 while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) ++this.pos;
1977 while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) ++this.pos;
1725 return this.pos > start;
1978 return this.pos > start;
1726 },
1979 },
1727 skipToEnd: function() {this.pos = this.string.length;},
1980 skipToEnd: function() {this.pos = this.string.length;},
1728 skipTo: function(ch) {
1981 skipTo: function(ch) {
1729 var found = this.string.indexOf(ch, this.pos);
1982 var found = this.string.indexOf(ch, this.pos);
1730 if (found > -1) {this.pos = found; return true;}
1983 if (found > -1) {this.pos = found; return true;}
1731 },
1984 },
1732 backUp: function(n) {this.pos -= n;},
1985 backUp: function(n) {this.pos -= n;},
1733 column: function() {return countColumn(this.string, this.start);},
1986 column: function() {return countColumn(this.string, this.start, this.tabSize);},
1734 indentation: function() {return countColumn(this.string);},
1987 indentation: function() {return countColumn(this.string, null, this.tabSize);},
1735 match: function(pattern, consume, caseInsensitive) {
1988 match: function(pattern, consume, caseInsensitive) {
1736 if (typeof pattern == "string") {
1989 if (typeof pattern == "string") {
1737 function cased(str) {return caseInsensitive ? str.toLowerCase() : str;}
1990 function cased(str) {return caseInsensitive ? str.toLowerCase() : str;}
1738 if (cased(this.string).indexOf(cased(pattern), this.pos) == this.pos) {
1991 if (cased(this.string).indexOf(cased(pattern), this.pos) == this.pos) {
1739 if (consume !== false) this.pos += pattern.length;
1992 if (consume !== false) this.pos += pattern.length;
1740 return true;
1993 return true;
1741 }
1994 }
1742 }
1995 }
1743 else {
1996 else {
1744 var match = this.string.slice(this.pos).match(pattern);
1997 var match = this.string.slice(this.pos).match(pattern);
1745 if (match && consume !== false) this.pos += match[0].length;
1998 if (match && consume !== false) this.pos += match[0].length;
1746 return match;
1999 return match;
1747 }
2000 }
1748 },
2001 },
1749 current: function(){return this.string.slice(this.start, this.pos);}
2002 current: function(){return this.string.slice(this.start, this.pos);}
1750 };
2003 };
1751 CodeMirror.StringStream = StringStream;
2004 CodeMirror.StringStream = StringStream;
1752
2005
2006 function MarkedText(from, to, className, set) {
2007 this.from = from; this.to = to; this.style = className; this.set = set;
2008 }
2009 MarkedText.prototype = {
2010 attach: function(line) { this.set.push(line); },
2011 detach: function(line) {
2012 var ix = indexOf(this.set, line);
2013 if (ix > -1) this.set.splice(ix, 1);
2014 },
2015 split: function(pos, lenBefore) {
2016 if (this.to <= pos && this.to != null) return null;
2017 var from = this.from < pos || this.from == null ? null : this.from - pos + lenBefore;
2018 var to = this.to == null ? null : this.to - pos + lenBefore;
2019 return new MarkedText(from, to, this.style, this.set);
2020 },
2021 dup: function() { return new MarkedText(null, null, this.style, this.set); },
2022 clipTo: function(fromOpen, from, toOpen, to, diff) {
2023 if (this.from != null && this.from >= from)
2024 this.from = Math.max(to, this.from) + diff;
2025 if (this.to != null && this.to > from)
2026 this.to = to < this.to ? this.to + diff : from;
2027 if (fromOpen && to > this.from && (to < this.to || this.to == null))
2028 this.from = null;
2029 if (toOpen && (from < this.to || this.to == null) && (from > this.from || this.from == null))
2030 this.to = null;
2031 },
2032 isDead: function() { return this.from != null && this.to != null && this.from >= this.to; },
2033 sameSet: function(x) { return this.set == x.set; }
2034 };
2035
2036 function Bookmark(pos) {
2037 this.from = pos; this.to = pos; this.line = null;
2038 }
2039 Bookmark.prototype = {
2040 attach: function(line) { this.line = line; },
2041 detach: function(line) { if (this.line == line) this.line = null; },
2042 split: function(pos, lenBefore) {
2043 if (pos < this.from) {
2044 this.from = this.to = (this.from - pos) + lenBefore;
2045 return this;
2046 }
2047 },
2048 isDead: function() { return this.from > this.to; },
2049 clipTo: function(fromOpen, from, toOpen, to, diff) {
2050 if ((fromOpen || from < this.from) && (toOpen || to > this.to)) {
2051 this.from = 0; this.to = -1;
2052 } else if (this.from > from) {
2053 this.from = this.to = Math.max(to, this.from) + diff;
2054 }
2055 },
2056 sameSet: function(x) { return false; },
2057 find: function() {
2058 if (!this.line || !this.line.parent) return null;
2059 return {line: lineNo(this.line), ch: this.from};
2060 },
2061 clear: function() {
2062 if (this.line) {
2063 var found = indexOf(this.line.marked, this);
2064 if (found != -1) this.line.marked.splice(found, 1);
2065 this.line = null;
2066 }
2067 }
2068 };
2069
1753 // Line objects. These hold state related to a line, including
2070 // Line objects. These hold state related to a line, including
1754 // highlighting info (the styles array).
2071 // highlighting info (the styles array).
1755 function Line(text, styles) {
2072 function Line(text, styles) {
1756 this.styles = styles || [text, null];
2073 this.styles = styles || [text, null];
1757 this.stateAfter = null;
1758 this.text = text;
2074 this.text = text;
1759 this.marked = this.gutterMarker = this.className = null;
2075 this.height = 1;
2076 this.marked = this.gutterMarker = this.className = this.handlers = null;
2077 this.stateAfter = this.parent = this.hidden = null;
2078 }
2079 Line.inheritMarks = function(text, orig) {
2080 var ln = new Line(text), mk = orig && orig.marked;
2081 if (mk) {
2082 for (var i = 0; i < mk.length; ++i) {
2083 if (mk[i].to == null && mk[i].style) {
2084 var newmk = ln.marked || (ln.marked = []), mark = mk[i];
2085 var nmark = mark.dup(); newmk.push(nmark); nmark.attach(ln);
2086 }
2087 }
2088 }
2089 return ln;
1760 }
2090 }
1761 Line.prototype = {
2091 Line.prototype = {
1762 // Replace a piece of a line, keeping the styles around it intact.
2092 // Replace a piece of a line, keeping the styles around it intact.
1763 replace: function(from, to, text) {
2093 replace: function(from, to_, text) {
1764 var st = [], mk = this.marked;
2094 var st = [], mk = this.marked, to = to_ == null ? this.text.length : to_;
1765 copyStyles(0, from, this.styles, st);
2095 copyStyles(0, from, this.styles, st);
1766 if (text) st.push(text, null);
2096 if (text) st.push(text, null);
1767 copyStyles(to, this.text.length, this.styles, st);
2097 copyStyles(to, this.text.length, this.styles, st);
1768 this.styles = st;
2098 this.styles = st;
1769 this.text = this.text.slice(0, from) + text + this.text.slice(to);
2099 this.text = this.text.slice(0, from) + text + this.text.slice(to);
1770 this.stateAfter = null;
2100 this.stateAfter = null;
1771 if (mk) {
2101 if (mk) {
1772 var diff = text.length - (to - from), end = this.text.length;
2102 var diff = text.length - (to - from);
1773 function fix(n) {return n <= Math.min(to, to + diff) ? n : n + diff;}
2103 for (var i = 0, mark = mk[i]; i < mk.length; ++i) {
1774 for (var i = 0; i < mk.length; ++i) {
2104 mark.clipTo(from == null, from || 0, to_ == null, to, diff);
1775 var mark = mk[i], del = false;
2105 if (mark.isDead()) {mark.detach(this); mk.splice(i--, 1);}
1776 if (mark.from >= end) del = true;
1777 else {mark.from = fix(mark.from); if (mark.to != null) mark.to = fix(mark.to);}
1778 if (del || mark.from >= mark.to) {mk.splice(i, 1); i--;}
1779 }
2106 }
1780 }
2107 }
1781 },
2108 },
1782 // Split a line in two, again keeping styles intact.
2109 // Split a part off a line, keeping styles and markers intact.
1783 split: function(pos, textBefore) {
2110 split: function(pos, textBefore) {
1784 var st = [textBefore, null];
2111 var st = [textBefore, null], mk = this.marked;
1785 copyStyles(pos, this.text.length, this.styles, st);
2112 copyStyles(pos, this.text.length, this.styles, st);
1786 return new Line(textBefore + this.text.slice(pos), st);
2113 var taken = new Line(textBefore + this.text.slice(pos), st);
2114 if (mk) {
2115 for (var i = 0; i < mk.length; ++i) {
2116 var mark = mk[i];
2117 var newmark = mark.split(pos, textBefore.length);
2118 if (newmark) {
2119 if (!taken.marked) taken.marked = [];
2120 taken.marked.push(newmark); newmark.attach(taken);
2121 }
2122 }
2123 }
2124 return taken;
1787 },
2125 },
1788 addMark: function(from, to, style) {
2126 append: function(line) {
1789 var mk = this.marked, mark = {from: from, to: to, style: style};
2127 var mylen = this.text.length, mk = line.marked, mymk = this.marked;
1790 if (this.marked == null) this.marked = [];
2128 this.text += line.text;
1791 this.marked.push(mark);
2129 copyStyles(0, line.text.length, line.styles, this.styles);
1792 this.marked.sort(function(a, b){return a.from - b.from;});
2130 if (mymk) {
1793 return mark;
2131 for (var i = 0; i < mymk.length; ++i)
2132 if (mymk[i].to == null) mymk[i].to = mylen;
2133 }
2134 if (mk && mk.length) {
2135 if (!mymk) this.marked = mymk = [];
2136 outer: for (var i = 0; i < mk.length; ++i) {
2137 var mark = mk[i];
2138 if (!mark.from) {
2139 for (var j = 0; j < mymk.length; ++j) {
2140 var mymark = mymk[j];
2141 if (mymark.to == mylen && mymark.sameSet(mark)) {
2142 mymark.to = mark.to == null ? null : mark.to + mylen;
2143 if (mymark.isDead()) {
2144 mymark.detach(this);
2145 mk.splice(i--, 1);
2146 }
2147 continue outer;
2148 }
2149 }
2150 }
2151 mymk.push(mark);
2152 mark.attach(this);
2153 mark.from += mylen;
2154 if (mark.to != null) mark.to += mylen;
2155 }
2156 }
1794 },
2157 },
1795 removeMark: function(mark) {
2158 fixMarkEnds: function(other) {
2159 var mk = this.marked, omk = other.marked;
2160 if (!mk) return;
2161 for (var i = 0; i < mk.length; ++i) {
2162 var mark = mk[i], close = mark.to == null;
2163 if (close && omk) {
2164 for (var j = 0; j < omk.length; ++j)
2165 if (omk[j].sameSet(mark)) {close = false; break;}
2166 }
2167 if (close) mark.to = this.text.length;
2168 }
2169 },
2170 fixMarkStarts: function() {
1796 var mk = this.marked;
2171 var mk = this.marked;
1797 if (!mk) return;
2172 if (!mk) return;
1798 for (var i = 0; i < mk.length; ++i)
2173 for (var i = 0; i < mk.length; ++i)
1799 if (mk[i] == mark) {mk.splice(i, 1); break;}
2174 if (mk[i].from == null) mk[i].from = 0;
2175 },
2176 addMark: function(mark) {
2177 mark.attach(this);
2178 if (this.marked == null) this.marked = [];
2179 this.marked.push(mark);
2180 this.marked.sort(function(a, b){return (a.from || 0) - (b.from || 0);});
1800 },
2181 },
1801 // Run the given mode's parser over a line, update the styles
2182 // Run the given mode's parser over a line, update the styles
1802 // array, which contains alternating fragments of text and CSS
2183 // array, which contains alternating fragments of text and CSS
1803 // classes.
2184 // classes.
1804 highlight: function(mode, state) {
2185 highlight: function(mode, state, tabSize) {
1805 var stream = new StringStream(this.text), st = this.styles, pos = 0;
2186 var stream = new StringStream(this.text, tabSize), st = this.styles, pos = 0;
1806 var changed = false, curWord = st[0], prevWord;
2187 var changed = false, curWord = st[0], prevWord;
1807 if (this.text == "" && mode.blankLine) mode.blankLine(state);
2188 if (this.text == "" && mode.blankLine) mode.blankLine(state);
1808 while (!stream.eol()) {
2189 while (!stream.eol()) {
1809 var style = mode.token(stream, state);
2190 var style = mode.token(stream, state);
1810 var substr = this.text.slice(stream.start, stream.pos);
2191 var substr = this.text.slice(stream.start, stream.pos);
1811 stream.start = stream.pos;
2192 stream.start = stream.pos;
1812 if (pos && st[pos-1] == style)
2193 if (pos && st[pos-1] == style)
1813 st[pos-2] += substr;
2194 st[pos-2] += substr;
1814 else if (substr) {
2195 else if (substr) {
1815 if (!changed && (st[pos+1] != style || (pos && st[pos-2] != prevWord))) changed = true;
2196 if (!changed && (st[pos+1] != style || (pos && st[pos-2] != prevWord))) changed = true;
1816 st[pos++] = substr; st[pos++] = style;
2197 st[pos++] = substr; st[pos++] = style;
1817 prevWord = curWord; curWord = st[pos];
2198 prevWord = curWord; curWord = st[pos];
1818 }
2199 }
1819 // Give up when line is ridiculously long
2200 // Give up when line is ridiculously long
1820 if (stream.pos > 5000) {
2201 if (stream.pos > 5000) {
1821 st[pos++] = this.text.slice(stream.pos); st[pos++] = null;
2202 st[pos++] = this.text.slice(stream.pos); st[pos++] = null;
1822 break;
2203 break;
1823 }
2204 }
1824 }
2205 }
1825 if (st.length != pos) {st.length = pos; changed = true;}
2206 if (st.length != pos) {st.length = pos; changed = true;}
1826 if (pos && st[pos-2] != prevWord) changed = true;
2207 if (pos && st[pos-2] != prevWord) changed = true;
1827 // Short lines with simple highlights return null, and are
2208 // Short lines with simple highlights return null, and are
1828 // counted as changed by the driver because they are likely to
2209 // counted as changed by the driver because they are likely to
1829 // highlight the same way in various contexts.
2210 // highlight the same way in various contexts.
1830 return changed || (st.length < 5 && this.text.length < 10 ? null : false);
2211 return changed || (st.length < 5 && this.text.length < 10 ? null : false);
1831 },
2212 },
1832 // Fetch the parser token for a given character. Useful for hacks
2213 // Fetch the parser token for a given character. Useful for hacks
1833 // that want to inspect the mode state (say, for completion).
2214 // that want to inspect the mode state (say, for completion).
1834 getTokenAt: function(mode, state, ch) {
2215 getTokenAt: function(mode, state, ch) {
1835 var txt = this.text, stream = new StringStream(txt);
2216 var txt = this.text, stream = new StringStream(txt);
1836 while (stream.pos < ch && !stream.eol()) {
2217 while (stream.pos < ch && !stream.eol()) {
1837 stream.start = stream.pos;
2218 stream.start = stream.pos;
1838 var style = mode.token(stream, state);
2219 var style = mode.token(stream, state);
1839 }
2220 }
1840 return {start: stream.start,
2221 return {start: stream.start,
1841 end: stream.pos,
2222 end: stream.pos,
1842 string: stream.current(),
2223 string: stream.current(),
1843 className: style || null,
2224 className: style || null,
1844 state: state};
2225 state: state};
1845 },
2226 },
1846 indentation: function() {return countColumn(this.text);},
2227 indentation: function(tabSize) {return countColumn(this.text, null, tabSize);},
1847 // Produces an HTML fragment for the line, taking selection,
2228 // Produces an HTML fragment for the line, taking selection,
1848 // marking, and highlighting into account.
2229 // marking, and highlighting into account.
1849 getHTML: function(sfrom, sto, includePre, endAt) {
2230 getHTML: function(sfrom, sto, includePre, tabText, endAt) {
1850 var html = [];
2231 var html = [], first = true;
1851 if (includePre)
2232 if (includePre)
1852 html.push(this.className ? '<pre class="' + this.className + '">': "<pre>");
2233 html.push(this.className ? '<pre class="' + this.className + '">': "<pre>");
1853 function span(text, style) {
2234 function span(text, style) {
1854 if (!text) return;
2235 if (!text) return;
1855 if (style) html.push('<span class="', style, '">', htmlEscape(text), "</span>");
2236 // Work around a bug where, in some compat modes, IE ignores leading spaces
1856 else html.push(htmlEscape(text));
2237 if (first && ie && text.charAt(0) == " ") text = "\u00a0" + text.slice(1);
2238 first = false;
2239 if (style) html.push('<span class="', style, '">', htmlEscape(text).replace(/\t/g, tabText), "</span>");
2240 else html.push(htmlEscape(text).replace(/\t/g, tabText));
1857 }
2241 }
1858 var st = this.styles, allText = this.text, marked = this.marked;
2242 var st = this.styles, allText = this.text, marked = this.marked;
1859 if (sfrom == sto) sfrom = null;
2243 if (sfrom == sto) sfrom = null;
1860 var len = allText.length;
2244 var len = allText.length;
1861 if (endAt != null) len = Math.min(endAt, len);
2245 if (endAt != null) len = Math.min(endAt, len);
1862
2246
1863 if (!allText && endAt == null)
2247 if (!allText && endAt == null)
1864 span(" ", sfrom != null && sto == null ? "CodeMirror-selected" : null);
2248 span(" ", sfrom != null && sto == null ? "CodeMirror-selected" : null);
1865 else if (!marked && sfrom == null)
2249 else if (!marked && sfrom == null)
1866 for (var i = 0, ch = 0; ch < len; i+=2) {
2250 for (var i = 0, ch = 0; ch < len; i+=2) {
1867 var str = st[i], l = str.length;
2251 var str = st[i], style = st[i+1], l = str.length;
1868 if (ch + l > len) str = str.slice(0, len - ch);
2252 if (ch + l > len) str = str.slice(0, len - ch);
1869 ch += l;
2253 ch += l;
1870 span(str, "cm-" + st[i+1]);
2254 span(str, style && "cm-" + style);
1871 }
2255 }
1872 else {
2256 else {
1873 var pos = 0, i = 0, text = "", style, sg = 0;
2257 var pos = 0, i = 0, text = "", style, sg = 0;
1874 var markpos = -1, mark = null;
2258 var markpos = -1, mark = null;
1875 function nextMark() {
2259 function nextMark() {
1876 if (marked) {
2260 if (marked) {
1877 markpos += 1;
2261 markpos += 1;
1878 mark = (markpos < marked.length) ? marked[markpos] : null;
2262 mark = (markpos < marked.length) ? marked[markpos] : null;
1879 }
2263 }
1880 }
2264 }
1881 nextMark();
2265 nextMark();
1882 while (pos < len) {
2266 while (pos < len) {
1883 var upto = len;
2267 var upto = len;
1884 var extraStyle = "";
2268 var extraStyle = "";
1885 if (sfrom != null) {
2269 if (sfrom != null) {
1886 if (sfrom > pos) upto = sfrom;
2270 if (sfrom > pos) upto = sfrom;
1887 else if (sto == null || sto > pos) {
2271 else if (sto == null || sto > pos) {
1888 extraStyle = " CodeMirror-selected";
2272 extraStyle = " CodeMirror-selected";
1889 if (sto != null) upto = Math.min(upto, sto);
2273 if (sto != null) upto = Math.min(upto, sto);
1890 }
2274 }
1891 }
2275 }
1892 while (mark && mark.to != null && mark.to <= pos) nextMark();
2276 while (mark && mark.to != null && mark.to <= pos) nextMark();
1893 if (mark) {
2277 if (mark) {
1894 if (mark.from > pos) upto = Math.min(upto, mark.from);
2278 if (mark.from > pos) upto = Math.min(upto, mark.from);
1895 else {
2279 else {
1896 extraStyle += " " + mark.style;
2280 extraStyle += " " + mark.style;
1897 if (mark.to != null) upto = Math.min(upto, mark.to);
2281 if (mark.to != null) upto = Math.min(upto, mark.to);
1898 }
2282 }
1899 }
2283 }
1900 for (;;) {
2284 for (;;) {
1901 var end = pos + text.length;
2285 var end = pos + text.length;
1902 var appliedStyle = style;
2286 var appliedStyle = style;
1903 if (extraStyle) appliedStyle = style ? style + extraStyle : extraStyle;
2287 if (extraStyle) appliedStyle = style ? style + extraStyle : extraStyle;
1904 span(end > upto ? text.slice(0, upto - pos) : text, appliedStyle);
2288 span(end > upto ? text.slice(0, upto - pos) : text, appliedStyle);
1905 if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;}
2289 if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;}
1906 pos = end;
2290 pos = end;
1907 text = st[i++]; style = "cm-" + st[i++];
2291 text = st[i++]; style = "cm-" + st[i++];
1908 }
2292 }
1909 }
2293 }
1910 if (sfrom != null && sto == null) span(" ", "CodeMirror-selected");
2294 if (sfrom != null && sto == null) span(" ", "CodeMirror-selected");
1911 }
2295 }
1912 if (includePre) html.push("</pre>");
2296 if (includePre) html.push("</pre>");
1913 return html.join("");
2297 return html.join("");
2298 },
2299 cleanUp: function() {
2300 this.parent = null;
2301 if (this.marked)
2302 for (var i = 0, e = this.marked.length; i < e; ++i) this.marked[i].detach(this);
1914 }
2303 }
1915 };
2304 };
1916 // Utility used by replace and split above
2305 // Utility used by replace and split above
1917 function copyStyles(from, to, source, dest) {
2306 function copyStyles(from, to, source, dest) {
1918 for (var i = 0, pos = 0, state = 0; pos < to; i+=2) {
2307 for (var i = 0, pos = 0, state = 0; pos < to; i+=2) {
1919 var part = source[i], end = pos + part.length;
2308 var part = source[i], end = pos + part.length;
1920 if (state == 0) {
2309 if (state == 0) {
1921 if (end > from) dest.push(part.slice(from - pos, Math.min(part.length, to - pos)), source[i+1]);
2310 if (end > from) dest.push(part.slice(from - pos, Math.min(part.length, to - pos)), source[i+1]);
1922 if (end >= from) state = 1;
2311 if (end >= from) state = 1;
1923 }
2312 }
1924 else if (state == 1) {
2313 else if (state == 1) {
1925 if (end > to) dest.push(part.slice(0, to - pos), source[i+1]);
2314 if (end > to) dest.push(part.slice(0, to - pos), source[i+1]);
1926 else dest.push(part, source[i+1]);
2315 else dest.push(part, source[i+1]);
1927 }
2316 }
1928 pos = end;
2317 pos = end;
1929 }
2318 }
1930 }
2319 }
1931
2320
2321 // Data structure that holds the sequence of lines.
2322 function LeafChunk(lines) {
2323 this.lines = lines;
2324 this.parent = null;
2325 for (var i = 0, e = lines.length, height = 0; i < e; ++i) {
2326 lines[i].parent = this;
2327 height += lines[i].height;
2328 }
2329 this.height = height;
2330 }
2331 LeafChunk.prototype = {
2332 chunkSize: function() { return this.lines.length; },
2333 remove: function(at, n, callbacks) {
2334 for (var i = at, e = at + n; i < e; ++i) {
2335 var line = this.lines[i];
2336 this.height -= line.height;
2337 line.cleanUp();
2338 if (line.handlers)
2339 for (var j = 0; j < line.handlers.length; ++j) callbacks.push(line.handlers[j]);
2340 }
2341 this.lines.splice(at, n);
2342 },
2343 collapse: function(lines) {
2344 lines.splice.apply(lines, [lines.length, 0].concat(this.lines));
2345 },
2346 insertHeight: function(at, lines, height) {
2347 this.height += height;
2348 this.lines.splice.apply(this.lines, [at, 0].concat(lines));
2349 for (var i = 0, e = lines.length; i < e; ++i) lines[i].parent = this;
2350 },
2351 iterN: function(at, n, op) {
2352 for (var e = at + n; at < e; ++at)
2353 if (op(this.lines[at])) return true;
2354 }
2355 };
2356 function BranchChunk(children) {
2357 this.children = children;
2358 var size = 0, height = 0;
2359 for (var i = 0, e = children.length; i < e; ++i) {
2360 var ch = children[i];
2361 size += ch.chunkSize(); height += ch.height;
2362 ch.parent = this;
2363 }
2364 this.size = size;
2365 this.height = height;
2366 this.parent = null;
2367 }
2368 BranchChunk.prototype = {
2369 chunkSize: function() { return this.size; },
2370 remove: function(at, n, callbacks) {
2371 this.size -= n;
2372 for (var i = 0; i < this.children.length; ++i) {
2373 var child = this.children[i], sz = child.chunkSize();
2374 if (at < sz) {
2375 var rm = Math.min(n, sz - at), oldHeight = child.height;
2376 child.remove(at, rm, callbacks);
2377 this.height -= oldHeight - child.height;
2378 if (sz == rm) { this.children.splice(i--, 1); child.parent = null; }
2379 if ((n -= rm) == 0) break;
2380 at = 0;
2381 } else at -= sz;
2382 }
2383 if (this.size - n < 25) {
2384 var lines = [];
2385 this.collapse(lines);
2386 this.children = [new LeafChunk(lines)];
2387 }
2388 },
2389 collapse: function(lines) {
2390 for (var i = 0, e = this.children.length; i < e; ++i) this.children[i].collapse(lines);
2391 },
2392 insert: function(at, lines) {
2393 var height = 0;
2394 for (var i = 0, e = lines.length; i < e; ++i) height += lines[i].height;
2395 this.insertHeight(at, lines, height);
2396 },
2397 insertHeight: function(at, lines, height) {
2398 this.size += lines.length;
2399 this.height += height;
2400 for (var i = 0, e = this.children.length; i < e; ++i) {
2401 var child = this.children[i], sz = child.chunkSize();
2402 if (at <= sz) {
2403 child.insertHeight(at, lines, height);
2404 if (child.lines && child.lines.length > 50) {
2405 while (child.lines.length > 50) {
2406 var spilled = child.lines.splice(child.lines.length - 25, 25);
2407 var newleaf = new LeafChunk(spilled);
2408 child.height -= newleaf.height;
2409 this.children.splice(i + 1, 0, newleaf);
2410 newleaf.parent = this;
2411 }
2412 this.maybeSpill();
2413 }
2414 break;
2415 }
2416 at -= sz;
2417 }
2418 },
2419 maybeSpill: function() {
2420 if (this.children.length <= 10) return;
2421 var me = this;
2422 do {
2423 var spilled = me.children.splice(me.children.length - 5, 5);
2424 var sibling = new BranchChunk(spilled);
2425 if (!me.parent) { // Become the parent node
2426 var copy = new BranchChunk(me.children);
2427 copy.parent = me;
2428 me.children = [copy, sibling];
2429 me = copy;
2430 } else {
2431 me.size -= sibling.size;
2432 me.height -= sibling.height;
2433 var myIndex = indexOf(me.parent.children, me);
2434 me.parent.children.splice(myIndex + 1, 0, sibling);
2435 }
2436 sibling.parent = me.parent;
2437 } while (me.children.length > 10);
2438 me.parent.maybeSpill();
2439 },
2440 iter: function(from, to, op) { this.iterN(from, to - from, op); },
2441 iterN: function(at, n, op) {
2442 for (var i = 0, e = this.children.length; i < e; ++i) {
2443 var child = this.children[i], sz = child.chunkSize();
2444 if (at < sz) {
2445 var used = Math.min(n, sz - at);
2446 if (child.iterN(at, used, op)) return true;
2447 if ((n -= used) == 0) break;
2448 at = 0;
2449 } else at -= sz;
2450 }
2451 }
2452 };
2453
2454 function getLineAt(chunk, n) {
2455 while (!chunk.lines) {
2456 for (var i = 0;; ++i) {
2457 var child = chunk.children[i], sz = child.chunkSize();
2458 if (n < sz) { chunk = child; break; }
2459 n -= sz;
2460 }
2461 }
2462 return chunk.lines[n];
2463 }
2464 function lineNo(line) {
2465 if (line.parent == null) return null;
2466 var cur = line.parent, no = indexOf(cur.lines, line);
2467 for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {
2468 for (var i = 0, e = chunk.children.length; ; ++i) {
2469 if (chunk.children[i] == cur) break;
2470 no += chunk.children[i].chunkSize();
2471 }
2472 }
2473 return no;
2474 }
2475 function lineAtHeight(chunk, h) {
2476 var n = 0;
2477 outer: do {
2478 for (var i = 0, e = chunk.children.length; i < e; ++i) {
2479 var child = chunk.children[i], ch = child.height;
2480 if (h < ch) { chunk = child; continue outer; }
2481 h -= ch;
2482 n += child.chunkSize();
2483 }
2484 return n;
2485 } while (!chunk.lines);
2486 for (var i = 0, e = chunk.lines.length; i < e; ++i) {
2487 var line = chunk.lines[i], lh = line.height;
2488 if (h < lh) break;
2489 h -= lh;
2490 }
2491 return n + i;
2492 }
2493 function heightAtLine(chunk, n) {
2494 var h = 0;
2495 outer: do {
2496 for (var i = 0, e = chunk.children.length; i < e; ++i) {
2497 var child = chunk.children[i], sz = child.chunkSize();
2498 if (n < sz) { chunk = child; continue outer; }
2499 n -= sz;
2500 h += child.height;
2501 }
2502 return h;
2503 } while (!chunk.lines);
2504 for (var i = 0; i < n; ++i) h += chunk.lines[i].height;
2505 return h;
2506 }
2507
1932 // The history object 'chunks' changes that are made close together
2508 // The history object 'chunks' changes that are made close together
1933 // and at almost the same time into bigger undoable units.
2509 // and at almost the same time into bigger undoable units.
1934 function History() {
2510 function History() {
1935 this.time = 0;
2511 this.time = 0;
1936 this.done = []; this.undone = [];
2512 this.done = []; this.undone = [];
1937 }
2513 }
1938 History.prototype = {
2514 History.prototype = {
1939 addChange: function(start, added, old) {
2515 addChange: function(start, added, old) {
1940 this.undone.length = 0;
2516 this.undone.length = 0;
1941 var time = +new Date, last = this.done[this.done.length - 1];
2517 var time = +new Date, last = this.done[this.done.length - 1];
1942 if (time - this.time > 400 || !last ||
2518 if (time - this.time > 400 || !last ||
1943 last.start > start + added || last.start + last.added < start - last.added + last.old.length)
2519 last.start > start + added || last.start + last.added < start - last.added + last.old.length)
1944 this.done.push({start: start, added: added, old: old});
2520 this.done.push({start: start, added: added, old: old});
1945 else {
2521 else {
1946 var oldoff = 0;
2522 var oldoff = 0;
1947 if (start < last.start) {
2523 if (start < last.start) {
1948 for (var i = last.start - start - 1; i >= 0; --i)
2524 for (var i = last.start - start - 1; i >= 0; --i)
1949 last.old.unshift(old[i]);
2525 last.old.unshift(old[i]);
1950 last.added += last.start - start;
2526 last.added += last.start - start;
1951 last.start = start;
2527 last.start = start;
1952 }
2528 }
1953 else if (last.start < start) {
2529 else if (last.start < start) {
1954 oldoff = start - last.start;
2530 oldoff = start - last.start;
1955 added += oldoff;
2531 added += oldoff;
1956 }
2532 }
1957 for (var i = last.added - oldoff, e = old.length; i < e; ++i)
2533 for (var i = last.added - oldoff, e = old.length; i < e; ++i)
1958 last.old.push(old[i]);
2534 last.old.push(old[i]);
1959 if (last.added < added) last.added = added;
2535 if (last.added < added) last.added = added;
1960 }
2536 }
1961 this.time = time;
2537 this.time = time;
1962 }
2538 }
1963 };
2539 };
1964
2540
1965 function stopMethod() {e_stop(this);}
2541 function stopMethod() {e_stop(this);}
1966 // Ensure an event has a stop method.
2542 // Ensure an event has a stop method.
1967 function addStop(event) {
2543 function addStop(event) {
1968 if (!event.stop) event.stop = stopMethod;
2544 if (!event.stop) event.stop = stopMethod;
1969 return event;
2545 return event;
1970 }
2546 }
1971
2547
1972 function e_preventDefault(e) {
2548 function e_preventDefault(e) {
1973 if (e.preventDefault) e.preventDefault();
2549 if (e.preventDefault) e.preventDefault();
1974 else e.returnValue = false;
2550 else e.returnValue = false;
1975 }
2551 }
1976 function e_stopPropagation(e) {
2552 function e_stopPropagation(e) {
1977 if (e.stopPropagation) e.stopPropagation();
2553 if (e.stopPropagation) e.stopPropagation();
1978 else e.cancelBubble = true;
2554 else e.cancelBubble = true;
1979 }
2555 }
1980 function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);}
2556 function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);}
2557 CodeMirror.e_stop = e_stop;
2558 CodeMirror.e_preventDefault = e_preventDefault;
2559 CodeMirror.e_stopPropagation = e_stopPropagation;
2560
1981 function e_target(e) {return e.target || e.srcElement;}
2561 function e_target(e) {return e.target || e.srcElement;}
1982 function e_button(e) {
2562 function e_button(e) {
1983 if (e.which) return e.which;
2563 if (e.which) return e.which;
1984 else if (e.button & 1) return 1;
2564 else if (e.button & 1) return 1;
1985 else if (e.button & 2) return 3;
2565 else if (e.button & 2) return 3;
1986 else if (e.button & 4) return 2;
2566 else if (e.button & 4) return 2;
1987 }
2567 }
1988
2568
1989 // Event handler registration. If disconnect is true, it'll return a
2569 // Event handler registration. If disconnect is true, it'll return a
1990 // function that unregisters the handler.
2570 // function that unregisters the handler.
1991 function connect(node, type, handler, disconnect) {
2571 function connect(node, type, handler, disconnect) {
1992 function wrapHandler(event) {handler(event || window.event);}
1993 if (typeof node.addEventListener == "function") {
2572 if (typeof node.addEventListener == "function") {
1994 node.addEventListener(type, wrapHandler, false);
2573 node.addEventListener(type, handler, false);
1995 if (disconnect) return function() {node.removeEventListener(type, wrapHandler, false);};
2574 if (disconnect) return function() {node.removeEventListener(type, handler, false);};
1996 }
2575 }
1997 else {
2576 else {
2577 var wrapHandler = function(event) {handler(event || window.event);};
1998 node.attachEvent("on" + type, wrapHandler);
2578 node.attachEvent("on" + type, wrapHandler);
1999 if (disconnect) return function() {node.detachEvent("on" + type, wrapHandler);};
2579 if (disconnect) return function() {node.detachEvent("on" + type, wrapHandler);};
2000 }
2580 }
2001 }
2581 }
2582 CodeMirror.connect = connect;
2002
2583
2003 function Delayed() {this.id = null;}
2584 function Delayed() {this.id = null;}
2004 Delayed.prototype = {set: function(ms, f) {clearTimeout(this.id); this.id = setTimeout(f, ms);}};
2585 Delayed.prototype = {set: function(ms, f) {clearTimeout(this.id); this.id = setTimeout(f, ms);}};
2005
2586
2006 // Some IE versions don't preserve whitespace when setting the
2007 // innerHTML of a PRE tag.
2008 var badInnerHTML = (function() {
2009 var pre = document.createElement("pre");
2010 pre.innerHTML = " "; return !pre.innerHTML;
2011 })();
2012
2013 // Detect drag-and-drop
2587 // Detect drag-and-drop
2014 var dragAndDrop = (function() {
2588 var dragAndDrop = function() {
2015 // IE8 has ondragstart and ondrop properties, but doesn't seem to
2589 // IE8 has ondragstart and ondrop properties, but doesn't seem to
2016 // actually support ondragstart the way it's supposed to work.
2590 // actually support ondragstart the way it's supposed to work.
2017 if (/MSIE [1-8]\b/.test(navigator.userAgent)) return false;
2591 if (/MSIE [1-8]\b/.test(navigator.userAgent)) return false;
2018 var div = document.createElement('div');
2592 var div = document.createElement('div');
2019 return "ondragstart" in div && "ondrop" in div;
2593 return "draggable" in div;
2020 })();
2594 }();
2021
2595
2022 var gecko = /gecko\/\d{7}/i.test(navigator.userAgent);
2596 var gecko = /gecko\/\d{7}/i.test(navigator.userAgent);
2023 var ie = /MSIE \d/.test(navigator.userAgent);
2597 var ie = /MSIE \d/.test(navigator.userAgent);
2024 var safari = /Apple Computer/.test(navigator.vendor);
2598 var webkit = /WebKit\//.test(navigator.userAgent);
2025
2599
2026 var lineSep = "\n";
2600 var lineSep = "\n";
2027 // Feature-detect whether newlines in textareas are converted to \r\n
2601 // Feature-detect whether newlines in textareas are converted to \r\n
2028 (function () {
2602 (function () {
2029 var te = document.createElement("textarea");
2603 var te = document.createElement("textarea");
2030 te.value = "foo\nbar";
2604 te.value = "foo\nbar";
2031 if (te.value.indexOf("\r") > -1) lineSep = "\r\n";
2605 if (te.value.indexOf("\r") > -1) lineSep = "\r\n";
2032 }());
2606 }());
2033
2607
2034 var tabSize = 8;
2035 var mac = /Mac/.test(navigator.platform);
2036 var movementKeys = {};
2037 for (var i = 35; i <= 40; ++i)
2038 movementKeys[i] = movementKeys["c" + i] = true;
2039
2040 // Counts the column offset in a string, taking tabs into account.
2608 // Counts the column offset in a string, taking tabs into account.
2041 // Used mostly to find indentation.
2609 // Used mostly to find indentation.
2042 function countColumn(string, end) {
2610 function countColumn(string, end, tabSize) {
2043 if (end == null) {
2611 if (end == null) {
2044 end = string.search(/[^\s\u00a0]/);
2612 end = string.search(/[^\s\u00a0]/);
2045 if (end == -1) end = string.length;
2613 if (end == -1) end = string.length;
2046 }
2614 }
2047 for (var i = 0, n = 0; i < end; ++i) {
2615 for (var i = 0, n = 0; i < end; ++i) {
2048 if (string.charAt(i) == "\t") n += tabSize - (n % tabSize);
2616 if (string.charAt(i) == "\t") n += tabSize - (n % tabSize);
2049 else ++n;
2617 else ++n;
2050 }
2618 }
2051 return n;
2619 return n;
2052 }
2620 }
2053
2621
2054 function computedStyle(elt) {
2622 function computedStyle(elt) {
2055 if (elt.currentStyle) return elt.currentStyle;
2623 if (elt.currentStyle) return elt.currentStyle;
2056 return window.getComputedStyle(elt, null);
2624 return window.getComputedStyle(elt, null);
2057 }
2625 }
2626
2058 // Find the position of an element by following the offsetParent chain.
2627 // Find the position of an element by following the offsetParent chain.
2059 // If screen==true, it returns screen (rather than page) coordinates.
2628 // If screen==true, it returns screen (rather than page) coordinates.
2060 function eltOffset(node, screen) {
2629 function eltOffset(node, screen) {
2061 var doc = node.ownerDocument.body;
2630 var bod = node.ownerDocument.body;
2062 var x = 0, y = 0, skipDoc = false;
2631 var x = 0, y = 0, skipBody = false;
2063 for (var n = node; n; n = n.offsetParent) {
2632 for (var n = node; n; n = n.offsetParent) {
2064 x += n.offsetLeft; y += n.offsetTop;
2633 var ol = n.offsetLeft, ot = n.offsetTop;
2634 // Firefox reports weird inverted offsets when the body has a border.
2635 if (n == bod) { x += Math.abs(ol); y += Math.abs(ot); }
2636 else { x += ol, y += ot; }
2065 if (screen && computedStyle(n).position == "fixed")
2637 if (screen && computedStyle(n).position == "fixed")
2066 skipDoc = true;
2638 skipBody = true;
2067 }
2639 }
2068 var e = screen && !skipDoc ? null : doc;
2640 var e = screen && !skipBody ? null : bod;
2069 for (var n = node.parentNode; n != e; n = n.parentNode)
2641 for (var n = node.parentNode; n != e; n = n.parentNode)
2070 if (n.scrollLeft != null) { x -= n.scrollLeft; y -= n.scrollTop;}
2642 if (n.scrollLeft != null) { x -= n.scrollLeft; y -= n.scrollTop;}
2071 return {left: x, top: y};
2643 return {left: x, top: y};
2072 }
2644 }
2645 // Use the faster and saner getBoundingClientRect method when possible.
2646 if (document.documentElement.getBoundingClientRect != null) eltOffset = function(node, screen) {
2647 // Take the parts of bounding client rect that we are interested in so we are able to edit if need be,
2648 // since the returned value cannot be changed externally (they are kept in sync as the element moves within the page)
2649 try { var box = node.getBoundingClientRect(); box = { top: box.top, left: box.left }; }
2650 catch(e) { box = {top: 0, left: 0}; }
2651 if (!screen) {
2652 // Get the toplevel scroll, working around browser differences.
2653 if (window.pageYOffset == null) {
2654 var t = document.documentElement || document.body.parentNode;
2655 if (t.scrollTop == null) t = document.body;
2656 box.top += t.scrollTop; box.left += t.scrollLeft;
2657 } else {
2658 box.top += window.pageYOffset; box.left += window.pageXOffset;
2659 }
2660 }
2661 return box;
2662 };
2663
2073 // Get a node's text content.
2664 // Get a node's text content.
2074 function eltText(node) {
2665 function eltText(node) {
2075 return node.textContent || node.innerText || node.nodeValue || "";
2666 return node.textContent || node.innerText || node.nodeValue || "";
2076 }
2667 }
2077
2668
2078 // Operations on {line, ch} objects.
2669 // Operations on {line, ch} objects.
2079 function posEq(a, b) {return a.line == b.line && a.ch == b.ch;}
2670 function posEq(a, b) {return a.line == b.line && a.ch == b.ch;}
2080 function posLess(a, b) {return a.line < b.line || (a.line == b.line && a.ch < b.ch);}
2671 function posLess(a, b) {return a.line < b.line || (a.line == b.line && a.ch < b.ch);}
2081 function copyPos(x) {return {line: x.line, ch: x.ch};}
2672 function copyPos(x) {return {line: x.line, ch: x.ch};}
2082
2673
2083 var escapeElement = document.createElement("div");
2674 var escapeElement = document.createElement("pre");
2084 function htmlEscape(str) {
2675 function htmlEscape(str) {
2085 escapeElement.innerText = escapeElement.textContent = str;
2676 escapeElement.textContent = str;
2086 return escapeElement.innerHTML;
2677 return escapeElement.innerHTML;
2087 }
2678 }
2679 // Recent (late 2011) Opera betas insert bogus newlines at the start
2680 // of the textContent, so we strip those.
2681 if (htmlEscape("a") == "\na")
2682 htmlEscape = function(str) {
2683 escapeElement.textContent = str;
2684 return escapeElement.innerHTML.slice(1);
2685 };
2686 // Some IEs don't preserve tabs through innerHTML
2687 else if (htmlEscape("\t") != "\t")
2688 htmlEscape = function(str) {
2689 escapeElement.innerHTML = "";
2690 escapeElement.appendChild(document.createTextNode(str));
2691 return escapeElement.innerHTML;
2692 };
2088 CodeMirror.htmlEscape = htmlEscape;
2693 CodeMirror.htmlEscape = htmlEscape;
2089
2694
2090 // Used to position the cursor after an undo/redo by finding the
2695 // Used to position the cursor after an undo/redo by finding the
2091 // last edited character.
2696 // last edited character.
2092 function editEnd(from, to) {
2697 function editEnd(from, to) {
2093 if (!to) return from ? from.length : 0;
2698 if (!to) return from ? from.length : 0;
2094 if (!from) return to.length;
2699 if (!from) return to.length;
2095 for (var i = from.length, j = to.length; i >= 0 && j >= 0; --i, --j)
2700 for (var i = from.length, j = to.length; i >= 0 && j >= 0; --i, --j)
2096 if (from.charAt(i) != to.charAt(j)) break;
2701 if (from.charAt(i) != to.charAt(j)) break;
2097 return j + 1;
2702 return j + 1;
2098 }
2703 }
2099
2704
2100 function indexOf(collection, elt) {
2705 function indexOf(collection, elt) {
2101 if (collection.indexOf) return collection.indexOf(elt);
2706 if (collection.indexOf) return collection.indexOf(elt);
2102 for (var i = 0, e = collection.length; i < e; ++i)
2707 for (var i = 0, e = collection.length; i < e; ++i)
2103 if (collection[i] == elt) return i;
2708 if (collection[i] == elt) return i;
2104 return -1;
2709 return -1;
2105 }
2710 }
2711 function isWordChar(ch) {
2712 return /\w/.test(ch) || ch.toUpperCase() != ch.toLowerCase();
2713 }
2106
2714
2107 // See if "".split is the broken IE version, if so, provide an
2715 // See if "".split is the broken IE version, if so, provide an
2108 // alternative way to split lines.
2716 // alternative way to split lines.
2109 var splitLines, selRange, setSelRange;
2717 var splitLines = "\n\nb".split(/\n/).length != 3 ? function(string) {
2110 if ("\n\nb".split(/\n/).length != 3)
2111 splitLines = function(string) {
2112 var pos = 0, nl, result = [];
2718 var pos = 0, nl, result = [];
2113 while ((nl = string.indexOf("\n", pos)) > -1) {
2719 while ((nl = string.indexOf("\n", pos)) > -1) {
2114 result.push(string.slice(pos, string.charAt(nl-1) == "\r" ? nl - 1 : nl));
2720 result.push(string.slice(pos, string.charAt(nl-1) == "\r" ? nl - 1 : nl));
2115 pos = nl + 1;
2721 pos = nl + 1;
2116 }
2722 }
2117 result.push(string.slice(pos));
2723 result.push(string.slice(pos));
2118 return result;
2724 return result;
2119 };
2725 } : function(string){return string.split(/\r?\n/);};
2120 else
2121 splitLines = function(string){return string.split(/\r?\n/);};
2122 CodeMirror.splitLines = splitLines;
2726 CodeMirror.splitLines = splitLines;
2123
2727
2124 // Sane model of finding and setting the selection in a textarea
2728 var hasSelection = window.getSelection ? function(te) {
2125 if (window.getSelection) {
2729 try { return te.selectionStart != te.selectionEnd; }
2126 selRange = function(te) {
2730 catch(e) { return false; }
2127 try {return {start: te.selectionStart, end: te.selectionEnd};}
2731 } : function(te) {
2128 catch(e) {return null;}
2129 };
2130 if (safari)
2131 // On Safari, selection set with setSelectionRange are in a sort
2132 // of limbo wrt their anchor. If you press shift-left in them,
2133 // the anchor is put at the end, and the selection expanded to
2134 // the left. If you press shift-right, the anchor ends up at the
2135 // front. This is not what CodeMirror wants, so it does a
2136 // spurious modify() call to get out of limbo.
2137 setSelRange = function(te, start, end) {
2138 if (start == end)
2139 te.setSelectionRange(start, end);
2140 else {
2141 te.setSelectionRange(start, end - 1);
2142 window.getSelection().modify("extend", "forward", "character");
2143 }
2144 };
2145 else
2146 setSelRange = function(te, start, end) {
2147 try {te.setSelectionRange(start, end);}
2148 catch(e) {} // Fails on Firefox when textarea isn't part of the document
2149 };
2150 }
2151 // IE model. Don't ask.
2152 else {
2153 selRange = function(te) {
2154 try {var range = te.ownerDocument.selection.createRange();}
2732 try {var range = te.ownerDocument.selection.createRange();}
2155 catch(e) {return null;}
2733 catch(e) {}
2156 if (!range || range.parentElement() != te) return null;
2734 if (!range || range.parentElement() != te) return false;
2157 var val = te.value, len = val.length, localRange = te.createTextRange();
2735 return range.compareEndPoints("StartToEnd", range) != 0;
2158 localRange.moveToBookmark(range.getBookmark());
2159 var endRange = te.createTextRange();
2160 endRange.collapse(false);
2161
2162 if (localRange.compareEndPoints("StartToEnd", endRange) > -1)
2163 return {start: len, end: len};
2164
2165 var start = -localRange.moveStart("character", -len);
2166 for (var i = val.indexOf("\r"); i > -1 && i < start; i = val.indexOf("\r", i+1), start++) {}
2167
2168 if (localRange.compareEndPoints("EndToEnd", endRange) > -1)
2169 return {start: start, end: len};
2170
2171 var end = -localRange.moveEnd("character", -len);
2172 for (var i = val.indexOf("\r"); i > -1 && i < end; i = val.indexOf("\r", i+1), end++) {}
2173 return {start: start, end: end};
2174 };
2175 setSelRange = function(te, start, end) {
2176 var range = te.createTextRange();
2177 range.collapse(true);
2178 var endrange = range.duplicate();
2179 var newlines = 0, txt = te.value;
2180 for (var pos = txt.indexOf("\n"); pos > -1 && pos < start; pos = txt.indexOf("\n", pos + 1))
2181 ++newlines;
2182 range.move("character", start - newlines);
2183 for (; pos > -1 && pos < end; pos = txt.indexOf("\n", pos + 1))
2184 ++newlines;
2185 endrange.move("character", end - newlines);
2186 range.setEndPoint("EndToEnd", endrange);
2187 range.select();
2188 };
2736 };
2189 }
2190
2737
2191 CodeMirror.defineMode("null", function() {
2738 CodeMirror.defineMode("null", function() {
2192 return {token: function(stream) {stream.skipToEnd();}};
2739 return {token: function(stream) {stream.skipToEnd();}};
2193 });
2740 });
2194 CodeMirror.defineMIME("text/plain", "null");
2741 CodeMirror.defineMIME("text/plain", "null");
2195
2742
2743 var keyNames = {3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
2744 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
2745 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
2746 46: "Delete", 59: ";", 91: "Mod", 92: "Mod", 93: "Mod", 186: ";", 187: "=", 188: ",",
2747 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\", 221: "]", 222: "'", 63276: "PageUp",
2748 63277: "PageDown", 63275: "End", 63273: "Home", 63234: "Left", 63232: "Up", 63235: "Right",
2749 63233: "Down", 63302: "Insert", 63272: "Delete"};
2750 CodeMirror.keyNames = keyNames;
2751 (function() {
2752 // Number keys
2753 for (var i = 0; i < 10; i++) keyNames[i + 48] = String(i);
2754 // Alphabetic keys
2755 for (var i = 65; i <= 90; i++) keyNames[i] = String.fromCharCode(i);
2756 // Function keys
2757 for (var i = 1; i <= 12; i++) keyNames[i + 111] = keyNames[i + 63235] = "F" + i;
2758 })();
2759
2196 return CodeMirror;
2760 return CodeMirror;
2197 })();
2761 })();
1 NO CONTENT: file renamed from IPython/frontend/html/notebook/static/codemirror/lib/overlay.js to IPython/frontend/html/notebook/static/codemirror/lib/util/overlay.js, modified file
NO CONTENT: file renamed from IPython/frontend/html/notebook/static/codemirror/lib/overlay.js to IPython/frontend/html/notebook/static/codemirror/lib/util/overlay.js, modified file
@@ -1,27 +1,27 b''
1 CodeMirror.runMode = function(string, modespec, callback) {
1 CodeMirror.runMode = function(string, modespec, callback) {
2 var mode = CodeMirror.getMode({indentUnit: 2}, modespec);
2 var mode = CodeMirror.getMode({indentUnit: 2}, modespec);
3 var isNode = callback.nodeType == 1;
3 var isNode = callback.nodeType == 1;
4 if (isNode) {
4 if (isNode) {
5 var node = callback, accum = [];
5 var node = callback, accum = [];
6 callback = function(string, style) {
6 callback = function(string, style) {
7 if (string == "\n")
7 if (string == "\n")
8 accum.push("<br>");
8 accum.push("<br>");
9 else if (style)
9 else if (style)
10 accum.push("<span class=\"cm-" + CodeMirror.htmlEscape(style) + "\">" + CodeMirror.htmlEscape(string) + "</span>");
10 accum.push("<span class=\"cm-" + CodeMirror.htmlEscape(style) + "\">" + CodeMirror.htmlEscape(string) + "</span>");
11 else
11 else
12 accum.push(CodeMirror.htmlEscape(string));
12 accum.push(CodeMirror.htmlEscape(string));
13 }
13 }
14 }
14 }
15 var lines = CodeMirror.splitLines(string), state = CodeMirror.startState(mode);
15 var lines = CodeMirror.splitLines(string), state = CodeMirror.startState(mode);
16 for (var i = 0, e = lines.length; i < e; ++i) {
16 for (var i = 0, e = lines.length; i < e; ++i) {
17 if (i) callback("\n");
17 if (i) callback("\n");
18 var stream = new CodeMirror.StringStream(lines[i]);
18 var stream = new CodeMirror.StringStream(lines[i]);
19 while (!stream.eol()) {
19 while (!stream.eol()) {
20 var style = mode.token(stream, state);
20 var style = mode.token(stream, state);
21 callback(stream.current(), style);
21 callback(stream.current(), style, i, stream.start);
22 stream.start = stream.pos;
22 stream.start = stream.pos;
23 }
23 }
24 }
24 }
25 if (isNode)
25 if (isNode)
26 node.innerHTML = accum.join("");
26 node.innerHTML = accum.join("");
27 };
27 };
@@ -1,56 +1,55 b''
1 <!doctype html>
1 <!doctype html>
2 <html>
2 <html>
3 <head>
3 <head>
4 <title>CodeMirror 2: CSS mode</title>
4 <title>CodeMirror: CSS mode</title>
5 <link rel="stylesheet" href="../../lib/codemirror.css">
5 <link rel="stylesheet" href="../../lib/codemirror.css">
6 <script src="../../lib/codemirror.js"></script>
6 <script src="../../lib/codemirror.js"></script>
7 <script src="css.js"></script>
7 <script src="css.js"></script>
8 <link rel="stylesheet" href="../../theme/default.css">
9 <style>.CodeMirror {background: #f8f8f8;}</style>
8 <style>.CodeMirror {background: #f8f8f8;}</style>
10 <link rel="stylesheet" href="../../css/docs.css">
9 <link rel="stylesheet" href="../../doc/docs.css">
11 </head>
10 </head>
12 <body>
11 <body>
13 <h1>CodeMirror 2: CSS mode</h1>
12 <h1>CodeMirror: CSS mode</h1>
14 <form><textarea id="code" name="code">
13 <form><textarea id="code" name="code">
15 /* Some example CSS */
14 /* Some example CSS */
16
15
17 @import url("something.css");
16 @import url("something.css");
18
17
19 body {
18 body {
20 margin: 0;
19 margin: 0;
21 padding: 3em 6em;
20 padding: 3em 6em;
22 font-family: tahoma, arial, sans-serif;
21 font-family: tahoma, arial, sans-serif;
23 color: #000;
22 color: #000;
24 }
23 }
25
24
26 #navigation a {
25 #navigation a {
27 font-weight: bold;
26 font-weight: bold;
28 text-decoration: none !important;
27 text-decoration: none !important;
29 }
28 }
30
29
31 h1 {
30 h1 {
32 font-size: 2.5em;
31 font-size: 2.5em;
33 }
32 }
34
33
35 h2 {
34 h2 {
36 font-size: 1.7em;
35 font-size: 1.7em;
37 }
36 }
38
37
39 h1:before, h2:before {
38 h1:before, h2:before {
40 content: "::";
39 content: "::";
41 }
40 }
42
41
43 code {
42 code {
44 font-family: courier, monospace;
43 font-family: courier, monospace;
45 font-size: 80%;
44 font-size: 80%;
46 color: #418A8A;
45 color: #418A8A;
47 }
46 }
48 </textarea></form>
47 </textarea></form>
49 <script>
48 <script>
50 var editor = CodeMirror.fromTextArea(document.getElementById("code"), {});
49 var editor = CodeMirror.fromTextArea(document.getElementById("code"), {});
51 </script>
50 </script>
52
51
53 <p><strong>MIME types defined:</strong> <code>text/css</code>.</p>
52 <p><strong>MIME types defined:</strong> <code>text/css</code>.</p>
54
53
55 </body>
54 </body>
56 </html>
55 </html>
@@ -1,79 +1,83 b''
1 CodeMirror.defineMode("htmlmixed", function(config, parserConfig) {
1 CodeMirror.defineMode("htmlmixed", function(config, parserConfig) {
2 var htmlMode = CodeMirror.getMode(config, {name: "xml", htmlMode: true});
2 var htmlMode = CodeMirror.getMode(config, {name: "xml", htmlMode: true});
3 var jsMode = CodeMirror.getMode(config, "javascript");
3 var jsMode = CodeMirror.getMode(config, "javascript");
4 var cssMode = CodeMirror.getMode(config, "css");
4 var cssMode = CodeMirror.getMode(config, "css");
5
5
6 function html(stream, state) {
6 function html(stream, state) {
7 var style = htmlMode.token(stream, state.htmlState);
7 var style = htmlMode.token(stream, state.htmlState);
8 if (style == "tag" && stream.current() == ">" && state.htmlState.context) {
8 if (style == "tag" && stream.current() == ">" && state.htmlState.context) {
9 if (/^script$/i.test(state.htmlState.context.tagName)) {
9 if (/^script$/i.test(state.htmlState.context.tagName)) {
10 state.token = javascript;
10 state.token = javascript;
11 state.localState = jsMode.startState(htmlMode.indent(state.htmlState, ""));
11 state.localState = jsMode.startState(htmlMode.indent(state.htmlState, ""));
12 state.mode = "javascript";
12 state.mode = "javascript";
13 }
13 }
14 else if (/^style$/i.test(state.htmlState.context.tagName)) {
14 else if (/^style$/i.test(state.htmlState.context.tagName)) {
15 state.token = css;
15 state.token = css;
16 state.localState = cssMode.startState(htmlMode.indent(state.htmlState, ""));
16 state.localState = cssMode.startState(htmlMode.indent(state.htmlState, ""));
17 state.mode = "css";
17 state.mode = "css";
18 }
18 }
19 }
19 }
20 return style;
20 return style;
21 }
21 }
22 function maybeBackup(stream, pat, style) {
22 function maybeBackup(stream, pat, style) {
23 var cur = stream.current();
23 var cur = stream.current();
24 var close = cur.search(pat);
24 var close = cur.search(pat);
25 if (close > -1) stream.backUp(cur.length - close);
25 if (close > -1) stream.backUp(cur.length - close);
26 return style;
26 return style;
27 }
27 }
28 function javascript(stream, state) {
28 function javascript(stream, state) {
29 if (stream.match(/^<\/\s*script\s*>/i, false)) {
29 if (stream.match(/^<\/\s*script\s*>/i, false)) {
30 state.token = html;
30 state.token = html;
31 state.curState = null;
31 state.curState = null;
32 state.mode = "html";
32 state.mode = "html";
33 return html(stream, state);
33 return html(stream, state);
34 }
34 }
35 return maybeBackup(stream, /<\/\s*script\s*>/,
35 return maybeBackup(stream, /<\/\s*script\s*>/,
36 jsMode.token(stream, state.localState));
36 jsMode.token(stream, state.localState));
37 }
37 }
38 function css(stream, state) {
38 function css(stream, state) {
39 if (stream.match(/^<\/\s*style\s*>/i, false)) {
39 if (stream.match(/^<\/\s*style\s*>/i, false)) {
40 state.token = html;
40 state.token = html;
41 state.localState = null;
41 state.localState = null;
42 state.mode = "html";
42 state.mode = "html";
43 return html(stream, state);
43 return html(stream, state);
44 }
44 }
45 return maybeBackup(stream, /<\/\s*style\s*>/,
45 return maybeBackup(stream, /<\/\s*style\s*>/,
46 cssMode.token(stream, state.localState));
46 cssMode.token(stream, state.localState));
47 }
47 }
48
48
49 return {
49 return {
50 startState: function() {
50 startState: function() {
51 var state = htmlMode.startState();
51 var state = htmlMode.startState();
52 return {token: html, localState: null, mode: "html", htmlState: state};
52 return {token: html, localState: null, mode: "html", htmlState: state};
53 },
53 },
54
54
55 copyState: function(state) {
55 copyState: function(state) {
56 if (state.localState)
56 if (state.localState)
57 var local = CodeMirror.copyState(state.token == css ? cssMode : jsMode, state.localState);
57 var local = CodeMirror.copyState(state.token == css ? cssMode : jsMode, state.localState);
58 return {token: state.token, localState: local, mode: state.mode,
58 return {token: state.token, localState: local, mode: state.mode,
59 htmlState: CodeMirror.copyState(htmlMode, state.htmlState)};
59 htmlState: CodeMirror.copyState(htmlMode, state.htmlState)};
60 },
60 },
61
61
62 token: function(stream, state) {
62 token: function(stream, state) {
63 return state.token(stream, state);
63 return state.token(stream, state);
64 },
64 },
65
65
66 indent: function(state, textAfter) {
66 indent: function(state, textAfter) {
67 if (state.token == html || /^\s*<\//.test(textAfter))
67 if (state.token == html || /^\s*<\//.test(textAfter))
68 return htmlMode.indent(state.htmlState, textAfter);
68 return htmlMode.indent(state.htmlState, textAfter);
69 else if (state.token == javascript)
69 else if (state.token == javascript)
70 return jsMode.indent(state.localState, textAfter);
70 return jsMode.indent(state.localState, textAfter);
71 else
71 else
72 return cssMode.indent(state.localState, textAfter);
72 return cssMode.indent(state.localState, textAfter);
73 },
73 },
74
74
75 compareStates: function(a, b) {
76 return htmlMode.compareStates(a.htmlState, b.htmlState);
77 },
78
75 electricChars: "/{}:"
79 electricChars: "/{}:"
76 }
80 }
77 });
81 });
78
82
79 CodeMirror.defineMIME("text/html", "htmlmixed");
83 CodeMirror.defineMIME("text/html", "htmlmixed");
@@ -1,52 +1,51 b''
1 <!doctype html>
1 <!doctype html>
2 <html>
2 <html>
3 <head>
3 <head>
4 <title>CodeMirror 2: HTML mixed mode</title>
4 <title>CodeMirror: HTML mixed mode</title>
5 <link rel="stylesheet" href="../../lib/codemirror.css">
5 <link rel="stylesheet" href="../../lib/codemirror.css">
6 <script src="../../lib/codemirror.js"></script>
6 <script src="../../lib/codemirror.js"></script>
7 <script src="../xml/xml.js"></script>
7 <script src="../xml/xml.js"></script>
8 <script src="../javascript/javascript.js"></script>
8 <script src="../javascript/javascript.js"></script>
9 <script src="../css/css.js"></script>
9 <script src="../css/css.js"></script>
10 <link rel="stylesheet" href="../../theme/default.css">
11 <script src="htmlmixed.js"></script>
10 <script src="htmlmixed.js"></script>
12 <link rel="stylesheet" href="../../css/docs.css">
11 <link rel="stylesheet" href="../../doc/docs.css">
13 <style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
12 <style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
14 </head>
13 </head>
15 <body>
14 <body>
16 <h1>CodeMirror 2: HTML mixed mode</h1>
15 <h1>CodeMirror: HTML mixed mode</h1>
17 <form><textarea id="code" name="code">
16 <form><textarea id="code" name="code">
18 <html style="color: green">
17 <html style="color: green">
19 <!-- this is a comment -->
18 <!-- this is a comment -->
20 <head>
19 <head>
21 <title>Mixed HTML Example</title>
20 <title>Mixed HTML Example</title>
22 <style type="text/css">
21 <style type="text/css">
23 h1 {font-family: comic sans; color: #f0f;}
22 h1 {font-family: comic sans; color: #f0f;}
24 div {background: yellow !important;}
23 div {background: yellow !important;}
25 body {
24 body {
26 max-width: 50em;
25 max-width: 50em;
27 margin: 1em 2em 1em 5em;
26 margin: 1em 2em 1em 5em;
28 }
27 }
29 </style>
28 </style>
30 </head>
29 </head>
31 <body>
30 <body>
32 <h1>Mixed HTML Example</h1>
31 <h1>Mixed HTML Example</h1>
33 <script>
32 <script>
34 function jsFunc(arg1, arg2) {
33 function jsFunc(arg1, arg2) {
35 if (arg1 && arg2) document.body.innerHTML = "achoo";
34 if (arg1 && arg2) document.body.innerHTML = "achoo";
36 }
35 }
37 </script>
36 </script>
38 </body>
37 </body>
39 </html>
38 </html>
40 </textarea></form>
39 </textarea></form>
41 <script>
40 <script>
42 var editor = CodeMirror.fromTextArea(document.getElementById("code"), {mode: "text/html", tabMode: "indent"});
41 var editor = CodeMirror.fromTextArea(document.getElementById("code"), {mode: "text/html", tabMode: "indent"});
43 </script>
42 </script>
44
43
45 <p>The HTML mixed mode depends on the XML, JavaScript, and CSS modes.</p>
44 <p>The HTML mixed mode depends on the XML, JavaScript, and CSS modes.</p>
46
45
47 <p><strong>MIME types defined:</strong> <code>text/html</code>
46 <p><strong>MIME types defined:</strong> <code>text/html</code>
48 (redefined, only takes effect if you load this parser after the
47 (redefined, only takes effect if you load this parser after the
49 XML parser).</p>
48 XML parser).</p>
50
49
51 </body>
50 </body>
52 </html>
51 </html>
@@ -1,78 +1,77 b''
1 <!doctype html>
1 <!doctype html>
2 <html>
2 <html>
3 <head>
3 <head>
4 <title>CodeMirror 2: JavaScript mode</title>
4 <title>CodeMirror: JavaScript mode</title>
5 <link rel="stylesheet" href="../../lib/codemirror.css">
5 <link rel="stylesheet" href="../../lib/codemirror.css">
6 <script src="../../lib/codemirror.js"></script>
6 <script src="../../lib/codemirror.js"></script>
7 <script src="javascript.js"></script>
7 <script src="javascript.js"></script>
8 <link rel="stylesheet" href="../../theme/default.css">
8 <link rel="stylesheet" href="../../doc/docs.css">
9 <link rel="stylesheet" href="../../css/docs.css">
10 <style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
9 <style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
11 </head>
10 </head>
12 <body>
11 <body>
13 <h1>CodeMirror 2: JavaScript mode</h1>
12 <h1>CodeMirror: JavaScript mode</h1>
14
13
15 <div><textarea id="code" name="code">
14 <div><textarea id="code" name="code">
16 // Demo code (the actual new parser character stream implementation)
15 // Demo code (the actual new parser character stream implementation)
17
16
18 function StringStream(string) {
17 function StringStream(string) {
19 this.pos = 0;
18 this.pos = 0;
20 this.string = string;
19 this.string = string;
21 }
20 }
22
21
23 StringStream.prototype = {
22 StringStream.prototype = {
24 done: function() {return this.pos >= this.string.length;},
23 done: function() {return this.pos >= this.string.length;},
25 peek: function() {return this.string.charAt(this.pos);},
24 peek: function() {return this.string.charAt(this.pos);},
26 next: function() {
25 next: function() {
27 if (this.pos &lt; this.string.length)
26 if (this.pos &lt; this.string.length)
28 return this.string.charAt(this.pos++);
27 return this.string.charAt(this.pos++);
29 },
28 },
30 eat: function(match) {
29 eat: function(match) {
31 var ch = this.string.charAt(this.pos);
30 var ch = this.string.charAt(this.pos);
32 if (typeof match == "string") var ok = ch == match;
31 if (typeof match == "string") var ok = ch == match;
33 else var ok = ch &amp;&amp; match.test ? match.test(ch) : match(ch);
32 else var ok = ch &amp;&amp; match.test ? match.test(ch) : match(ch);
34 if (ok) {this.pos++; return ch;}
33 if (ok) {this.pos++; return ch;}
35 },
34 },
36 eatWhile: function(match) {
35 eatWhile: function(match) {
37 var start = this.pos;
36 var start = this.pos;
38 while (this.eat(match));
37 while (this.eat(match));
39 if (this.pos > start) return this.string.slice(start, this.pos);
38 if (this.pos > start) return this.string.slice(start, this.pos);
40 },
39 },
41 backUp: function(n) {this.pos -= n;},
40 backUp: function(n) {this.pos -= n;},
42 column: function() {return this.pos;},
41 column: function() {return this.pos;},
43 eatSpace: function() {
42 eatSpace: function() {
44 var start = this.pos;
43 var start = this.pos;
45 while (/\s/.test(this.string.charAt(this.pos))) this.pos++;
44 while (/\s/.test(this.string.charAt(this.pos))) this.pos++;
46 return this.pos - start;
45 return this.pos - start;
47 },
46 },
48 match: function(pattern, consume, caseInsensitive) {
47 match: function(pattern, consume, caseInsensitive) {
49 if (typeof pattern == "string") {
48 if (typeof pattern == "string") {
50 function cased(str) {return caseInsensitive ? str.toLowerCase() : str;}
49 function cased(str) {return caseInsensitive ? str.toLowerCase() : str;}
51 if (cased(this.string).indexOf(cased(pattern), this.pos) == this.pos) {
50 if (cased(this.string).indexOf(cased(pattern), this.pos) == this.pos) {
52 if (consume !== false) this.pos += str.length;
51 if (consume !== false) this.pos += str.length;
53 return true;
52 return true;
54 }
53 }
55 }
54 }
56 else {
55 else {
57 var match = this.string.slice(this.pos).match(pattern);
56 var match = this.string.slice(this.pos).match(pattern);
58 if (match &amp;&amp; consume !== false) this.pos += match[0].length;
57 if (match &amp;&amp; consume !== false) this.pos += match[0].length;
59 return match;
58 return match;
60 }
59 }
61 }
60 }
62 };
61 };
63 </textarea></div>
62 </textarea></div>
64
63
65 <script>
64 <script>
66 var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
65 var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
67 lineNumbers: true,
66 lineNumbers: true,
68 matchBrackets: true
67 matchBrackets: true
69 });
68 });
70 </script>
69 </script>
71
70
72 <p>JavaScript mode supports a single configuration
71 <p>JavaScript mode supports a single configuration
73 option, <code>json</code>, which will set the mode to expect JSON
72 option, <code>json</code>, which will set the mode to expect JSON
74 data rather than a JavaScript program.</p>
73 data rather than a JavaScript program.</p>
75
74
76 <p><strong>MIME types defined:</strong> <code>text/javascript</code>, <code>application/json</code>.</p>
75 <p><strong>MIME types defined:</strong> <code>text/javascript</code>, <code>application/json</code>.</p>
77 </body>
76 </body>
78 </html>
77 </html>
@@ -1,352 +1,360 b''
1 CodeMirror.defineMode("javascript", function(config, parserConfig) {
1 CodeMirror.defineMode("javascript", function(config, parserConfig) {
2 var indentUnit = config.indentUnit;
2 var indentUnit = config.indentUnit;
3 var jsonMode = parserConfig.json;
3 var jsonMode = parserConfig.json;
4
4
5 // Tokenizer
5 // Tokenizer
6
6
7 var keywords = function(){
7 var keywords = function(){
8 function kw(type) {return {type: type, style: "keyword"};}
8 function kw(type) {return {type: type, style: "keyword"};}
9 var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c");
9 var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c");
10 var operator = kw("operator"), atom = {type: "atom", style: "atom"};
10 var operator = kw("operator"), atom = {type: "atom", style: "atom"};
11 return {
11 return {
12 "if": A, "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B,
12 "if": A, "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B,
13 "return": C, "break": C, "continue": C, "new": C, "delete": C, "throw": C,
13 "return": C, "break": C, "continue": C, "new": C, "delete": C, "throw": C,
14 "var": kw("var"), "function": kw("function"), "catch": kw("catch"),
14 "var": kw("var"), "const": kw("var"), "let": kw("var"),
15 "function": kw("function"), "catch": kw("catch"),
15 "for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"),
16 "for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"),
16 "in": operator, "typeof": operator, "instanceof": operator,
17 "in": operator, "typeof": operator, "instanceof": operator,
17 "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom
18 "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom
18 };
19 };
19 }();
20 }();
20
21
21 var isOperatorChar = /[+\-*&%=<>!?|]/;
22 var isOperatorChar = /[+\-*&%=<>!?|]/;
22
23
23 function chain(stream, state, f) {
24 function chain(stream, state, f) {
24 state.tokenize = f;
25 state.tokenize = f;
25 return f(stream, state);
26 return f(stream, state);
26 }
27 }
27
28
28 function nextUntilUnescaped(stream, end) {
29 function nextUntilUnescaped(stream, end) {
29 var escaped = false, next;
30 var escaped = false, next;
30 while ((next = stream.next()) != null) {
31 while ((next = stream.next()) != null) {
31 if (next == end && !escaped)
32 if (next == end && !escaped)
32 return false;
33 return false;
33 escaped = !escaped && next == "\\";
34 escaped = !escaped && next == "\\";
34 }
35 }
35 return escaped;
36 return escaped;
36 }
37 }
37
38
38 // Used as scratch variables to communicate multiple values without
39 // Used as scratch variables to communicate multiple values without
39 // consing up tons of objects.
40 // consing up tons of objects.
40 var type, content;
41 var type, content;
41 function ret(tp, style, cont) {
42 function ret(tp, style, cont) {
42 type = tp; content = cont;
43 type = tp; content = cont;
43 return style;
44 return style;
44 }
45 }
45
46
46 function jsTokenBase(stream, state) {
47 function jsTokenBase(stream, state) {
47 var ch = stream.next();
48 var ch = stream.next();
48 if (ch == '"' || ch == "'")
49 if (ch == '"' || ch == "'")
49 return chain(stream, state, jsTokenString(ch));
50 return chain(stream, state, jsTokenString(ch));
50 else if (/[\[\]{}\(\),;\:\.]/.test(ch))
51 else if (/[\[\]{}\(\),;\:\.]/.test(ch))
51 return ret(ch);
52 return ret(ch);
52 else if (ch == "0" && stream.eat(/x/i)) {
53 else if (ch == "0" && stream.eat(/x/i)) {
53 stream.eatWhile(/[\da-f]/i);
54 stream.eatWhile(/[\da-f]/i);
54 return ret("number", "number");
55 return ret("number", "number");
55 }
56 }
56 else if (/\d/.test(ch)) {
57 else if (/\d/.test(ch)) {
57 stream.match(/^\d*(?:\.\d*)?(?:e[+\-]?\d+)?/);
58 stream.match(/^\d*(?:\.\d*)?(?:[eE][+\-]?\d+)?/);
58 return ret("number", "number");
59 return ret("number", "number");
59 }
60 }
60 else if (ch == "/") {
61 else if (ch == "/") {
61 if (stream.eat("*")) {
62 if (stream.eat("*")) {
62 return chain(stream, state, jsTokenComment);
63 return chain(stream, state, jsTokenComment);
63 }
64 }
64 else if (stream.eat("/")) {
65 else if (stream.eat("/")) {
65 stream.skipToEnd();
66 stream.skipToEnd();
66 return ret("comment", "comment");
67 return ret("comment", "comment");
67 }
68 }
68 else if (state.reAllowed) {
69 else if (state.reAllowed) {
69 nextUntilUnescaped(stream, "/");
70 nextUntilUnescaped(stream, "/");
70 stream.eatWhile(/[gimy]/); // 'y' is "sticky" option in Mozilla
71 stream.eatWhile(/[gimy]/); // 'y' is "sticky" option in Mozilla
71 return ret("regexp", "string");
72 return ret("regexp", "string");
72 }
73 }
73 else {
74 else {
74 stream.eatWhile(isOperatorChar);
75 stream.eatWhile(isOperatorChar);
75 return ret("operator", null, stream.current());
76 return ret("operator", null, stream.current());
76 }
77 }
77 }
78 }
78 else if (ch == "#") {
79 else if (ch == "#") {
79 stream.skipToEnd();
80 stream.skipToEnd();
80 return ret("error", "error");
81 return ret("error", "error");
81 }
82 }
82 else if (isOperatorChar.test(ch)) {
83 else if (isOperatorChar.test(ch)) {
83 stream.eatWhile(isOperatorChar);
84 stream.eatWhile(isOperatorChar);
84 return ret("operator", null, stream.current());
85 return ret("operator", null, stream.current());
85 }
86 }
86 else {
87 else {
87 stream.eatWhile(/[\w\$_]/);
88 stream.eatWhile(/[\w\$_]/);
88 var word = stream.current(), known = keywords.propertyIsEnumerable(word) && keywords[word];
89 var word = stream.current(), known = keywords.propertyIsEnumerable(word) && keywords[word];
89 return known ? ret(known.type, known.style, word) :
90 return (known && state.kwAllowed) ? ret(known.type, known.style, word) :
90 ret("variable", "variable", word);
91 ret("variable", "variable", word);
91 }
92 }
92 }
93 }
93
94
94 function jsTokenString(quote) {
95 function jsTokenString(quote) {
95 return function(stream, state) {
96 return function(stream, state) {
96 if (!nextUntilUnescaped(stream, quote))
97 if (!nextUntilUnescaped(stream, quote))
97 state.tokenize = jsTokenBase;
98 state.tokenize = jsTokenBase;
98 return ret("string", "string");
99 return ret("string", "string");
99 };
100 };
100 }
101 }
101
102
102 function jsTokenComment(stream, state) {
103 function jsTokenComment(stream, state) {
103 var maybeEnd = false, ch;
104 var maybeEnd = false, ch;
104 while (ch = stream.next()) {
105 while (ch = stream.next()) {
105 if (ch == "/" && maybeEnd) {
106 if (ch == "/" && maybeEnd) {
106 state.tokenize = jsTokenBase;
107 state.tokenize = jsTokenBase;
107 break;
108 break;
108 }
109 }
109 maybeEnd = (ch == "*");
110 maybeEnd = (ch == "*");
110 }
111 }
111 return ret("comment", "comment");
112 return ret("comment", "comment");
112 }
113 }
113
114
114 // Parser
115 // Parser
115
116
116 var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true};
117 var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true};
117
118
118 function JSLexical(indented, column, type, align, prev, info) {
119 function JSLexical(indented, column, type, align, prev, info) {
119 this.indented = indented;
120 this.indented = indented;
120 this.column = column;
121 this.column = column;
121 this.type = type;
122 this.type = type;
122 this.prev = prev;
123 this.prev = prev;
123 this.info = info;
124 this.info = info;
124 if (align != null) this.align = align;
125 if (align != null) this.align = align;
125 }
126 }
126
127
127 function inScope(state, varname) {
128 function inScope(state, varname) {
128 for (var v = state.localVars; v; v = v.next)
129 for (var v = state.localVars; v; v = v.next)
129 if (v.name == varname) return true;
130 if (v.name == varname) return true;
130 }
131 }
131
132
132 function parseJS(state, style, type, content, stream) {
133 function parseJS(state, style, type, content, stream) {
133 var cc = state.cc;
134 var cc = state.cc;
134 // Communicate our context to the combinators.
135 // Communicate our context to the combinators.
135 // (Less wasteful than consing up a hundred closures on every call.)
136 // (Less wasteful than consing up a hundred closures on every call.)
136 cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc;
137 cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc;
137
138
138 if (!state.lexical.hasOwnProperty("align"))
139 if (!state.lexical.hasOwnProperty("align"))
139 state.lexical.align = true;
140 state.lexical.align = true;
140
141
141 while(true) {
142 while(true) {
142 var combinator = cc.length ? cc.pop() : jsonMode ? expression : statement;
143 var combinator = cc.length ? cc.pop() : jsonMode ? expression : statement;
143 if (combinator(type, content)) {
144 if (combinator(type, content)) {
144 while(cc.length && cc[cc.length - 1].lex)
145 while(cc.length && cc[cc.length - 1].lex)
145 cc.pop()();
146 cc.pop()();
146 if (cx.marked) return cx.marked;
147 if (cx.marked) return cx.marked;
147 if (type == "variable" && inScope(state, content)) return "variable-2";
148 if (type == "variable" && inScope(state, content)) return "variable-2";
148 return style;
149 return style;
149 }
150 }
150 }
151 }
151 }
152 }
152
153
153 // Combinator utils
154 // Combinator utils
154
155
155 var cx = {state: null, column: null, marked: null, cc: null};
156 var cx = {state: null, column: null, marked: null, cc: null};
156 function pass() {
157 function pass() {
157 for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]);
158 for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]);
158 }
159 }
159 function cont() {
160 function cont() {
160 pass.apply(null, arguments);
161 pass.apply(null, arguments);
161 return true;
162 return true;
162 }
163 }
163 function register(varname) {
164 function register(varname) {
164 var state = cx.state;
165 var state = cx.state;
165 if (state.context) {
166 if (state.context) {
166 cx.marked = "def";
167 cx.marked = "def";
167 for (var v = state.localVars; v; v = v.next)
168 for (var v = state.localVars; v; v = v.next)
168 if (v.name == varname) return;
169 if (v.name == varname) return;
169 state.localVars = {name: varname, next: state.localVars};
170 state.localVars = {name: varname, next: state.localVars};
170 }
171 }
171 }
172 }
172
173
173 // Combinators
174 // Combinators
174
175
175 var defaultVars = {name: "this", next: {name: "arguments"}};
176 var defaultVars = {name: "this", next: {name: "arguments"}};
176 function pushcontext() {
177 function pushcontext() {
177 if (!cx.state.context) cx.state.localVars = defaultVars;
178 if (!cx.state.context) cx.state.localVars = defaultVars;
178 cx.state.context = {prev: cx.state.context, vars: cx.state.localVars};
179 cx.state.context = {prev: cx.state.context, vars: cx.state.localVars};
179 }
180 }
180 function popcontext() {
181 function popcontext() {
181 cx.state.localVars = cx.state.context.vars;
182 cx.state.localVars = cx.state.context.vars;
182 cx.state.context = cx.state.context.prev;
183 cx.state.context = cx.state.context.prev;
183 }
184 }
184 function pushlex(type, info) {
185 function pushlex(type, info) {
185 var result = function() {
186 var result = function() {
186 var state = cx.state;
187 var state = cx.state;
187 state.lexical = new JSLexical(state.indented, cx.stream.column(), type, null, state.lexical, info)
188 state.lexical = new JSLexical(state.indented, cx.stream.column(), type, null, state.lexical, info)
188 };
189 };
189 result.lex = true;
190 result.lex = true;
190 return result;
191 return result;
191 }
192 }
192 function poplex() {
193 function poplex() {
193 var state = cx.state;
194 var state = cx.state;
194 if (state.lexical.prev) {
195 if (state.lexical.prev) {
195 if (state.lexical.type == ")")
196 if (state.lexical.type == ")")
196 state.indented = state.lexical.indented;
197 state.indented = state.lexical.indented;
197 state.lexical = state.lexical.prev;
198 state.lexical = state.lexical.prev;
198 }
199 }
199 }
200 }
200 poplex.lex = true;
201 poplex.lex = true;
201
202
202 function expect(wanted) {
203 function expect(wanted) {
203 return function expecting(type) {
204 return function expecting(type) {
204 if (type == wanted) return cont();
205 if (type == wanted) return cont();
205 else if (wanted == ";") return pass();
206 else if (wanted == ";") return pass();
206 else return cont(arguments.callee);
207 else return cont(arguments.callee);
207 };
208 };
208 }
209 }
209
210
210 function statement(type) {
211 function statement(type) {
211 if (type == "var") return cont(pushlex("vardef"), vardef1, expect(";"), poplex);
212 if (type == "var") return cont(pushlex("vardef"), vardef1, expect(";"), poplex);
212 if (type == "keyword a") return cont(pushlex("form"), expression, statement, poplex);
213 if (type == "keyword a") return cont(pushlex("form"), expression, statement, poplex);
213 if (type == "keyword b") return cont(pushlex("form"), statement, poplex);
214 if (type == "keyword b") return cont(pushlex("form"), statement, poplex);
214 if (type == "{") return cont(pushlex("}"), block, poplex);
215 if (type == "{") return cont(pushlex("}"), block, poplex);
215 if (type == ";") return cont();
216 if (type == ";") return cont();
216 if (type == "function") return cont(functiondef);
217 if (type == "function") return cont(functiondef);
217 if (type == "for") return cont(pushlex("form"), expect("("), pushlex(")"), forspec1, expect(")"),
218 if (type == "for") return cont(pushlex("form"), expect("("), pushlex(")"), forspec1, expect(")"),
218 poplex, statement, poplex);
219 poplex, statement, poplex);
219 if (type == "variable") return cont(pushlex("stat"), maybelabel);
220 if (type == "variable") return cont(pushlex("stat"), maybelabel);
220 if (type == "switch") return cont(pushlex("form"), expression, pushlex("}", "switch"), expect("{"),
221 if (type == "switch") return cont(pushlex("form"), expression, pushlex("}", "switch"), expect("{"),
221 block, poplex, poplex);
222 block, poplex, poplex);
222 if (type == "case") return cont(expression, expect(":"));
223 if (type == "case") return cont(expression, expect(":"));
223 if (type == "default") return cont(expect(":"));
224 if (type == "default") return cont(expect(":"));
224 if (type == "catch") return cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"),
225 if (type == "catch") return cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"),
225 statement, poplex, popcontext);
226 statement, poplex, popcontext);
226 return pass(pushlex("stat"), expression, expect(";"), poplex);
227 return pass(pushlex("stat"), expression, expect(";"), poplex);
227 }
228 }
228 function expression(type) {
229 function expression(type) {
229 if (atomicTypes.hasOwnProperty(type)) return cont(maybeoperator);
230 if (atomicTypes.hasOwnProperty(type)) return cont(maybeoperator);
230 if (type == "function") return cont(functiondef);
231 if (type == "function") return cont(functiondef);
231 if (type == "keyword c") return cont(expression);
232 if (type == "keyword c") return cont(maybeexpression);
232 if (type == "(") return cont(pushlex(")"), expression, expect(")"), poplex, maybeoperator);
233 if (type == "(") return cont(pushlex(")"), expression, expect(")"), poplex, maybeoperator);
233 if (type == "operator") return cont(expression);
234 if (type == "operator") return cont(expression);
234 if (type == "[") return cont(pushlex("]"), commasep(expression, "]"), poplex, maybeoperator);
235 if (type == "[") return cont(pushlex("]"), commasep(expression, "]"), poplex, maybeoperator);
235 if (type == "{") return cont(pushlex("}"), commasep(objprop, "}"), poplex, maybeoperator);
236 if (type == "{") return cont(pushlex("}"), commasep(objprop, "}"), poplex, maybeoperator);
236 return cont();
237 return cont();
237 }
238 }
239 function maybeexpression(type) {
240 if (type.match(/[;\}\)\],]/)) return pass();
241 return pass(expression);
242 }
243
238 function maybeoperator(type, value) {
244 function maybeoperator(type, value) {
239 if (type == "operator" && /\+\+|--/.test(value)) return cont(maybeoperator);
245 if (type == "operator" && /\+\+|--/.test(value)) return cont(maybeoperator);
240 if (type == "operator") return cont(expression);
246 if (type == "operator") return cont(expression);
241 if (type == ";") return;
247 if (type == ";") return;
242 if (type == "(") return cont(pushlex(")"), commasep(expression, ")"), poplex, maybeoperator);
248 if (type == "(") return cont(pushlex(")"), commasep(expression, ")"), poplex, maybeoperator);
243 if (type == ".") return cont(property, maybeoperator);
249 if (type == ".") return cont(property, maybeoperator);
244 if (type == "[") return cont(pushlex("]"), expression, expect("]"), poplex, maybeoperator);
250 if (type == "[") return cont(pushlex("]"), expression, expect("]"), poplex, maybeoperator);
245 }
251 }
246 function maybelabel(type) {
252 function maybelabel(type) {
247 if (type == ":") return cont(poplex, statement);
253 if (type == ":") return cont(poplex, statement);
248 return pass(maybeoperator, expect(";"), poplex);
254 return pass(maybeoperator, expect(";"), poplex);
249 }
255 }
250 function property(type) {
256 function property(type) {
251 if (type == "variable") {cx.marked = "property"; return cont();}
257 if (type == "variable") {cx.marked = "property"; return cont();}
252 }
258 }
253 function objprop(type) {
259 function objprop(type) {
254 if (type == "variable") cx.marked = "property";
260 if (type == "variable") cx.marked = "property";
255 if (atomicTypes.hasOwnProperty(type)) return cont(expect(":"), expression);
261 if (atomicTypes.hasOwnProperty(type)) return cont(expect(":"), expression);
256 }
262 }
257 function commasep(what, end) {
263 function commasep(what, end) {
258 function proceed(type) {
264 function proceed(type) {
259 if (type == ",") return cont(what, proceed);
265 if (type == ",") return cont(what, proceed);
260 if (type == end) return cont();
266 if (type == end) return cont();
261 return cont(expect(end));
267 return cont(expect(end));
262 }
268 }
263 return function commaSeparated(type) {
269 return function commaSeparated(type) {
264 if (type == end) return cont();
270 if (type == end) return cont();
265 else return pass(what, proceed);
271 else return pass(what, proceed);
266 };
272 };
267 }
273 }
268 function block(type) {
274 function block(type) {
269 if (type == "}") return cont();
275 if (type == "}") return cont();
270 return pass(statement, block);
276 return pass(statement, block);
271 }
277 }
272 function vardef1(type, value) {
278 function vardef1(type, value) {
273 if (type == "variable"){register(value); return cont(vardef2);}
279 if (type == "variable"){register(value); return cont(vardef2);}
274 return cont();
280 return cont();
275 }
281 }
276 function vardef2(type, value) {
282 function vardef2(type, value) {
277 if (value == "=") return cont(expression, vardef2);
283 if (value == "=") return cont(expression, vardef2);
278 if (type == ",") return cont(vardef1);
284 if (type == ",") return cont(vardef1);
279 }
285 }
280 function forspec1(type) {
286 function forspec1(type) {
281 if (type == "var") return cont(vardef1, forspec2);
287 if (type == "var") return cont(vardef1, forspec2);
282 if (type == ";") return pass(forspec2);
288 if (type == ";") return pass(forspec2);
283 if (type == "variable") return cont(formaybein);
289 if (type == "variable") return cont(formaybein);
284 return pass(forspec2);
290 return pass(forspec2);
285 }
291 }
286 function formaybein(type, value) {
292 function formaybein(type, value) {
287 if (value == "in") return cont(expression);
293 if (value == "in") return cont(expression);
288 return cont(maybeoperator, forspec2);
294 return cont(maybeoperator, forspec2);
289 }
295 }
290 function forspec2(type, value) {
296 function forspec2(type, value) {
291 if (type == ";") return cont(forspec3);
297 if (type == ";") return cont(forspec3);
292 if (value == "in") return cont(expression);
298 if (value == "in") return cont(expression);
293 return cont(expression, expect(";"), forspec3);
299 return cont(expression, expect(";"), forspec3);
294 }
300 }
295 function forspec3(type) {
301 function forspec3(type) {
296 if (type != ")") cont(expression);
302 if (type != ")") cont(expression);
297 }
303 }
298 function functiondef(type, value) {
304 function functiondef(type, value) {
299 if (type == "variable") {register(value); return cont(functiondef);}
305 if (type == "variable") {register(value); return cont(functiondef);}
300 if (type == "(") return cont(pushlex(")"), pushcontext, commasep(funarg, ")"), poplex, statement, popcontext);
306 if (type == "(") return cont(pushlex(")"), pushcontext, commasep(funarg, ")"), poplex, statement, popcontext);
301 }
307 }
302 function funarg(type, value) {
308 function funarg(type, value) {
303 if (type == "variable") {register(value); return cont();}
309 if (type == "variable") {register(value); return cont();}
304 }
310 }
305
311
306 // Interface
312 // Interface
307
313
308 return {
314 return {
309 startState: function(basecolumn) {
315 startState: function(basecolumn) {
310 return {
316 return {
311 tokenize: jsTokenBase,
317 tokenize: jsTokenBase,
312 reAllowed: true,
318 reAllowed: true,
319 kwAllowed: true,
313 cc: [],
320 cc: [],
314 lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false),
321 lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false),
315 localVars: null,
322 localVars: null,
316 context: null,
323 context: null,
317 indented: 0
324 indented: 0
318 };
325 };
319 },
326 },
320
327
321 token: function(stream, state) {
328 token: function(stream, state) {
322 if (stream.sol()) {
329 if (stream.sol()) {
323 if (!state.lexical.hasOwnProperty("align"))
330 if (!state.lexical.hasOwnProperty("align"))
324 state.lexical.align = false;
331 state.lexical.align = false;
325 state.indented = stream.indentation();
332 state.indented = stream.indentation();
326 }
333 }
327 if (stream.eatSpace()) return null;
334 if (stream.eatSpace()) return null;
328 var style = state.tokenize(stream, state);
335 var style = state.tokenize(stream, state);
329 if (type == "comment") return style;
336 if (type == "comment") return style;
330 state.reAllowed = type == "operator" || type == "keyword c" || type.match(/^[\[{}\(,;:]$/);
337 state.reAllowed = type == "operator" || type == "keyword c" || type.match(/^[\[{}\(,;:]$/);
338 state.kwAllowed = type != '.';
331 return parseJS(state, style, type, content, stream);
339 return parseJS(state, style, type, content, stream);
332 },
340 },
333
341
334 indent: function(state, textAfter) {
342 indent: function(state, textAfter) {
335 if (state.tokenize != jsTokenBase) return 0;
343 if (state.tokenize != jsTokenBase) return 0;
336 var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical,
344 var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical,
337 type = lexical.type, closing = firstChar == type;
345 type = lexical.type, closing = firstChar == type;
338 if (type == "vardef") return lexical.indented + 4;
346 if (type == "vardef") return lexical.indented + 4;
339 else if (type == "form" && firstChar == "{") return lexical.indented;
347 else if (type == "form" && firstChar == "{") return lexical.indented;
340 else if (type == "stat" || type == "form") return lexical.indented + indentUnit;
348 else if (type == "stat" || type == "form") return lexical.indented + indentUnit;
341 else if (lexical.info == "switch" && !closing)
349 else if (lexical.info == "switch" && !closing)
342 return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit);
350 return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit);
343 else if (lexical.align) return lexical.column + (closing ? 0 : 1);
351 else if (lexical.align) return lexical.column + (closing ? 0 : 1);
344 else return lexical.indented + (closing ? 0 : indentUnit);
352 else return lexical.indented + (closing ? 0 : indentUnit);
345 },
353 },
346
354
347 electricChars: ":{}"
355 electricChars: ":{}"
348 };
356 };
349 });
357 });
350
358
351 CodeMirror.defineMIME("text/javascript", "javascript");
359 CodeMirror.defineMIME("text/javascript", "javascript");
352 CodeMirror.defineMIME("application/json", {name: "javascript", json: true});
360 CodeMirror.defineMIME("application/json", {name: "javascript", json: true});
@@ -1,340 +1,339 b''
1 <!doctype html>
1 <!doctype html>
2 <html>
2 <html>
3 <head>
3 <head>
4 <title>CodeMirror 2: Markdown mode</title>
4 <title>CodeMirror: Markdown mode</title>
5 <link rel="stylesheet" href="../../lib/codemirror.css">
5 <link rel="stylesheet" href="../../lib/codemirror.css">
6 <script src="../../lib/codemirror.js"></script>
6 <script src="../../lib/codemirror.js"></script>
7 <script src="../xml/xml.js"></script>
7 <script src="../xml/xml.js"></script>
8 <script src="markdown.js"></script>
8 <script src="markdown.js"></script>
9 <link rel="stylesheet" href="../../theme/default.css">
10 <link rel="stylesheet" href="markdown.css">
9 <link rel="stylesheet" href="markdown.css">
11 <style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
10 <style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
12 <link rel="stylesheet" href="../../css/docs.css">
11 <link rel="stylesheet" href="../../doc/docs.css">
13 </head>
12 </head>
14 <body>
13 <body>
15 <h1>CodeMirror 2: Markdown mode</h1>
14 <h1>CodeMirror: Markdown mode</h1>
16
15
17 <!-- source: http://daringfireball.net/projects/markdown/basics.text -->
16 <!-- source: http://daringfireball.net/projects/markdown/basics.text -->
18 <form><textarea id="code" name="code">
17 <form><textarea id="code" name="code">
19 Markdown: Basics
18 Markdown: Basics
20 ================
19 ================
21
20
22 &lt;ul id="ProjectSubmenu"&gt;
21 &lt;ul id="ProjectSubmenu"&gt;
23 &lt;li&gt;&lt;a href="/projects/markdown/" title="Markdown Project Page"&gt;Main&lt;/a&gt;&lt;/li&gt;
22 &lt;li&gt;&lt;a href="/projects/markdown/" title="Markdown Project Page"&gt;Main&lt;/a&gt;&lt;/li&gt;
24 &lt;li&gt;&lt;a class="selected" title="Markdown Basics"&gt;Basics&lt;/a&gt;&lt;/li&gt;
23 &lt;li&gt;&lt;a class="selected" title="Markdown Basics"&gt;Basics&lt;/a&gt;&lt;/li&gt;
25 &lt;li&gt;&lt;a href="/projects/markdown/syntax" title="Markdown Syntax Documentation"&gt;Syntax&lt;/a&gt;&lt;/li&gt;
24 &lt;li&gt;&lt;a href="/projects/markdown/syntax" title="Markdown Syntax Documentation"&gt;Syntax&lt;/a&gt;&lt;/li&gt;
26 &lt;li&gt;&lt;a href="/projects/markdown/license" title="Pricing and License Information"&gt;License&lt;/a&gt;&lt;/li&gt;
25 &lt;li&gt;&lt;a href="/projects/markdown/license" title="Pricing and License Information"&gt;License&lt;/a&gt;&lt;/li&gt;
27 &lt;li&gt;&lt;a href="/projects/markdown/dingus" title="Online Markdown Web Form"&gt;Dingus&lt;/a&gt;&lt;/li&gt;
26 &lt;li&gt;&lt;a href="/projects/markdown/dingus" title="Online Markdown Web Form"&gt;Dingus&lt;/a&gt;&lt;/li&gt;
28 &lt;/ul&gt;
27 &lt;/ul&gt;
29
28
30
29
31 Getting the Gist of Markdown's Formatting Syntax
30 Getting the Gist of Markdown's Formatting Syntax
32 ------------------------------------------------
31 ------------------------------------------------
33
32
34 This page offers a brief overview of what it's like to use Markdown.
33 This page offers a brief overview of what it's like to use Markdown.
35 The [syntax page] [s] provides complete, detailed documentation for
34 The [syntax page] [s] provides complete, detailed documentation for
36 every feature, but Markdown should be very easy to pick up simply by
35 every feature, but Markdown should be very easy to pick up simply by
37 looking at a few examples of it in action. The examples on this page
36 looking at a few examples of it in action. The examples on this page
38 are written in a before/after style, showing example syntax and the
37 are written in a before/after style, showing example syntax and the
39 HTML output produced by Markdown.
38 HTML output produced by Markdown.
40
39
41 It's also helpful to simply try Markdown out; the [Dingus] [d] is a
40 It's also helpful to simply try Markdown out; the [Dingus] [d] is a
42 web application that allows you type your own Markdown-formatted text
41 web application that allows you type your own Markdown-formatted text
43 and translate it to XHTML.
42 and translate it to XHTML.
44
43
45 **Note:** This document is itself written using Markdown; you
44 **Note:** This document is itself written using Markdown; you
46 can [see the source for it by adding '.text' to the URL] [src].
45 can [see the source for it by adding '.text' to the URL] [src].
47
46
48 [s]: /projects/markdown/syntax "Markdown Syntax"
47 [s]: /projects/markdown/syntax "Markdown Syntax"
49 [d]: /projects/markdown/dingus "Markdown Dingus"
48 [d]: /projects/markdown/dingus "Markdown Dingus"
50 [src]: /projects/markdown/basics.text
49 [src]: /projects/markdown/basics.text
51
50
52
51
53 ## Paragraphs, Headers, Blockquotes ##
52 ## Paragraphs, Headers, Blockquotes ##
54
53
55 A paragraph is simply one or more consecutive lines of text, separated
54 A paragraph is simply one or more consecutive lines of text, separated
56 by one or more blank lines. (A blank line is any line that looks like
55 by one or more blank lines. (A blank line is any line that looks like
57 a blank line -- a line containing nothing but spaces or tabs is
56 a blank line -- a line containing nothing but spaces or tabs is
58 considered blank.) Normal paragraphs should not be indented with
57 considered blank.) Normal paragraphs should not be indented with
59 spaces or tabs.
58 spaces or tabs.
60
59
61 Markdown offers two styles of headers: *Setext* and *atx*.
60 Markdown offers two styles of headers: *Setext* and *atx*.
62 Setext-style headers for `&lt;h1&gt;` and `&lt;h2&gt;` are created by
61 Setext-style headers for `&lt;h1&gt;` and `&lt;h2&gt;` are created by
63 "underlining" with equal signs (`=`) and hyphens (`-`), respectively.
62 "underlining" with equal signs (`=`) and hyphens (`-`), respectively.
64 To create an atx-style header, you put 1-6 hash marks (`#`) at the
63 To create an atx-style header, you put 1-6 hash marks (`#`) at the
65 beginning of the line -- the number of hashes equals the resulting
64 beginning of the line -- the number of hashes equals the resulting
66 HTML header level.
65 HTML header level.
67
66
68 Blockquotes are indicated using email-style '`&gt;`' angle brackets.
67 Blockquotes are indicated using email-style '`&gt;`' angle brackets.
69
68
70 Markdown:
69 Markdown:
71
70
72 A First Level Header
71 A First Level Header
73 ====================
72 ====================
74
73
75 A Second Level Header
74 A Second Level Header
76 ---------------------
75 ---------------------
77
76
78 Now is the time for all good men to come to
77 Now is the time for all good men to come to
79 the aid of their country. This is just a
78 the aid of their country. This is just a
80 regular paragraph.
79 regular paragraph.
81
80
82 The quick brown fox jumped over the lazy
81 The quick brown fox jumped over the lazy
83 dog's back.
82 dog's back.
84
83
85 ### Header 3
84 ### Header 3
86
85
87 &gt; This is a blockquote.
86 &gt; This is a blockquote.
88 &gt;
87 &gt;
89 &gt; This is the second paragraph in the blockquote.
88 &gt; This is the second paragraph in the blockquote.
90 &gt;
89 &gt;
91 &gt; ## This is an H2 in a blockquote
90 &gt; ## This is an H2 in a blockquote
92
91
93
92
94 Output:
93 Output:
95
94
96 &lt;h1&gt;A First Level Header&lt;/h1&gt;
95 &lt;h1&gt;A First Level Header&lt;/h1&gt;
97
96
98 &lt;h2&gt;A Second Level Header&lt;/h2&gt;
97 &lt;h2&gt;A Second Level Header&lt;/h2&gt;
99
98
100 &lt;p&gt;Now is the time for all good men to come to
99 &lt;p&gt;Now is the time for all good men to come to
101 the aid of their country. This is just a
100 the aid of their country. This is just a
102 regular paragraph.&lt;/p&gt;
101 regular paragraph.&lt;/p&gt;
103
102
104 &lt;p&gt;The quick brown fox jumped over the lazy
103 &lt;p&gt;The quick brown fox jumped over the lazy
105 dog's back.&lt;/p&gt;
104 dog's back.&lt;/p&gt;
106
105
107 &lt;h3&gt;Header 3&lt;/h3&gt;
106 &lt;h3&gt;Header 3&lt;/h3&gt;
108
107
109 &lt;blockquote&gt;
108 &lt;blockquote&gt;
110 &lt;p&gt;This is a blockquote.&lt;/p&gt;
109 &lt;p&gt;This is a blockquote.&lt;/p&gt;
111
110
112 &lt;p&gt;This is the second paragraph in the blockquote.&lt;/p&gt;
111 &lt;p&gt;This is the second paragraph in the blockquote.&lt;/p&gt;
113
112
114 &lt;h2&gt;This is an H2 in a blockquote&lt;/h2&gt;
113 &lt;h2&gt;This is an H2 in a blockquote&lt;/h2&gt;
115 &lt;/blockquote&gt;
114 &lt;/blockquote&gt;
116
115
117
116
118
117
119 ### Phrase Emphasis ###
118 ### Phrase Emphasis ###
120
119
121 Markdown uses asterisks and underscores to indicate spans of emphasis.
120 Markdown uses asterisks and underscores to indicate spans of emphasis.
122
121
123 Markdown:
122 Markdown:
124
123
125 Some of these words *are emphasized*.
124 Some of these words *are emphasized*.
126 Some of these words _are emphasized also_.
125 Some of these words _are emphasized also_.
127
126
128 Use two asterisks for **strong emphasis**.
127 Use two asterisks for **strong emphasis**.
129 Or, if you prefer, __use two underscores instead__.
128 Or, if you prefer, __use two underscores instead__.
130
129
131 Output:
130 Output:
132
131
133 &lt;p&gt;Some of these words &lt;em&gt;are emphasized&lt;/em&gt;.
132 &lt;p&gt;Some of these words &lt;em&gt;are emphasized&lt;/em&gt;.
134 Some of these words &lt;em&gt;are emphasized also&lt;/em&gt;.&lt;/p&gt;
133 Some of these words &lt;em&gt;are emphasized also&lt;/em&gt;.&lt;/p&gt;
135
134
136 &lt;p&gt;Use two asterisks for &lt;strong&gt;strong emphasis&lt;/strong&gt;.
135 &lt;p&gt;Use two asterisks for &lt;strong&gt;strong emphasis&lt;/strong&gt;.
137 Or, if you prefer, &lt;strong&gt;use two underscores instead&lt;/strong&gt;.&lt;/p&gt;
136 Or, if you prefer, &lt;strong&gt;use two underscores instead&lt;/strong&gt;.&lt;/p&gt;
138
137
139
138
140
139
141 ## Lists ##
140 ## Lists ##
142
141
143 Unordered (bulleted) lists use asterisks, pluses, and hyphens (`*`,
142 Unordered (bulleted) lists use asterisks, pluses, and hyphens (`*`,
144 `+`, and `-`) as list markers. These three markers are
143 `+`, and `-`) as list markers. These three markers are
145 interchangable; this:
144 interchangable; this:
146
145
147 * Candy.
146 * Candy.
148 * Gum.
147 * Gum.
149 * Booze.
148 * Booze.
150
149
151 this:
150 this:
152
151
153 + Candy.
152 + Candy.
154 + Gum.
153 + Gum.
155 + Booze.
154 + Booze.
156
155
157 and this:
156 and this:
158
157
159 - Candy.
158 - Candy.
160 - Gum.
159 - Gum.
161 - Booze.
160 - Booze.
162
161
163 all produce the same output:
162 all produce the same output:
164
163
165 &lt;ul&gt;
164 &lt;ul&gt;
166 &lt;li&gt;Candy.&lt;/li&gt;
165 &lt;li&gt;Candy.&lt;/li&gt;
167 &lt;li&gt;Gum.&lt;/li&gt;
166 &lt;li&gt;Gum.&lt;/li&gt;
168 &lt;li&gt;Booze.&lt;/li&gt;
167 &lt;li&gt;Booze.&lt;/li&gt;
169 &lt;/ul&gt;
168 &lt;/ul&gt;
170
169
171 Ordered (numbered) lists use regular numbers, followed by periods, as
170 Ordered (numbered) lists use regular numbers, followed by periods, as
172 list markers:
171 list markers:
173
172
174 1. Red
173 1. Red
175 2. Green
174 2. Green
176 3. Blue
175 3. Blue
177
176
178 Output:
177 Output:
179
178
180 &lt;ol&gt;
179 &lt;ol&gt;
181 &lt;li&gt;Red&lt;/li&gt;
180 &lt;li&gt;Red&lt;/li&gt;
182 &lt;li&gt;Green&lt;/li&gt;
181 &lt;li&gt;Green&lt;/li&gt;
183 &lt;li&gt;Blue&lt;/li&gt;
182 &lt;li&gt;Blue&lt;/li&gt;
184 &lt;/ol&gt;
183 &lt;/ol&gt;
185
184
186 If you put blank lines between items, you'll get `&lt;p&gt;` tags for the
185 If you put blank lines between items, you'll get `&lt;p&gt;` tags for the
187 list item text. You can create multi-paragraph list items by indenting
186 list item text. You can create multi-paragraph list items by indenting
188 the paragraphs by 4 spaces or 1 tab:
187 the paragraphs by 4 spaces or 1 tab:
189
188
190 * A list item.
189 * A list item.
191
190
192 With multiple paragraphs.
191 With multiple paragraphs.
193
192
194 * Another item in the list.
193 * Another item in the list.
195
194
196 Output:
195 Output:
197
196
198 &lt;ul&gt;
197 &lt;ul&gt;
199 &lt;li&gt;&lt;p&gt;A list item.&lt;/p&gt;
198 &lt;li&gt;&lt;p&gt;A list item.&lt;/p&gt;
200 &lt;p&gt;With multiple paragraphs.&lt;/p&gt;&lt;/li&gt;
199 &lt;p&gt;With multiple paragraphs.&lt;/p&gt;&lt;/li&gt;
201 &lt;li&gt;&lt;p&gt;Another item in the list.&lt;/p&gt;&lt;/li&gt;
200 &lt;li&gt;&lt;p&gt;Another item in the list.&lt;/p&gt;&lt;/li&gt;
202 &lt;/ul&gt;
201 &lt;/ul&gt;
203
202
204
203
205
204
206 ### Links ###
205 ### Links ###
207
206
208 Markdown supports two styles for creating links: *inline* and
207 Markdown supports two styles for creating links: *inline* and
209 *reference*. With both styles, you use square brackets to delimit the
208 *reference*. With both styles, you use square brackets to delimit the
210 text you want to turn into a link.
209 text you want to turn into a link.
211
210
212 Inline-style links use parentheses immediately after the link text.
211 Inline-style links use parentheses immediately after the link text.
213 For example:
212 For example:
214
213
215 This is an [example link](http://example.com/).
214 This is an [example link](http://example.com/).
216
215
217 Output:
216 Output:
218
217
219 &lt;p&gt;This is an &lt;a href="http://example.com/"&gt;
218 &lt;p&gt;This is an &lt;a href="http://example.com/"&gt;
220 example link&lt;/a&gt;.&lt;/p&gt;
219 example link&lt;/a&gt;.&lt;/p&gt;
221
220
222 Optionally, you may include a title attribute in the parentheses:
221 Optionally, you may include a title attribute in the parentheses:
223
222
224 This is an [example link](http://example.com/ "With a Title").
223 This is an [example link](http://example.com/ "With a Title").
225
224
226 Output:
225 Output:
227
226
228 &lt;p&gt;This is an &lt;a href="http://example.com/" title="With a Title"&gt;
227 &lt;p&gt;This is an &lt;a href="http://example.com/" title="With a Title"&gt;
229 example link&lt;/a&gt;.&lt;/p&gt;
228 example link&lt;/a&gt;.&lt;/p&gt;
230
229
231 Reference-style links allow you to refer to your links by names, which
230 Reference-style links allow you to refer to your links by names, which
232 you define elsewhere in your document:
231 you define elsewhere in your document:
233
232
234 I get 10 times more traffic from [Google][1] than from
233 I get 10 times more traffic from [Google][1] than from
235 [Yahoo][2] or [MSN][3].
234 [Yahoo][2] or [MSN][3].
236
235
237 [1]: http://google.com/ "Google"
236 [1]: http://google.com/ "Google"
238 [2]: http://search.yahoo.com/ "Yahoo Search"
237 [2]: http://search.yahoo.com/ "Yahoo Search"
239 [3]: http://search.msn.com/ "MSN Search"
238 [3]: http://search.msn.com/ "MSN Search"
240
239
241 Output:
240 Output:
242
241
243 &lt;p&gt;I get 10 times more traffic from &lt;a href="http://google.com/"
242 &lt;p&gt;I get 10 times more traffic from &lt;a href="http://google.com/"
244 title="Google"&gt;Google&lt;/a&gt; than from &lt;a href="http://search.yahoo.com/"
243 title="Google"&gt;Google&lt;/a&gt; than from &lt;a href="http://search.yahoo.com/"
245 title="Yahoo Search"&gt;Yahoo&lt;/a&gt; or &lt;a href="http://search.msn.com/"
244 title="Yahoo Search"&gt;Yahoo&lt;/a&gt; or &lt;a href="http://search.msn.com/"
246 title="MSN Search"&gt;MSN&lt;/a&gt;.&lt;/p&gt;
245 title="MSN Search"&gt;MSN&lt;/a&gt;.&lt;/p&gt;
247
246
248 The title attribute is optional. Link names may contain letters,
247 The title attribute is optional. Link names may contain letters,
249 numbers and spaces, but are *not* case sensitive:
248 numbers and spaces, but are *not* case sensitive:
250
249
251 I start my morning with a cup of coffee and
250 I start my morning with a cup of coffee and
252 [The New York Times][NY Times].
251 [The New York Times][NY Times].
253
252
254 [ny times]: http://www.nytimes.com/
253 [ny times]: http://www.nytimes.com/
255
254
256 Output:
255 Output:
257
256
258 &lt;p&gt;I start my morning with a cup of coffee and
257 &lt;p&gt;I start my morning with a cup of coffee and
259 &lt;a href="http://www.nytimes.com/"&gt;The New York Times&lt;/a&gt;.&lt;/p&gt;
258 &lt;a href="http://www.nytimes.com/"&gt;The New York Times&lt;/a&gt;.&lt;/p&gt;
260
259
261
260
262 ### Images ###
261 ### Images ###
263
262
264 Image syntax is very much like link syntax.
263 Image syntax is very much like link syntax.
265
264
266 Inline (titles are optional):
265 Inline (titles are optional):
267
266
268 ![alt text](/path/to/img.jpg "Title")
267 ![alt text](/path/to/img.jpg "Title")
269
268
270 Reference-style:
269 Reference-style:
271
270
272 ![alt text][id]
271 ![alt text][id]
273
272
274 [id]: /path/to/img.jpg "Title"
273 [id]: /path/to/img.jpg "Title"
275
274
276 Both of the above examples produce the same output:
275 Both of the above examples produce the same output:
277
276
278 &lt;img src="/path/to/img.jpg" alt="alt text" title="Title" /&gt;
277 &lt;img src="/path/to/img.jpg" alt="alt text" title="Title" /&gt;
279
278
280
279
281
280
282 ### Code ###
281 ### Code ###
283
282
284 In a regular paragraph, you can create code span by wrapping text in
283 In a regular paragraph, you can create code span by wrapping text in
285 backtick quotes. Any ampersands (`&amp;`) and angle brackets (`&lt;` or
284 backtick quotes. Any ampersands (`&amp;`) and angle brackets (`&lt;` or
286 `&gt;`) will automatically be translated into HTML entities. This makes
285 `&gt;`) will automatically be translated into HTML entities. This makes
287 it easy to use Markdown to write about HTML example code:
286 it easy to use Markdown to write about HTML example code:
288
287
289 I strongly recommend against using any `&lt;blink&gt;` tags.
288 I strongly recommend against using any `&lt;blink&gt;` tags.
290
289
291 I wish SmartyPants used named entities like `&amp;mdash;`
290 I wish SmartyPants used named entities like `&amp;mdash;`
292 instead of decimal-encoded entites like `&amp;#8212;`.
291 instead of decimal-encoded entites like `&amp;#8212;`.
293
292
294 Output:
293 Output:
295
294
296 &lt;p&gt;I strongly recommend against using any
295 &lt;p&gt;I strongly recommend against using any
297 &lt;code&gt;&amp;lt;blink&amp;gt;&lt;/code&gt; tags.&lt;/p&gt;
296 &lt;code&gt;&amp;lt;blink&amp;gt;&lt;/code&gt; tags.&lt;/p&gt;
298
297
299 &lt;p&gt;I wish SmartyPants used named entities like
298 &lt;p&gt;I wish SmartyPants used named entities like
300 &lt;code&gt;&amp;amp;mdash;&lt;/code&gt; instead of decimal-encoded
299 &lt;code&gt;&amp;amp;mdash;&lt;/code&gt; instead of decimal-encoded
301 entites like &lt;code&gt;&amp;amp;#8212;&lt;/code&gt;.&lt;/p&gt;
300 entites like &lt;code&gt;&amp;amp;#8212;&lt;/code&gt;.&lt;/p&gt;
302
301
303
302
304 To specify an entire block of pre-formatted code, indent every line of
303 To specify an entire block of pre-formatted code, indent every line of
305 the block by 4 spaces or 1 tab. Just like with code spans, `&amp;`, `&lt;`,
304 the block by 4 spaces or 1 tab. Just like with code spans, `&amp;`, `&lt;`,
306 and `&gt;` characters will be escaped automatically.
305 and `&gt;` characters will be escaped automatically.
307
306
308 Markdown:
307 Markdown:
309
308
310 If you want your page to validate under XHTML 1.0 Strict,
309 If you want your page to validate under XHTML 1.0 Strict,
311 you've got to put paragraph tags in your blockquotes:
310 you've got to put paragraph tags in your blockquotes:
312
311
313 &lt;blockquote&gt;
312 &lt;blockquote&gt;
314 &lt;p&gt;For example.&lt;/p&gt;
313 &lt;p&gt;For example.&lt;/p&gt;
315 &lt;/blockquote&gt;
314 &lt;/blockquote&gt;
316
315
317 Output:
316 Output:
318
317
319 &lt;p&gt;If you want your page to validate under XHTML 1.0 Strict,
318 &lt;p&gt;If you want your page to validate under XHTML 1.0 Strict,
320 you've got to put paragraph tags in your blockquotes:&lt;/p&gt;
319 you've got to put paragraph tags in your blockquotes:&lt;/p&gt;
321
320
322 &lt;pre&gt;&lt;code&gt;&amp;lt;blockquote&amp;gt;
321 &lt;pre&gt;&lt;code&gt;&amp;lt;blockquote&amp;gt;
323 &amp;lt;p&amp;gt;For example.&amp;lt;/p&amp;gt;
322 &amp;lt;p&amp;gt;For example.&amp;lt;/p&amp;gt;
324 &amp;lt;/blockquote&amp;gt;
323 &amp;lt;/blockquote&amp;gt;
325 &lt;/code&gt;&lt;/pre&gt;
324 &lt;/code&gt;&lt;/pre&gt;
326 </textarea></form>
325 </textarea></form>
327
326
328 <script>
327 <script>
329 var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
328 var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
330 mode: 'markdown',
329 mode: 'markdown',
331 lineNumbers: true,
330 lineNumbers: true,
332 matchBrackets: true,
331 matchBrackets: true,
333 theme: "default"
332 theme: "default"
334 });
333 });
335 </script>
334 </script>
336
335
337 <p><strong>MIME types defined:</strong> <code>text/x-markdown</code>.</p>
336 <p><strong>MIME types defined:</strong> <code>text/x-markdown</code>.</p>
338
337
339 </body>
338 </body>
340 </html>
339 </html>
@@ -1,230 +1,242 b''
1 CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
1 CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
2
2
3 var htmlMode = CodeMirror.getMode(cmCfg, { name: 'xml', htmlMode: true });
3 var htmlMode = CodeMirror.getMode(cmCfg, { name: 'xml', htmlMode: true });
4
4
5 var header = 'header'
5 var header = 'header'
6 , code = 'code'
6 , code = 'comment'
7 , quote = 'quote'
7 , quote = 'quote'
8 , list = 'list'
8 , list = 'string'
9 , hr = 'hr'
9 , hr = 'hr'
10 , linktext = 'linktext'
10 , linktext = 'link'
11 , linkhref = 'linkhref'
11 , linkhref = 'string'
12 , em = 'em'
12 , em = 'em'
13 , strong = 'strong'
13 , strong = 'strong'
14 , emstrong = 'emstrong';
14 , emstrong = 'emstrong';
15
15
16 var hrRE = /^[*-=_]/
16 var hrRE = /^[*-=_]/
17 , ulRE = /^[*-+]\s+/
17 , ulRE = /^[*-+]\s+/
18 , olRE = /^[0-9]\.\s+/
18 , olRE = /^[0-9]\.\s+/
19 , headerRE = /^(?:\={3,}|-{3,})$/
19 , headerRE = /^(?:\={3,}|-{3,})$/
20 , codeRE = /^(k:\t|\s{4,})/
20 , codeRE = /^(k:\t|\s{4,})/
21 , textRE = /^[^\[*_\\<>`]+/;
21 , textRE = /^[^\[*_\\<>`]+/;
22
22
23 function switchInline(stream, state, f) {
23 function switchInline(stream, state, f) {
24 state.f = state.inline = f;
24 state.f = state.inline = f;
25 return f(stream, state);
25 return f(stream, state);
26 }
26 }
27
27
28 function switchBlock(stream, state, f) {
28 function switchBlock(stream, state, f) {
29 state.f = state.block = f;
29 state.f = state.block = f;
30 return f(stream, state);
30 return f(stream, state);
31 }
31 }
32
32
33
33
34 // Blocks
34 // Blocks
35
35
36 function blockNormal(stream, state) {
36 function blockNormal(stream, state) {
37 if (stream.match(codeRE)) {
37 if (stream.match(codeRE)) {
38 stream.skipToEnd();
38 stream.skipToEnd();
39 return code;
39 return code;
40 }
40 }
41
41
42 if (stream.eatSpace()) {
42 if (stream.eatSpace()) {
43 return null;
43 return null;
44 }
44 }
45
45
46 if (stream.peek() === '#' || stream.match(headerRE)) {
46 if (stream.peek() === '#' || stream.match(headerRE)) {
47 stream.skipToEnd();
47 stream.skipToEnd();
48 return header;
48 return header;
49 }
49 }
50 if (stream.eat('>')) {
50 if (stream.eat('>')) {
51 state.indentation++;
51 state.indentation++;
52 return quote;
52 return quote;
53 }
53 }
54 if (stream.peek() === '<') {
55 return switchBlock(stream, state, htmlBlock);
56 }
57 if (stream.peek() === '[') {
54 if (stream.peek() === '[') {
58 return switchInline(stream, state, footnoteLink);
55 return switchInline(stream, state, footnoteLink);
59 }
56 }
60 if (hrRE.test(stream.peek())) {
57 if (hrRE.test(stream.peek())) {
61 var re = new RegExp('(?:\s*['+stream.peek()+']){3,}$');
58 var re = new RegExp('(?:\s*['+stream.peek()+']){3,}$');
62 if (stream.match(re, true)) {
59 if (stream.match(re, true)) {
63 return hr;
60 return hr;
64 }
61 }
65 }
62 }
66
63
67 var match;
64 var match;
68 if (match = stream.match(ulRE, true) || stream.match(olRE, true)) {
65 if (match = stream.match(ulRE, true) || stream.match(olRE, true)) {
69 state.indentation += match[0].length;
66 state.indentation += match[0].length;
70 return list;
67 return list;
71 }
68 }
72
69
73 return switchInline(stream, state, state.inline);
70 return switchInline(stream, state, state.inline);
74 }
71 }
75
72
76 function htmlBlock(stream, state) {
73 function htmlBlock(stream, state) {
77 var type = htmlMode.token(stream, state.htmlState);
74 var style = htmlMode.token(stream, state.htmlState);
78 if (stream.eol() && !state.htmlState.context) {
75 if (style === 'tag' && state.htmlState.type !== 'openTag' && !state.htmlState.context) {
76 state.f = inlineNormal;
79 state.block = blockNormal;
77 state.block = blockNormal;
80 }
78 }
81 return type;
79 return style;
82 }
80 }
83
81
84
82
85 // Inline
83 // Inline
86
84 function getType(state) {
87 function inlineNormal(stream, state) {
88 function getType() {
89 return state.strong ? (state.em ? emstrong : strong)
85 return state.strong ? (state.em ? emstrong : strong)
90 : (state.em ? em : null);
86 : (state.em ? em : null);
91 }
87 }
92
88
89 function handleText(stream, state) {
93 if (stream.match(textRE, true)) {
90 if (stream.match(textRE, true)) {
94 return getType();
91 return getType(state);
92 }
93 return undefined;
95 }
94 }
96
95
96 function inlineNormal(stream, state) {
97 var style = state.text(stream, state)
98 if (typeof style !== 'undefined')
99 return style;
100
97 var ch = stream.next();
101 var ch = stream.next();
98
102
99 if (ch === '\\') {
103 if (ch === '\\') {
100 stream.next();
104 stream.next();
101 return getType();
105 return getType(state);
102 }
106 }
103 if (ch === '`') {
107 if (ch === '`') {
104 return switchInline(stream, state, inlineElement(code, '`'));
108 return switchInline(stream, state, inlineElement(code, '`'));
105 }
109 }
106 if (ch === '<') {
107 return switchInline(stream, state, inlineElement(linktext, '>'));
108 }
109 if (ch === '[') {
110 if (ch === '[') {
110 return switchInline(stream, state, linkText);
111 return switchInline(stream, state, linkText);
111 }
112 }
113 if (ch === '<' && stream.match(/^\w/, false)) {
114 stream.backUp(1);
115 return switchBlock(stream, state, htmlBlock);
116 }
112
117
113 var t = getType();
118 var t = getType(state);
114 if (ch === '*' || ch === '_') {
119 if (ch === '*' || ch === '_') {
115 if (stream.eat(ch)) {
120 if (stream.eat(ch)) {
116 return (state.strong = !state.strong) ? getType() : t;
121 return (state.strong = !state.strong) ? getType(state) : t;
117 }
122 }
118 return (state.em = !state.em) ? getType() : t;
123 return (state.em = !state.em) ? getType(state) : t;
119 }
124 }
120
125
121 return getType();
126 return getType(state);
122 }
127 }
123
128
124 function linkText(stream, state) {
129 function linkText(stream, state) {
125 while (!stream.eol()) {
130 while (!stream.eol()) {
126 var ch = stream.next();
131 var ch = stream.next();
127 if (ch === '\\') stream.next();
132 if (ch === '\\') stream.next();
128 if (ch === ']') {
133 if (ch === ']') {
129 state.inline = state.f = linkHref;
134 state.inline = state.f = linkHref;
130 return linktext;
135 return linktext;
131 }
136 }
132 }
137 }
133 return linktext;
138 return linktext;
134 }
139 }
135
140
136 function linkHref(stream, state) {
141 function linkHref(stream, state) {
137 stream.eatSpace();
142 stream.eatSpace();
138 var ch = stream.next();
143 var ch = stream.next();
139 if (ch === '(' || ch === '[') {
144 if (ch === '(' || ch === '[') {
140 return switchInline(stream, state, inlineElement(linkhref, ch === '(' ? ')' : ']'));
145 return switchInline(stream, state, inlineElement(linkhref, ch === '(' ? ')' : ']'));
141 }
146 }
142 return 'error';
147 return 'error';
143 }
148 }
144
149
145 function footnoteLink(stream, state) {
150 function footnoteLink(stream, state) {
146 if (stream.match(/^[^\]]*\]:/, true)) {
151 if (stream.match(/^[^\]]*\]:/, true)) {
147 state.f = footnoteUrl;
152 state.f = footnoteUrl;
148 return linktext;
153 return linktext;
149 }
154 }
150 return switchInline(stream, state, inlineNormal);
155 return switchInline(stream, state, inlineNormal);
151 }
156 }
152
157
153 function footnoteUrl(stream, state) {
158 function footnoteUrl(stream, state) {
154 stream.eatSpace();
159 stream.eatSpace();
155 stream.match(/^[^\s]+/, true);
160 stream.match(/^[^\s]+/, true);
156 state.f = state.inline = inlineNormal;
161 state.f = state.inline = inlineNormal;
157 return linkhref;
162 return linkhref;
158 }
163 }
159
164
165 function inlineRE(endChar) {
166 if (!inlineRE[endChar]) {
167 // match any not-escaped-non-endChar and any escaped char
168 // then match endChar or eol
169 inlineRE[endChar] = new RegExp('^(?:[^\\\\\\' + endChar + ']|\\\\.)*(?:\\' + endChar + '|$)');
170 }
171 return inlineRE[endChar];
172 }
173
160 function inlineElement(type, endChar, next) {
174 function inlineElement(type, endChar, next) {
161 next = next || inlineNormal;
175 next = next || inlineNormal;
162 return function(stream, state) {
176 return function(stream, state) {
163 while (!stream.eol()) {
177 stream.match(inlineRE(endChar));
164 var ch = stream.next();
165 if (ch === '\\') stream.next();
166 if (ch === endChar) {
167 state.inline = state.f = next;
178 state.inline = state.f = next;
168 return type;
179 return type;
169 }
170 }
171 return type;
172 };
180 };
173 }
181 }
174
182
175 return {
183 return {
176 startState: function() {
184 startState: function() {
177 return {
185 return {
178 f: blockNormal,
186 f: blockNormal,
179
187
180 block: blockNormal,
188 block: blockNormal,
181 htmlState: htmlMode.startState(),
189 htmlState: htmlMode.startState(),
182 indentation: 0,
190 indentation: 0,
183
191
184 inline: inlineNormal,
192 inline: inlineNormal,
193 text: handleText,
185 em: false,
194 em: false,
186 strong: false
195 strong: false
187 };
196 };
188 },
197 },
189
198
190 copyState: function(s) {
199 copyState: function(s) {
191 return {
200 return {
192 f: s.f,
201 f: s.f,
193
202
194 block: s.block,
203 block: s.block,
195 htmlState: CodeMirror.copyState(htmlMode, s.htmlState),
204 htmlState: CodeMirror.copyState(htmlMode, s.htmlState),
196 indentation: s.indentation,
205 indentation: s.indentation,
197
206
198 inline: s.inline,
207 inline: s.inline,
208 text: s.text,
199 em: s.em,
209 em: s.em,
200 strong: s.strong
210 strong: s.strong
201 };
211 };
202 },
212 },
203
213
204 token: function(stream, state) {
214 token: function(stream, state) {
205 if (stream.sol()) {
215 if (stream.sol()) {
206 state.f = state.block;
216 state.f = state.block;
207 var previousIndentation = state.indentation
217 var previousIndentation = state.indentation
208 , currentIndentation = 0;
218 , currentIndentation = 0;
209 while (previousIndentation > 0) {
219 while (previousIndentation > 0) {
210 if (stream.eat(' ')) {
220 if (stream.eat(' ')) {
211 previousIndentation--;
221 previousIndentation--;
212 currentIndentation++;
222 currentIndentation++;
213 } else if (previousIndentation >= 4 && stream.eat('\t')) {
223 } else if (previousIndentation >= 4 && stream.eat('\t')) {
214 previousIndentation -= 4;
224 previousIndentation -= 4;
215 currentIndentation += 4;
225 currentIndentation += 4;
216 } else {
226 } else {
217 break;
227 break;
218 }
228 }
219 }
229 }
220 state.indentation = currentIndentation;
230 state.indentation = currentIndentation;
221
231
222 if (currentIndentation > 0) return null;
232 if (currentIndentation > 0) return null;
223 }
233 }
224 return state.f(stream, state);
234 return state.f(stream, state);
225 }
235 },
236
237 getType: getType
226 };
238 };
227
239
228 });
240 });
229
241
230 CodeMirror.defineMIME("text/x-markdown", "markdown");
242 CodeMirror.defineMIME("text/x-markdown", "markdown");
@@ -1,123 +1,122 b''
1 <!doctype html>
1 <!doctype html>
2 <html>
2 <html>
3 <head>
3 <head>
4 <title>CodeMirror 2: Python mode</title>
4 <title>CodeMirror: Python mode</title>
5 <link rel="stylesheet" href="../../lib/codemirror.css">
5 <link rel="stylesheet" href="../../lib/codemirror.css">
6 <script src="../../lib/codemirror.js"></script>
6 <script src="../../lib/codemirror.js"></script>
7 <script src="python.js"></script>
7 <script src="python.js"></script>
8 <link rel="stylesheet" href="../../theme/default.css">
8 <link rel="stylesheet" href="../../doc/docs.css">
9 <link rel="stylesheet" href="../../css/docs.css">
10 <style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
9 <style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
11 </head>
10 </head>
12 <body>
11 <body>
13 <h1>CodeMirror 2: Python mode</h1>
12 <h1>CodeMirror: Python mode</h1>
14
13
15 <div><textarea id="code" name="code">
14 <div><textarea id="code" name="code">
16 # Literals
15 # Literals
17 1234
16 1234
18 0.0e101
17 0.0e101
19 .123
18 .123
20 0b01010011100
19 0b01010011100
21 0o01234567
20 0o01234567
22 0x0987654321abcdef
21 0x0987654321abcdef
23 7
22 7
24 2147483647
23 2147483647
25 3L
24 3L
26 79228162514264337593543950336L
25 79228162514264337593543950336L
27 0x100000000L
26 0x100000000L
28 79228162514264337593543950336
27 79228162514264337593543950336
29 0xdeadbeef
28 0xdeadbeef
30 3.14j
29 3.14j
31 10.j
30 10.j
32 10j
31 10j
33 .001j
32 .001j
34 1e100j
33 1e100j
35 3.14e-10j
34 3.14e-10j
36
35
37
36
38 # String Literals
37 # String Literals
39 'For\''
38 'For\''
40 "God\""
39 "God\""
41 """so loved
40 """so loved
42 the world"""
41 the world"""
43 '''that he gave
42 '''that he gave
44 his only begotten\' '''
43 his only begotten\' '''
45 'that whosoever believeth \
44 'that whosoever believeth \
46 in him'
45 in him'
47 ''
46 ''
48
47
49 # Identifiers
48 # Identifiers
50 __a__
49 __a__
51 a.b
50 a.b
52 a.b.c
51 a.b.c
53
52
54 # Operators
53 # Operators
55 + - * / % & | ^ ~ < >
54 + - * / % & | ^ ~ < >
56 == != <= >= <> << >> // **
55 == != <= >= <> << >> // **
57 and or not in is
56 and or not in is
58
57
59 # Delimiters
58 # Delimiters
60 () [] {} , : ` = ; @ . # Note that @ and . require the proper context.
59 () [] {} , : ` = ; @ . # Note that @ and . require the proper context.
61 += -= *= /= %= &= |= ^=
60 += -= *= /= %= &= |= ^=
62 //= >>= <<= **=
61 //= >>= <<= **=
63
62
64 # Keywords
63 # Keywords
65 as assert break class continue def del elif else except
64 as assert break class continue def del elif else except
66 finally for from global if import lambda pass raise
65 finally for from global if import lambda pass raise
67 return try while with yield
66 return try while with yield
68
67
69 # Python 2 Keywords (otherwise Identifiers)
68 # Python 2 Keywords (otherwise Identifiers)
70 exec print
69 exec print
71
70
72 # Python 3 Keywords (otherwise Identifiers)
71 # Python 3 Keywords (otherwise Identifiers)
73 nonlocal
72 nonlocal
74
73
75 # Types
74 # Types
76 bool classmethod complex dict enumerate float frozenset int list object
75 bool classmethod complex dict enumerate float frozenset int list object
77 property reversed set slice staticmethod str super tuple type
76 property reversed set slice staticmethod str super tuple type
78
77
79 # Python 2 Types (otherwise Identifiers)
78 # Python 2 Types (otherwise Identifiers)
80 basestring buffer file long unicode xrange
79 basestring buffer file long unicode xrange
81
80
82 # Python 3 Types (otherwise Identifiers)
81 # Python 3 Types (otherwise Identifiers)
83 bytearray bytes filter map memoryview open range zip
82 bytearray bytes filter map memoryview open range zip
84
83
85 # Some Example code
84 # Some Example code
86 import os
85 import os
87 from package import ParentClass
86 from package import ParentClass
88
87
89 @nonsenseDecorator
88 @nonsenseDecorator
90 def doesNothing():
89 def doesNothing():
91 pass
90 pass
92
91
93 class ExampleClass(ParentClass):
92 class ExampleClass(ParentClass):
94 @staticmethod
93 @staticmethod
95 def example(inputStr):
94 def example(inputStr):
96 a = list(inputStr)
95 a = list(inputStr)
97 a.reverse()
96 a.reverse()
98 return ''.join(a)
97 return ''.join(a)
99
98
100 def __init__(self, mixin = 'Hello'):
99 def __init__(self, mixin = 'Hello'):
101 self.mixin = mixin
100 self.mixin = mixin
102
101
103 </textarea></div>
102 </textarea></div>
104 <script>
103 <script>
105 var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
104 var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
106 mode: {name: "python",
105 mode: {name: "python",
107 version: 2,
106 version: 2,
108 singleLineStringErrors: false},
107 singleLineStringErrors: false},
109 lineNumbers: true,
108 lineNumbers: true,
110 indentUnit: 4,
109 indentUnit: 4,
111 tabMode: "shift",
110 tabMode: "shift",
112 matchBrackets: true
111 matchBrackets: true
113 });
112 });
114 </script>
113 </script>
115 <h2>Configuration Options:</h2>
114 <h2>Configuration Options:</h2>
116 <ul>
115 <ul>
117 <li>version - 2/3 - The version of Python to recognize. Default is 2.</li>
116 <li>version - 2/3 - The version of Python to recognize. Default is 2.</li>
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>
117 <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 </ul>
118 </ul>
120
119
121 <p><strong>MIME types defined:</strong> <code>text/x-python</code>.</p>
120 <p><strong>MIME types defined:</strong> <code>text/x-python</code>.</p>
122 </body>
121 </body>
123 </html>
122 </html>
@@ -1,324 +1,333 b''
1 CodeMirror.defineMode("python", function(conf, parserConf) {
1 CodeMirror.defineMode("python", function(conf, parserConf) {
2 var ERRORCLASS = 'error';
2 var ERRORCLASS = 'error';
3
3
4 function wordRegexp(words) {
4 function wordRegexp(words) {
5 return new RegExp("^((" + words.join(")|(") + "))\\b");
5 return new RegExp("^((" + words.join(")|(") + "))\\b");
6 }
6 }
7
7
8 // IPython-specific changes: add '?' as recognized character.
9 //var singleOperators = new RegExp("^[\\+\\-\\*/%&|\\^~<>!]");
10 var singleOperators = new RegExp("^[\\+\\-\\*/%&|\\^~<>!\\?]");
8 var singleOperators = new RegExp("^[\\+\\-\\*/%&|\\^~<>!\\?]");
11 // End IPython changes.
12
13 var singleDelimiters = new RegExp('^[\\(\\)\\[\\]\\{\\}@,:`=;\\.]');
9 var singleDelimiters = new RegExp('^[\\(\\)\\[\\]\\{\\}@,:`=;\\.]');
14 var doubleOperators = new RegExp("^((==)|(!=)|(<=)|(>=)|(<>)|(<<)|(>>)|(//)|(\\*\\*))");
10 var doubleOperators = new RegExp("^((==)|(!=)|(<=)|(>=)|(<>)|(<<)|(>>)|(//)|(\\*\\*))");
15 var doubleDelimiters = new RegExp("^((\\+=)|(\\-=)|(\\*=)|(%=)|(/=)|(&=)|(\\|=)|(\\^=))");
11 var doubleDelimiters = new RegExp("^((\\+=)|(\\-=)|(\\*=)|(%=)|(/=)|(&=)|(\\|=)|(\\^=))");
16 var tripleDelimiters = new RegExp("^((//=)|(>>=)|(<<=)|(\\*\\*=))");
12 var tripleDelimiters = new RegExp("^((//=)|(>>=)|(<<=)|(\\*\\*=))");
17 var identifiers = new RegExp("^[_A-Za-z][_A-Za-z0-9]*");
13 var identifiers = new RegExp("^[_A-Za-z][_A-Za-z0-9]*");
18
14
19 var wordOperators = wordRegexp(['and', 'or', 'not', 'is', 'in']);
15 var wordOperators = wordRegexp(['and', 'or', 'not', 'is', 'in']);
20 var commonkeywords = ['as', 'assert', 'break', 'class', 'continue',
16 var commonkeywords = ['as', 'assert', 'break', 'class', 'continue',
21 'def', 'del', 'elif', 'else', 'except', 'finally',
17 'def', 'del', 'elif', 'else', 'except', 'finally',
22 'for', 'from', 'global', 'if', 'import',
18 'for', 'from', 'global', 'if', 'import',
23 'lambda', 'pass', 'raise', 'return',
19 'lambda', 'pass', 'raise', 'return',
24 'try', 'while', 'with', 'yield'];
20 'try', 'while', 'with', 'yield'];
25 var commontypes = ['bool', 'classmethod', 'complex', 'dict', 'enumerate',
21 var commontypes = ['bool', 'classmethod', 'complex', 'dict', 'enumerate',
26 'float', 'frozenset', 'int', 'list', 'object',
22 'float', 'frozenset', 'int', 'list', 'object',
27 'property', 'reversed', 'set', 'slice', 'staticmethod',
23 'property', 'reversed', 'set', 'slice', 'staticmethod',
28 'str', 'super', 'tuple', 'type'];
24 'str', 'super', 'tuple', 'type'];
29 var py2 = {'types': ['basestring', 'buffer', 'file', 'long', 'unicode',
25 var py2 = {'types': ['basestring', 'buffer', 'file', 'long', 'unicode',
30 'xrange'],
26 'xrange'],
31 'keywords': ['exec', 'print']};
27 'keywords': ['exec', 'print']};
32 var py3 = {'types': ['bytearray', 'bytes', 'filter', 'map', 'memoryview',
28 var py3 = {'types': ['bytearray', 'bytes', 'filter', 'map', 'memoryview',
33 'open', 'range', 'zip'],
29 'open', 'range', 'zip'],
34 'keywords': ['nonlocal']};
30 'keywords': ['nonlocal']};
35
31
36 if (!!parserConf.version && parseInt(parserConf.version, 10) === 3) {
32 if (!!parserConf.version && parseInt(parserConf.version, 10) === 3) {
37 commonkeywords = commonkeywords.concat(py3.keywords);
33 commonkeywords = commonkeywords.concat(py3.keywords);
38 commontypes = commontypes.concat(py3.types);
34 commontypes = commontypes.concat(py3.types);
39 var stringPrefixes = new RegExp("^(([rb]|(br))?('{3}|\"{3}|['\"]))", "i");
35 var stringPrefixes = new RegExp("^(([rb]|(br))?('{3}|\"{3}|['\"]))", "i");
40 } else {
36 } else {
41 commonkeywords = commonkeywords.concat(py2.keywords);
37 commonkeywords = commonkeywords.concat(py2.keywords);
42 commontypes = commontypes.concat(py2.types);
38 commontypes = commontypes.concat(py2.types);
43 var stringPrefixes = new RegExp("^(([rub]|(ur)|(br))?('{3}|\"{3}|['\"]))", "i");
39 var stringPrefixes = new RegExp("^(([rub]|(ur)|(br))?('{3}|\"{3}|['\"]))", "i");
44 }
40 }
45 var keywords = wordRegexp(commonkeywords);
41 var keywords = wordRegexp(commonkeywords);
46 var types = wordRegexp(commontypes);
42 var types = wordRegexp(commontypes);
47
43
48 var indentInfo = null;
44 var indentInfo = null;
49
45
50 // tokenizers
46 // tokenizers
51 function tokenBase(stream, state) {
47 function tokenBase(stream, state) {
52 // Handle scope changes
48 // Handle scope changes
53 if (stream.sol()) {
49 if (stream.sol()) {
54 var scopeOffset = state.scopes[0].offset;
50 var scopeOffset = state.scopes[0].offset;
55 if (stream.eatSpace()) {
51 if (stream.eatSpace()) {
56 var lineOffset = stream.indentation();
52 var lineOffset = stream.indentation();
57 if (lineOffset > scopeOffset) {
53 if (lineOffset > scopeOffset) {
58 indentInfo = 'indent';
54 indentInfo = 'indent';
59 } else if (lineOffset < scopeOffset) {
55 } else if (lineOffset < scopeOffset) {
60 indentInfo = 'dedent';
56 indentInfo = 'dedent';
61 }
57 }
62 return null;
58 return null;
63 } else {
59 } else {
64 if (scopeOffset > 0) {
60 if (scopeOffset > 0) {
65 dedent(stream, state);
61 dedent(stream, state);
66 }
62 }
67 }
63 }
68 }
64 }
69 if (stream.eatSpace()) {
65 if (stream.eatSpace()) {
70 return null;
66 return null;
71 }
67 }
72
68
73 var ch = stream.peek();
69 var ch = stream.peek();
74
70
75 // Handle Comments
71 // Handle Comments
76 if (ch === '#') {
72 if (ch === '#') {
77 stream.skipToEnd();
73 stream.skipToEnd();
78 return 'comment';
74 return 'comment';
79 }
75 }
80
76
81 // Handle Number Literals
77 // Handle Number Literals
82 if (stream.match(/^[0-9\.]/, false)) {
78 if (stream.match(/^[0-9\.]/, false)) {
83 var floatLiteral = false;
79 var floatLiteral = false;
84 // Floats
80 // Floats
85 if (stream.match(/^\d*\.\d+(e[\+\-]?\d+)?/i)) { floatLiteral = true; }
81 if (stream.match(/^\d*\.\d+(e[\+\-]?\d+)?/i)) { floatLiteral = true; }
86 if (stream.match(/^\d+\.\d*/)) { floatLiteral = true; }
82 if (stream.match(/^\d+\.\d*/)) { floatLiteral = true; }
87 if (stream.match(/^\.\d+/)) { floatLiteral = true; }
83 if (stream.match(/^\.\d+/)) { floatLiteral = true; }
88 if (floatLiteral) {
84 if (floatLiteral) {
89 // Float literals may be "imaginary"
85 // Float literals may be "imaginary"
90 stream.eat(/J/i);
86 stream.eat(/J/i);
91 return 'number';
87 return 'number';
92 }
88 }
93 // Integers
89 // Integers
94 var intLiteral = false;
90 var intLiteral = false;
95 // Hex
91 // Hex
96 if (stream.match(/^0x[0-9a-f]+/i)) { intLiteral = true; }
92 if (stream.match(/^0x[0-9a-f]+/i)) { intLiteral = true; }
97 // Binary
93 // Binary
98 if (stream.match(/^0b[01]+/i)) { intLiteral = true; }
94 if (stream.match(/^0b[01]+/i)) { intLiteral = true; }
99 // Octal
95 // Octal
100 if (stream.match(/^0o[0-7]+/i)) { intLiteral = true; }
96 if (stream.match(/^0o[0-7]+/i)) { intLiteral = true; }
101 // Decimal
97 // Decimal
102 if (stream.match(/^[1-9]\d*(e[\+\-]?\d+)?/)) {
98 if (stream.match(/^[1-9]\d*(e[\+\-]?\d+)?/)) {
103 // Decimal literals may be "imaginary"
99 // Decimal literals may be "imaginary"
104 stream.eat(/J/i);
100 stream.eat(/J/i);
105 // TODO - Can you have imaginary longs?
101 // TODO - Can you have imaginary longs?
106 intLiteral = true;
102 intLiteral = true;
107 }
103 }
108 // Zero by itself with no other piece of number.
104 // Zero by itself with no other piece of number.
109 if (stream.match(/^0(?![\dx])/i)) { intLiteral = true; }
105 if (stream.match(/^0(?![\dx])/i)) { intLiteral = true; }
110 if (intLiteral) {
106 if (intLiteral) {
111 // Integer literals may be "long"
107 // Integer literals may be "long"
112 stream.eat(/L/i);
108 stream.eat(/L/i);
113 return 'number';
109 return 'number';
114 }
110 }
115 }
111 }
116
112
117 // Handle Strings
113 // Handle Strings
118 if (stream.match(stringPrefixes)) {
114 if (stream.match(stringPrefixes)) {
119 state.tokenize = tokenStringFactory(stream.current());
115 state.tokenize = tokenStringFactory(stream.current());
120 return state.tokenize(stream, state);
116 return state.tokenize(stream, state);
121 }
117 }
122
118
123 // Handle operators and Delimiters
119 // Handle operators and Delimiters
124 if (stream.match(tripleDelimiters) || stream.match(doubleDelimiters)) {
120 if (stream.match(tripleDelimiters) || stream.match(doubleDelimiters)) {
125 return null;
121 return null;
126 }
122 }
127 if (stream.match(doubleOperators)
123 if (stream.match(doubleOperators)
128 || stream.match(singleOperators)
124 || stream.match(singleOperators)
129 || stream.match(wordOperators)) {
125 || stream.match(wordOperators)) {
130 return 'operator';
126 return 'operator';
131 }
127 }
132 if (stream.match(singleDelimiters)) {
128 if (stream.match(singleDelimiters)) {
133 return null;
129 return null;
134 }
130 }
135
131
136 if (stream.match(types)) {
132 if (stream.match(types)) {
137 return 'builtin';
133 return 'builtin';
138 }
134 }
139
135
140 if (stream.match(keywords)) {
136 if (stream.match(keywords)) {
141 return 'keyword';
137 return 'keyword';
142 }
138 }
143
139
144 if (stream.match(identifiers)) {
140 if (stream.match(identifiers)) {
145 return 'variable';
141 return 'variable';
146 }
142 }
147
143
148 // Handle non-detected items
144 // Handle non-detected items
149 stream.next();
145 stream.next();
150 return ERRORCLASS;
146 return ERRORCLASS;
151 }
147 }
152
148
153 function tokenStringFactory(delimiter) {
149 function tokenStringFactory(delimiter) {
154 while ('rub'.indexOf(delimiter.charAt(0).toLowerCase()) >= 0) {
150 while ('rub'.indexOf(delimiter.charAt(0).toLowerCase()) >= 0) {
155 delimiter = delimiter.substr(1);
151 delimiter = delimiter.substr(1);
156 }
152 }
157 var singleline = delimiter.length == 1;
153 var singleline = delimiter.length == 1;
158 var OUTCLASS = 'string';
154 var OUTCLASS = 'string';
159
155
160 return function tokenString(stream, state) {
156 return function tokenString(stream, state) {
161 while (!stream.eol()) {
157 while (!stream.eol()) {
162 stream.eatWhile(/[^'"\\]/);
158 stream.eatWhile(/[^'"\\]/);
163 if (stream.eat('\\')) {
159 if (stream.eat('\\')) {
164 stream.next();
160 stream.next();
165 if (singleline && stream.eol()) {
161 if (singleline && stream.eol()) {
166 return OUTCLASS;
162 return OUTCLASS;
167 }
163 }
168 } else if (stream.match(delimiter)) {
164 } else if (stream.match(delimiter)) {
169 state.tokenize = tokenBase;
165 state.tokenize = tokenBase;
170 return OUTCLASS;
166 return OUTCLASS;
171 } else {
167 } else {
172 stream.eat(/['"]/);
168 stream.eat(/['"]/);
173 }
169 }
174 }
170 }
175 if (singleline) {
171 if (singleline) {
176 if (parserConf.singleLineStringErrors) {
172 if (parserConf.singleLineStringErrors) {
177 return ERRORCLASS;
173 return ERRORCLASS;
178 } else {
174 } else {
179 state.tokenize = tokenBase;
175 state.tokenize = tokenBase;
180 }
176 }
181 }
177 }
182 return OUTCLASS;
178 return OUTCLASS;
183 };
179 };
184 }
180 }
185
181
186 function indent(stream, state, type) {
182 function indent(stream, state, type) {
187 type = type || 'py';
183 type = type || 'py';
188 var indentUnit = 0;
184 var indentUnit = 0;
189 if (type === 'py') {
185 if (type === 'py') {
186 if (state.scopes[0].type !== 'py') {
187 state.scopes[0].offset = stream.indentation();
188 return;
189 }
190 for (var i = 0; i < state.scopes.length; ++i) {
190 for (var i = 0; i < state.scopes.length; ++i) {
191 if (state.scopes[i].type === 'py') {
191 if (state.scopes[i].type === 'py') {
192 indentUnit = state.scopes[i].offset + conf.indentUnit;
192 indentUnit = state.scopes[i].offset + conf.indentUnit;
193 break;
193 break;
194 }
194 }
195 }
195 }
196 } else {
196 } else {
197 indentUnit = stream.column() + stream.current().length;
197 indentUnit = stream.column() + stream.current().length;
198 }
198 }
199 state.scopes.unshift({
199 state.scopes.unshift({
200 offset: indentUnit,
200 offset: indentUnit,
201 type: type
201 type: type
202 });
202 });
203 }
203 }
204
204
205 function dedent(stream, state) {
205 function dedent(stream, state, type) {
206 type = type || 'py';
206 if (state.scopes.length == 1) return;
207 if (state.scopes.length == 1) return;
207 if (state.scopes[0].type === 'py') {
208 if (state.scopes[0].type === 'py') {
208 var _indent = stream.indentation();
209 var _indent = stream.indentation();
209 var _indent_index = -1;
210 var _indent_index = -1;
210 for (var i = 0; i < state.scopes.length; ++i) {
211 for (var i = 0; i < state.scopes.length; ++i) {
211 if (_indent === state.scopes[i].offset) {
212 if (_indent === state.scopes[i].offset) {
212 _indent_index = i;
213 _indent_index = i;
213 break;
214 break;
214 }
215 }
215 }
216 }
216 if (_indent_index === -1) {
217 if (_indent_index === -1) {
217 return true;
218 return true;
218 }
219 }
219 while (state.scopes[0].offset !== _indent) {
220 while (state.scopes[0].offset !== _indent) {
220 state.scopes.shift();
221 state.scopes.shift();
221 }
222 }
222 return false
223 return false
223 } else {
224 } else {
225 if (type === 'py') {
226 state.scopes[0].offset = stream.indentation();
227 return false;
228 } else {
229 if (state.scopes[0].type != type) {
230 return true;
231 }
224 state.scopes.shift();
232 state.scopes.shift();
225 return false;
233 return false;
226 }
234 }
227 }
235 }
236 }
228
237
229 function tokenLexer(stream, state) {
238 function tokenLexer(stream, state) {
230 indentInfo = null;
239 indentInfo = null;
231 var style = state.tokenize(stream, state);
240 var style = state.tokenize(stream, state);
232 var current = stream.current();
241 var current = stream.current();
233
242
234 // Handle '.' connected identifiers
243 // Handle '.' connected identifiers
235 if (current === '.') {
244 if (current === '.') {
236 style = state.tokenize(stream, state);
245 style = state.tokenize(stream, state);
237 current = stream.current();
246 current = stream.current();
238 if (style === 'variable') {
247 if (style === 'variable') {
239 return 'variable';
248 return 'variable';
240 } else {
249 } else {
241 return ERRORCLASS;
250 return ERRORCLASS;
242 }
251 }
243 }
252 }
244
253
245 // Handle decorators
254 // Handle decorators
246 if (current === '@') {
255 if (current === '@') {
247 style = state.tokenize(stream, state);
256 style = state.tokenize(stream, state);
248 current = stream.current();
257 current = stream.current();
249 if (style === 'variable'
258 if (style === 'variable'
250 || current === '@staticmethod'
259 || current === '@staticmethod'
251 || current === '@classmethod') {
260 || current === '@classmethod') {
252 return 'meta';
261 return 'meta';
253 } else {
262 } else {
254 return ERRORCLASS;
263 return ERRORCLASS;
255 }
264 }
256 }
265 }
257
266
258 // Handle scope changes.
267 // Handle scope changes.
259 if (current === 'pass' || current === 'return') {
268 if (current === 'pass' || current === 'return') {
260 state.dedent += 1;
269 state.dedent += 1;
261 }
270 }
262 if ((current === ':' && !state.lambda && state.scopes[0].type == 'py')
271 if ((current === ':' && !state.lambda && state.scopes[0].type == 'py')
263 || indentInfo === 'indent') {
272 || indentInfo === 'indent') {
264 indent(stream, state);
273 indent(stream, state);
265 }
274 }
266 var delimiter_index = '[({'.indexOf(current);
275 var delimiter_index = '[({'.indexOf(current);
267 if (delimiter_index !== -1) {
276 if (delimiter_index !== -1) {
268 indent(stream, state, '])}'.slice(delimiter_index, delimiter_index+1));
277 indent(stream, state, '])}'.slice(delimiter_index, delimiter_index+1));
269 }
278 }
270 if (indentInfo === 'dedent') {
279 if (indentInfo === 'dedent') {
271 if (dedent(stream, state)) {
280 if (dedent(stream, state)) {
272 return ERRORCLASS;
281 return ERRORCLASS;
273 }
282 }
274 }
283 }
275 delimiter_index = '])}'.indexOf(current);
284 delimiter_index = '])}'.indexOf(current);
276 if (delimiter_index !== -1) {
285 if (delimiter_index !== -1) {
277 if (dedent(stream, state)) {
286 if (dedent(stream, state, current)) {
278 return ERRORCLASS;
287 return ERRORCLASS;
279 }
288 }
280 }
289 }
281 if (state.dedent > 0 && stream.eol() && state.scopes[0].type == 'py') {
290 if (state.dedent > 0 && stream.eol() && state.scopes[0].type == 'py') {
282 if (state.scopes.length > 1) state.scopes.shift();
291 if (state.scopes.length > 1) state.scopes.shift();
283 state.dedent -= 1;
292 state.dedent -= 1;
284 }
293 }
285
294
286 return style;
295 return style;
287 }
296 }
288
297
289 var external = {
298 var external = {
290 startState: function(basecolumn) {
299 startState: function(basecolumn) {
291 return {
300 return {
292 tokenize: tokenBase,
301 tokenize: tokenBase,
293 scopes: [{offset:basecolumn || 0, type:'py'}],
302 scopes: [{offset:basecolumn || 0, type:'py'}],
294 lastToken: null,
303 lastToken: null,
295 lambda: false,
304 lambda: false,
296 dedent: 0
305 dedent: 0
297 };
306 };
298 },
307 },
299
308
300 token: function(stream, state) {
309 token: function(stream, state) {
301 var style = tokenLexer(stream, state);
310 var style = tokenLexer(stream, state);
302
311
303 state.lastToken = {style:style, content: stream.current()};
312 state.lastToken = {style:style, content: stream.current()};
304
313
305 if (stream.eol() && stream.lambda) {
314 if (stream.eol() && stream.lambda) {
306 state.lambda = false;
315 state.lambda = false;
307 }
316 }
308
317
309 return style;
318 return style;
310 },
319 },
311
320
312 indent: function(state, textAfter) {
321 indent: function(state, textAfter) {
313 if (state.tokenize != tokenBase) {
322 if (state.tokenize != tokenBase) {
314 return 0;
323 return 0;
315 }
324 }
316
325
317 return state.scopes[0].offset;
326 return state.scopes[0].offset;
318 }
327 }
319
328
320 };
329 };
321 return external;
330 return external;
322 });
331 });
323
332
324 CodeMirror.defineMIME("text/x-python", "python");
333 CodeMirror.defineMIME("text/x-python", "python");
@@ -1,525 +1,525 b''
1 <!doctype html>
1 <!doctype html>
2 <html>
2 <html>
3 <head>
3 <head>
4 <title>CodeMirror 2: reStructuredText mode</title>
4 <title>CodeMirror: reStructuredText mode</title>
5 <link rel="stylesheet" href="../../lib/codemirror.css">
5 <link rel="stylesheet" href="../../lib/codemirror.css">
6 <script src="../../lib/codemirror.js"></script>
6 <script src="../../lib/codemirror.js"></script>
7 <script src="rst.js"></script>
7 <script src="rst.js"></script>
8 <link rel="stylesheet" href="rst.css">
9 <style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
8 <style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
10 <link rel="stylesheet" href="../../css/docs.css">
9 <link rel="stylesheet" href="../../doc/docs.css">
11 </head>
10 </head>
12 <body>
11 <body>
13 <h1>CodeMirror 2: reStructuredText mode</h1>
12 <h1>CodeMirror: reStructuredText mode</h1>
14
13
15 <form><textarea id="code" name="code">
14 <form><textarea id="code" name="code">
16 .. This is an excerpt from Sphinx documentation: http://sphinx.pocoo.org/_sources/rest.txt
15 .. This is an excerpt from Sphinx documentation: http://sphinx.pocoo.org/_sources/rest.txt
17
16
18 .. highlightlang:: rest
17 .. highlightlang:: rest
19
18
20 .. _rst-primer:
19 .. _rst-primer:
21
20
22 reStructuredText Primer
21 reStructuredText Primer
23 =======================
22 =======================
24
23
25 This section is a brief introduction to reStructuredText (reST) concepts and
24 This section is a brief introduction to reStructuredText (reST) concepts and
26 syntax, intended to provide authors with enough information to author documents
25 syntax, intended to provide authors with enough information to author documents
27 productively. Since reST was designed to be a simple, unobtrusive markup
26 productively. Since reST was designed to be a simple, unobtrusive markup
28 language, this will not take too long.
27 language, this will not take too long.
29
28
30 .. seealso::
29 .. seealso::
31
30
32 The authoritative `reStructuredText User Documentation
31 The authoritative `reStructuredText User Documentation
33 &lt;http://docutils.sourceforge.net/rst.html&gt;`_. The "ref" links in this
32 &lt;http://docutils.sourceforge.net/rst.html&gt;`_. The "ref" links in this
34 document link to the description of the individual constructs in the reST
33 document link to the description of the individual constructs in the reST
35 reference.
34 reference.
36
35
37
36
38 Paragraphs
37 Paragraphs
39 ----------
38 ----------
40
39
41 The paragraph (:duref:`ref &lt;paragraphs&gt;`) is the most basic block in a reST
40 The paragraph (:duref:`ref &lt;paragraphs&gt;`) is the most basic block in a reST
42 document. Paragraphs are simply chunks of text separated by one or more blank
41 document. Paragraphs are simply chunks of text separated by one or more blank
43 lines. As in Python, indentation is significant in reST, so all lines of the
42 lines. As in Python, indentation is significant in reST, so all lines of the
44 same paragraph must be left-aligned to the same level of indentation.
43 same paragraph must be left-aligned to the same level of indentation.
45
44
46
45
47 .. _inlinemarkup:
46 .. _inlinemarkup:
48
47
49 Inline markup
48 Inline markup
50 -------------
49 -------------
51
50
52 The standard reST inline markup is quite simple: use
51 The standard reST inline markup is quite simple: use
53
52
54 * one asterisk: ``*text*`` for emphasis (italics),
53 * one asterisk: ``*text*`` for emphasis (italics),
55 * two asterisks: ``**text**`` for strong emphasis (boldface), and
54 * two asterisks: ``**text**`` for strong emphasis (boldface), and
56 * backquotes: ````text```` for code samples.
55 * backquotes: ````text```` for code samples.
57
56
58 If asterisks or backquotes appear in running text and could be confused with
57 If asterisks or backquotes appear in running text and could be confused with
59 inline markup delimiters, they have to be escaped with a backslash.
58 inline markup delimiters, they have to be escaped with a backslash.
60
59
61 Be aware of some restrictions of this markup:
60 Be aware of some restrictions of this markup:
62
61
63 * it may not be nested,
62 * it may not be nested,
64 * content may not start or end with whitespace: ``* text*`` is wrong,
63 * content may not start or end with whitespace: ``* text*`` is wrong,
65 * it must be separated from surrounding text by non-word characters. Use a
64 * it must be separated from surrounding text by non-word characters. Use a
66 backslash escaped space to work around that: ``thisis\ *one*\ word``.
65 backslash escaped space to work around that: ``thisis\ *one*\ word``.
67
66
68 These restrictions may be lifted in future versions of the docutils.
67 These restrictions may be lifted in future versions of the docutils.
69
68
70 reST also allows for custom "interpreted text roles"', which signify that the
69 reST also allows for custom "interpreted text roles"', which signify that the
71 enclosed text should be interpreted in a specific way. Sphinx uses this to
70 enclosed text should be interpreted in a specific way. Sphinx uses this to
72 provide semantic markup and cross-referencing of identifiers, as described in
71 provide semantic markup and cross-referencing of identifiers, as described in
73 the appropriate section. The general syntax is ``:rolename:`content```.
72 the appropriate section. The general syntax is ``:rolename:`content```.
74
73
75 Standard reST provides the following roles:
74 Standard reST provides the following roles:
76
75
77 * :durole:`emphasis` -- alternate spelling for ``*emphasis*``
76 * :durole:`emphasis` -- alternate spelling for ``*emphasis*``
78 * :durole:`strong` -- alternate spelling for ``**strong**``
77 * :durole:`strong` -- alternate spelling for ``**strong**``
79 * :durole:`literal` -- alternate spelling for ````literal````
78 * :durole:`literal` -- alternate spelling for ````literal````
80 * :durole:`subscript` -- subscript text
79 * :durole:`subscript` -- subscript text
81 * :durole:`superscript` -- superscript text
80 * :durole:`superscript` -- superscript text
82 * :durole:`title-reference` -- for titles of books, periodicals, and other
81 * :durole:`title-reference` -- for titles of books, periodicals, and other
83 materials
82 materials
84
83
85 See :ref:`inline-markup` for roles added by Sphinx.
84 See :ref:`inline-markup` for roles added by Sphinx.
86
85
87
86
88 Lists and Quote-like blocks
87 Lists and Quote-like blocks
89 ---------------------------
88 ---------------------------
90
89
91 List markup (:duref:`ref &lt;bullet-lists&gt;`) is natural: just place an asterisk at
90 List markup (:duref:`ref &lt;bullet-lists&gt;`) is natural: just place an asterisk at
92 the start of a paragraph and indent properly. The same goes for numbered lists;
91 the start of a paragraph and indent properly. The same goes for numbered lists;
93 they can also be autonumbered using a ``#`` sign::
92 they can also be autonumbered using a ``#`` sign::
94
93
95 * This is a bulleted list.
94 * This is a bulleted list.
96 * It has two items, the second
95 * It has two items, the second
97 item uses two lines.
96 item uses two lines.
98
97
99 1. This is a numbered list.
98 1. This is a numbered list.
100 2. It has two items too.
99 2. It has two items too.
101
100
102 #. This is a numbered list.
101 #. This is a numbered list.
103 #. It has two items too.
102 #. It has two items too.
104
103
105
104
106 Nested lists are possible, but be aware that they must be separated from the
105 Nested lists are possible, but be aware that they must be separated from the
107 parent list items by blank lines::
106 parent list items by blank lines::
108
107
109 * this is
108 * this is
110 * a list
109 * a list
111
110
112 * with a nested list
111 * with a nested list
113 * and some subitems
112 * and some subitems
114
113
115 * and here the parent list continues
114 * and here the parent list continues
116
115
117 Definition lists (:duref:`ref &lt;definition-lists&gt;`) are created as follows::
116 Definition lists (:duref:`ref &lt;definition-lists&gt;`) are created as follows::
118
117
119 term (up to a line of text)
118 term (up to a line of text)
120 Definition of the term, which must be indented
119 Definition of the term, which must be indented
121
120
122 and can even consist of multiple paragraphs
121 and can even consist of multiple paragraphs
123
122
124 next term
123 next term
125 Description.
124 Description.
126
125
127 Note that the term cannot have more than one line of text.
126 Note that the term cannot have more than one line of text.
128
127
129 Quoted paragraphs (:duref:`ref &lt;block-quotes&gt;`) are created by just indenting
128 Quoted paragraphs (:duref:`ref &lt;block-quotes&gt;`) are created by just indenting
130 them more than the surrounding paragraphs.
129 them more than the surrounding paragraphs.
131
130
132 Line blocks (:duref:`ref &lt;line-blocks&gt;`) are a way of preserving line breaks::
131 Line blocks (:duref:`ref &lt;line-blocks&gt;`) are a way of preserving line breaks::
133
132
134 | These lines are
133 | These lines are
135 | broken exactly like in
134 | broken exactly like in
136 | the source file.
135 | the source file.
137
136
138 There are also several more special blocks available:
137 There are also several more special blocks available:
139
138
140 * field lists (:duref:`ref &lt;field-lists&gt;`)
139 * field lists (:duref:`ref &lt;field-lists&gt;`)
141 * option lists (:duref:`ref &lt;option-lists&gt;`)
140 * option lists (:duref:`ref &lt;option-lists&gt;`)
142 * quoted literal blocks (:duref:`ref &lt;quoted-literal-blocks&gt;`)
141 * quoted literal blocks (:duref:`ref &lt;quoted-literal-blocks&gt;`)
143 * doctest blocks (:duref:`ref &lt;doctest-blocks&gt;`)
142 * doctest blocks (:duref:`ref &lt;doctest-blocks&gt;`)
144
143
145
144
146 Source Code
145 Source Code
147 -----------
146 -----------
148
147
149 Literal code blocks (:duref:`ref &lt;literal-blocks&gt;`) are introduced by ending a
148 Literal code blocks (:duref:`ref &lt;literal-blocks&gt;`) are introduced by ending a
150 paragraph with the special marker ``::``. The literal block must be indented
149 paragraph with the special marker ``::``. The literal block must be indented
151 (and, like all paragraphs, separated from the surrounding ones by blank lines)::
150 (and, like all paragraphs, separated from the surrounding ones by blank lines)::
152
151
153 This is a normal text paragraph. The next paragraph is a code sample::
152 This is a normal text paragraph. The next paragraph is a code sample::
154
153
155 It is not processed in any way, except
154 It is not processed in any way, except
156 that the indentation is removed.
155 that the indentation is removed.
157
156
158 It can span multiple lines.
157 It can span multiple lines.
159
158
160 This is a normal text paragraph again.
159 This is a normal text paragraph again.
161
160
162 The handling of the ``::`` marker is smart:
161 The handling of the ``::`` marker is smart:
163
162
164 * If it occurs as a paragraph of its own, that paragraph is completely left
163 * If it occurs as a paragraph of its own, that paragraph is completely left
165 out of the document.
164 out of the document.
166 * If it is preceded by whitespace, the marker is removed.
165 * If it is preceded by whitespace, the marker is removed.
167 * If it is preceded by non-whitespace, the marker is replaced by a single
166 * If it is preceded by non-whitespace, the marker is replaced by a single
168 colon.
167 colon.
169
168
170 That way, the second sentence in the above example's first paragraph would be
169 That way, the second sentence in the above example's first paragraph would be
171 rendered as "The next paragraph is a code sample:".
170 rendered as "The next paragraph is a code sample:".
172
171
173
172
174 .. _rst-tables:
173 .. _rst-tables:
175
174
176 Tables
175 Tables
177 ------
176 ------
178
177
179 Two forms of tables are supported. For *grid tables* (:duref:`ref
178 Two forms of tables are supported. For *grid tables* (:duref:`ref
180 &lt;grid-tables&gt;`), you have to "paint" the cell grid yourself. They look like
179 &lt;grid-tables&gt;`), you have to "paint" the cell grid yourself. They look like
181 this::
180 this::
182
181
183 +------------------------+------------+----------+----------+
182 +------------------------+------------+----------+----------+
184 | Header row, column 1 | Header 2 | Header 3 | Header 4 |
183 | Header row, column 1 | Header 2 | Header 3 | Header 4 |
185 | (header rows optional) | | | |
184 | (header rows optional) | | | |
186 +========================+============+==========+==========+
185 +========================+============+==========+==========+
187 | body row 1, column 1 | column 2 | column 3 | column 4 |
186 | body row 1, column 1 | column 2 | column 3 | column 4 |
188 +------------------------+------------+----------+----------+
187 +------------------------+------------+----------+----------+
189 | body row 2 | ... | ... | |
188 | body row 2 | ... | ... | |
190 +------------------------+------------+----------+----------+
189 +------------------------+------------+----------+----------+
191
190
192 *Simple tables* (:duref:`ref &lt;simple-tables&gt;`) are easier to write, but
191 *Simple tables* (:duref:`ref &lt;simple-tables&gt;`) are easier to write, but
193 limited: they must contain more than one row, and the first column cannot
192 limited: they must contain more than one row, and the first column cannot
194 contain multiple lines. They look like this::
193 contain multiple lines. They look like this::
195
194
196 ===== ===== =======
195 ===== ===== =======
197 A B A and B
196 A B A and B
198 ===== ===== =======
197 ===== ===== =======
199 False False False
198 False False False
200 True False False
199 True False False
201 False True False
200 False True False
202 True True True
201 True True True
203 ===== ===== =======
202 ===== ===== =======
204
203
205
204
206 Hyperlinks
205 Hyperlinks
207 ----------
206 ----------
208
207
209 External links
208 External links
210 ^^^^^^^^^^^^^^
209 ^^^^^^^^^^^^^^
211
210
212 Use ```Link text &lt;http://example.com/&gt;`_`` for inline web links. If the link
211 Use ```Link text &lt;http://example.com/&gt;`_`` for inline web links. If the link
213 text should be the web address, you don't need special markup at all, the parser
212 text should be the web address, you don't need special markup at all, the parser
214 finds links and mail addresses in ordinary text.
213 finds links and mail addresses in ordinary text.
215
214
216 You can also separate the link and the target definition (:duref:`ref
215 You can also separate the link and the target definition (:duref:`ref
217 &lt;hyperlink-targets&gt;`), like this::
216 &lt;hyperlink-targets&gt;`), like this::
218
217
219 This is a paragraph that contains `a link`_.
218 This is a paragraph that contains `a link`_.
220
219
221 .. _a link: http://example.com/
220 .. _a link: http://example.com/
222
221
223
222
224 Internal links
223 Internal links
225 ^^^^^^^^^^^^^^
224 ^^^^^^^^^^^^^^
226
225
227 Internal linking is done via a special reST role provided by Sphinx, see the
226 Internal linking is done via a special reST role provided by Sphinx, see the
228 section on specific markup, :ref:`ref-role`.
227 section on specific markup, :ref:`ref-role`.
229
228
230
229
231 Sections
230 Sections
232 --------
231 --------
233
232
234 Section headers (:duref:`ref &lt;sections&gt;`) are created by underlining (and
233 Section headers (:duref:`ref &lt;sections&gt;`) are created by underlining (and
235 optionally overlining) the section title with a punctuation character, at least
234 optionally overlining) the section title with a punctuation character, at least
236 as long as the text::
235 as long as the text::
237
236
238 =================
237 =================
239 This is a heading
238 This is a heading
240 =================
239 =================
241
240
242 Normally, there are no heading levels assigned to certain characters as the
241 Normally, there are no heading levels assigned to certain characters as the
243 structure is determined from the succession of headings. However, for the
242 structure is determined from the succession of headings. However, for the
244 Python documentation, this convention is used which you may follow:
243 Python documentation, this convention is used which you may follow:
245
244
246 * ``#`` with overline, for parts
245 * ``#`` with overline, for parts
247 * ``*`` with overline, for chapters
246 * ``*`` with overline, for chapters
248 * ``=``, for sections
247 * ``=``, for sections
249 * ``-``, for subsections
248 * ``-``, for subsections
250 * ``^``, for subsubsections
249 * ``^``, for subsubsections
251 * ``"``, for paragraphs
250 * ``"``, for paragraphs
252
251
253 Of course, you are free to use your own marker characters (see the reST
252 Of course, you are free to use your own marker characters (see the reST
254 documentation), and use a deeper nesting level, but keep in mind that most
253 documentation), and use a deeper nesting level, but keep in mind that most
255 target formats (HTML, LaTeX) have a limited supported nesting depth.
254 target formats (HTML, LaTeX) have a limited supported nesting depth.
256
255
257
256
258 Explicit Markup
257 Explicit Markup
259 ---------------
258 ---------------
260
259
261 "Explicit markup" (:duref:`ref &lt;explicit-markup-blocks&gt;`) is used in reST for
260 "Explicit markup" (:duref:`ref &lt;explicit-markup-blocks&gt;`) is used in reST for
262 most constructs that need special handling, such as footnotes,
261 most constructs that need special handling, such as footnotes,
263 specially-highlighted paragraphs, comments, and generic directives.
262 specially-highlighted paragraphs, comments, and generic directives.
264
263
265 An explicit markup block begins with a line starting with ``..`` followed by
264 An explicit markup block begins with a line starting with ``..`` followed by
266 whitespace and is terminated by the next paragraph at the same level of
265 whitespace and is terminated by the next paragraph at the same level of
267 indentation. (There needs to be a blank line between explicit markup and normal
266 indentation. (There needs to be a blank line between explicit markup and normal
268 paragraphs. This may all sound a bit complicated, but it is intuitive enough
267 paragraphs. This may all sound a bit complicated, but it is intuitive enough
269 when you write it.)
268 when you write it.)
270
269
271
270
272 .. _directives:
271 .. _directives:
273
272
274 Directives
273 Directives
275 ----------
274 ----------
276
275
277 A directive (:duref:`ref &lt;directives&gt;`) is a generic block of explicit markup.
276 A directive (:duref:`ref &lt;directives&gt;`) is a generic block of explicit markup.
278 Besides roles, it is one of the extension mechanisms of reST, and Sphinx makes
277 Besides roles, it is one of the extension mechanisms of reST, and Sphinx makes
279 heavy use of it.
278 heavy use of it.
280
279
281 Docutils supports the following directives:
280 Docutils supports the following directives:
282
281
283 * Admonitions: :dudir:`attention`, :dudir:`caution`, :dudir:`danger`,
282 * Admonitions: :dudir:`attention`, :dudir:`caution`, :dudir:`danger`,
284 :dudir:`error`, :dudir:`hint`, :dudir:`important`, :dudir:`note`,
283 :dudir:`error`, :dudir:`hint`, :dudir:`important`, :dudir:`note`,
285 :dudir:`tip`, :dudir:`warning` and the generic :dudir:`admonition`.
284 :dudir:`tip`, :dudir:`warning` and the generic :dudir:`admonition`.
286 (Most themes style only "note" and "warning" specially.)
285 (Most themes style only "note" and "warning" specially.)
287
286
288 * Images:
287 * Images:
289
288
290 - :dudir:`image` (see also Images_ below)
289 - :dudir:`image` (see also Images_ below)
291 - :dudir:`figure` (an image with caption and optional legend)
290 - :dudir:`figure` (an image with caption and optional legend)
292
291
293 * Additional body elements:
292 * Additional body elements:
294
293
295 - :dudir:`contents` (a local, i.e. for the current file only, table of
294 - :dudir:`contents` (a local, i.e. for the current file only, table of
296 contents)
295 contents)
297 - :dudir:`container` (a container with a custom class, useful to generate an
296 - :dudir:`container` (a container with a custom class, useful to generate an
298 outer ``&lt;div&gt;`` in HTML)
297 outer ``&lt;div&gt;`` in HTML)
299 - :dudir:`rubric` (a heading without relation to the document sectioning)
298 - :dudir:`rubric` (a heading without relation to the document sectioning)
300 - :dudir:`topic`, :dudir:`sidebar` (special highlighted body elements)
299 - :dudir:`topic`, :dudir:`sidebar` (special highlighted body elements)
301 - :dudir:`parsed-literal` (literal block that supports inline markup)
300 - :dudir:`parsed-literal` (literal block that supports inline markup)
302 - :dudir:`epigraph` (a block quote with optional attribution line)
301 - :dudir:`epigraph` (a block quote with optional attribution line)
303 - :dudir:`highlights`, :dudir:`pull-quote` (block quotes with their own
302 - :dudir:`highlights`, :dudir:`pull-quote` (block quotes with their own
304 class attribute)
303 class attribute)
305 - :dudir:`compound` (a compound paragraph)
304 - :dudir:`compound` (a compound paragraph)
306
305
307 * Special tables:
306 * Special tables:
308
307
309 - :dudir:`table` (a table with title)
308 - :dudir:`table` (a table with title)
310 - :dudir:`csv-table` (a table generated from comma-separated values)
309 - :dudir:`csv-table` (a table generated from comma-separated values)
311 - :dudir:`list-table` (a table generated from a list of lists)
310 - :dudir:`list-table` (a table generated from a list of lists)
312
311
313 * Special directives:
312 * Special directives:
314
313
315 - :dudir:`raw` (include raw target-format markup)
314 - :dudir:`raw` (include raw target-format markup)
316 - :dudir:`include` (include reStructuredText from another file)
315 - :dudir:`include` (include reStructuredText from another file)
317 -- in Sphinx, when given an absolute include file path, this directive takes
316 -- in Sphinx, when given an absolute include file path, this directive takes
318 it as relative to the source directory
317 it as relative to the source directory
319 - :dudir:`class` (assign a class attribute to the next element) [1]_
318 - :dudir:`class` (assign a class attribute to the next element) [1]_
320
319
321 * HTML specifics:
320 * HTML specifics:
322
321
323 - :dudir:`meta` (generation of HTML ``&lt;meta&gt;`` tags)
322 - :dudir:`meta` (generation of HTML ``&lt;meta&gt;`` tags)
324 - :dudir:`title` (override document title)
323 - :dudir:`title` (override document title)
325
324
326 * Influencing markup:
325 * Influencing markup:
327
326
328 - :dudir:`default-role` (set a new default role)
327 - :dudir:`default-role` (set a new default role)
329 - :dudir:`role` (create a new role)
328 - :dudir:`role` (create a new role)
330
329
331 Since these are only per-file, better use Sphinx' facilities for setting the
330 Since these are only per-file, better use Sphinx' facilities for setting the
332 :confval:`default_role`.
331 :confval:`default_role`.
333
332
334 Do *not* use the directives :dudir:`sectnum`, :dudir:`header` and
333 Do *not* use the directives :dudir:`sectnum`, :dudir:`header` and
335 :dudir:`footer`.
334 :dudir:`footer`.
336
335
337 Directives added by Sphinx are described in :ref:`sphinxmarkup`.
336 Directives added by Sphinx are described in :ref:`sphinxmarkup`.
338
337
339 Basically, a directive consists of a name, arguments, options and content. (Keep
338 Basically, a directive consists of a name, arguments, options and content. (Keep
340 this terminology in mind, it is used in the next chapter describing custom
339 this terminology in mind, it is used in the next chapter describing custom
341 directives.) Looking at this example, ::
340 directives.) Looking at this example, ::
342
341
343 .. function:: foo(x)
342 .. function:: foo(x)
344 foo(y, z)
343 foo(y, z)
345 :module: some.module.name
344 :module: some.module.name
346
345
347 Return a line of text input from the user.
346 Return a line of text input from the user.
348
347
349 ``function`` is the directive name. It is given two arguments here, the
348 ``function`` is the directive name. It is given two arguments here, the
350 remainder of the first line and the second line, as well as one option
349 remainder of the first line and the second line, as well as one option
351 ``module`` (as you can see, options are given in the lines immediately following
350 ``module`` (as you can see, options are given in the lines immediately following
352 the arguments and indicated by the colons). Options must be indented to the
351 the arguments and indicated by the colons). Options must be indented to the
353 same level as the directive content.
352 same level as the directive content.
354
353
355 The directive content follows after a blank line and is indented relative to the
354 The directive content follows after a blank line and is indented relative to the
356 directive start.
355 directive start.
357
356
358
357
359 Images
358 Images
360 ------
359 ------
361
360
362 reST supports an image directive (:dudir:`ref &lt;image&gt;`), used like so::
361 reST supports an image directive (:dudir:`ref &lt;image&gt;`), used like so::
363
362
364 .. image:: gnu.png
363 .. image:: gnu.png
365 (options)
364 (options)
366
365
367 When used within Sphinx, the file name given (here ``gnu.png``) must either be
366 When used within Sphinx, the file name given (here ``gnu.png``) must either be
368 relative to the source file, or absolute which means that they are relative to
367 relative to the source file, or absolute which means that they are relative to
369 the top source directory. For example, the file ``sketch/spam.rst`` could refer
368 the top source directory. For example, the file ``sketch/spam.rst`` could refer
370 to the image ``images/spam.png`` as ``../images/spam.png`` or
369 to the image ``images/spam.png`` as ``../images/spam.png`` or
371 ``/images/spam.png``.
370 ``/images/spam.png``.
372
371
373 Sphinx will automatically copy image files over to a subdirectory of the output
372 Sphinx will automatically copy image files over to a subdirectory of the output
374 directory on building (e.g. the ``_static`` directory for HTML output.)
373 directory on building (e.g. the ``_static`` directory for HTML output.)
375
374
376 Interpretation of image size options (``width`` and ``height``) is as follows:
375 Interpretation of image size options (``width`` and ``height``) is as follows:
377 if the size has no unit or the unit is pixels, the given size will only be
376 if the size has no unit or the unit is pixels, the given size will only be
378 respected for output channels that support pixels (i.e. not in LaTeX output).
377 respected for output channels that support pixels (i.e. not in LaTeX output).
379 Other units (like ``pt`` for points) will be used for HTML and LaTeX output.
378 Other units (like ``pt`` for points) will be used for HTML and LaTeX output.
380
379
381 Sphinx extends the standard docutils behavior by allowing an asterisk for the
380 Sphinx extends the standard docutils behavior by allowing an asterisk for the
382 extension::
381 extension::
383
382
384 .. image:: gnu.*
383 .. image:: gnu.*
385
384
386 Sphinx then searches for all images matching the provided pattern and determines
385 Sphinx then searches for all images matching the provided pattern and determines
387 their type. Each builder then chooses the best image out of these candidates.
386 their type. Each builder then chooses the best image out of these candidates.
388 For instance, if the file name ``gnu.*`` was given and two files :file:`gnu.pdf`
387 For instance, if the file name ``gnu.*`` was given and two files :file:`gnu.pdf`
389 and :file:`gnu.png` existed in the source tree, the LaTeX builder would choose
388 and :file:`gnu.png` existed in the source tree, the LaTeX builder would choose
390 the former, while the HTML builder would prefer the latter.
389 the former, while the HTML builder would prefer the latter.
391
390
392 .. versionchanged:: 0.4
391 .. versionchanged:: 0.4
393 Added the support for file names ending in an asterisk.
392 Added the support for file names ending in an asterisk.
394
393
395 .. versionchanged:: 0.6
394 .. versionchanged:: 0.6
396 Image paths can now be absolute.
395 Image paths can now be absolute.
397
396
398
397
399 Footnotes
398 Footnotes
400 ---------
399 ---------
401
400
402 For footnotes (:duref:`ref &lt;footnotes&gt;`), use ``[#name]_`` to mark the footnote
401 For footnotes (:duref:`ref &lt;footnotes&gt;`), use ``[#name]_`` to mark the footnote
403 location, and add the footnote body at the bottom of the document after a
402 location, and add the footnote body at the bottom of the document after a
404 "Footnotes" rubric heading, like so::
403 "Footnotes" rubric heading, like so::
405
404
406 Lorem ipsum [#f1]_ dolor sit amet ... [#f2]_
405 Lorem ipsum [#f1]_ dolor sit amet ... [#f2]_
407
406
408 .. rubric:: Footnotes
407 .. rubric:: Footnotes
409
408
410 .. [#f1] Text of the first footnote.
409 .. [#f1] Text of the first footnote.
411 .. [#f2] Text of the second footnote.
410 .. [#f2] Text of the second footnote.
412
411
413 You can also explicitly number the footnotes (``[1]_``) or use auto-numbered
412 You can also explicitly number the footnotes (``[1]_``) or use auto-numbered
414 footnotes without names (``[#]_``).
413 footnotes without names (``[#]_``).
415
414
416
415
417 Citations
416 Citations
418 ---------
417 ---------
419
418
420 Standard reST citations (:duref:`ref &lt;citations&gt;`) are supported, with the
419 Standard reST citations (:duref:`ref &lt;citations&gt;`) are supported, with the
421 additional feature that they are "global", i.e. all citations can be referenced
420 additional feature that they are "global", i.e. all citations can be referenced
422 from all files. Use them like so::
421 from all files. Use them like so::
423
422
424 Lorem ipsum [Ref]_ dolor sit amet.
423 Lorem ipsum [Ref]_ dolor sit amet.
425
424
426 .. [Ref] Book or article reference, URL or whatever.
425 .. [Ref] Book or article reference, URL or whatever.
427
426
428 Citation usage is similar to footnote usage, but with a label that is not
427 Citation usage is similar to footnote usage, but with a label that is not
429 numeric or begins with ``#``.
428 numeric or begins with ``#``.
430
429
431
430
432 Substitutions
431 Substitutions
433 -------------
432 -------------
434
433
435 reST supports "substitutions" (:duref:`ref &lt;substitution-definitions&gt;`), which
434 reST supports "substitutions" (:duref:`ref &lt;substitution-definitions&gt;`), which
436 are pieces of text and/or markup referred to in the text by ``|name|``. They
435 are pieces of text and/or markup referred to in the text by ``|name|``. They
437 are defined like footnotes with explicit markup blocks, like this::
436 are defined like footnotes with explicit markup blocks, like this::
438
437
439 .. |name| replace:: replacement *text*
438 .. |name| replace:: replacement *text*
440
439
441 or this::
440 or this::
442
441
443 .. |caution| image:: warning.png
442 .. |caution| image:: warning.png
444 :alt: Warning!
443 :alt: Warning!
445
444
446 See the :duref:`reST reference for substitutions &lt;substitution-definitions&gt;`
445 See the :duref:`reST reference for substitutions &lt;substitution-definitions&gt;`
447 for details.
446 for details.
448
447
449 If you want to use some substitutions for all documents, put them into
448 If you want to use some substitutions for all documents, put them into
450 :confval:`rst_prolog` or put them into a separate file and include it into all
449 :confval:`rst_prolog` or put them into a separate file and include it into all
451 documents you want to use them in, using the :rst:dir:`include` directive. (Be
450 documents you want to use them in, using the :rst:dir:`include` directive. (Be
452 sure to give the include file a file name extension differing from that of other
451 sure to give the include file a file name extension differing from that of other
453 source files, to avoid Sphinx finding it as a standalone document.)
452 source files, to avoid Sphinx finding it as a standalone document.)
454
453
455 Sphinx defines some default substitutions, see :ref:`default-substitutions`.
454 Sphinx defines some default substitutions, see :ref:`default-substitutions`.
456
455
457
456
458 Comments
457 Comments
459 --------
458 --------
460
459
461 Every explicit markup block which isn't a valid markup construct (like the
460 Every explicit markup block which isn't a valid markup construct (like the
462 footnotes above) is regarded as a comment (:duref:`ref &lt;comments&gt;`). For
461 footnotes above) is regarded as a comment (:duref:`ref &lt;comments&gt;`). For
463 example::
462 example::
464
463
465 .. This is a comment.
464 .. This is a comment.
466
465
467 You can indent text after a comment start to form multiline comments::
466 You can indent text after a comment start to form multiline comments::
468
467
469 ..
468 ..
470 This whole indented block
469 This whole indented block
471 is a comment.
470 is a comment.
472
471
473 Still in the comment.
472 Still in the comment.
474
473
475
474
476 Source encoding
475 Source encoding
477 ---------------
476 ---------------
478
477
479 Since the easiest way to include special characters like em dashes or copyright
478 Since the easiest way to include special characters like em dashes or copyright
480 signs in reST is to directly write them as Unicode characters, one has to
479 signs in reST is to directly write them as Unicode characters, one has to
481 specify an encoding. Sphinx assumes source files to be encoded in UTF-8 by
480 specify an encoding. Sphinx assumes source files to be encoded in UTF-8 by
482 default; you can change this with the :confval:`source_encoding` config value.
481 default; you can change this with the :confval:`source_encoding` config value.
483
482
484
483
485 Gotchas
484 Gotchas
486 -------
485 -------
487
486
488 There are some problems one commonly runs into while authoring reST documents:
487 There are some problems one commonly runs into while authoring reST documents:
489
488
490 * **Separation of inline markup:** As said above, inline markup spans must be
489 * **Separation of inline markup:** As said above, inline markup spans must be
491 separated from the surrounding text by non-word characters, you have to use a
490 separated from the surrounding text by non-word characters, you have to use a
492 backslash-escaped space to get around that. See `the reference
491 backslash-escaped space to get around that. See `the reference
493 &lt;http://docutils.sf.net/docs/ref/rst/restructuredtext.html#inline-markup&gt;`_
492 &lt;http://docutils.sf.net/docs/ref/rst/restructuredtext.html#inline-markup&gt;`_
494 for the details.
493 for the details.
495
494
496 * **No nested inline markup:** Something like ``*see :func:`foo`*`` is not
495 * **No nested inline markup:** Something like ``*see :func:`foo`*`` is not
497 possible.
496 possible.
498
497
499
498
500 .. rubric:: Footnotes
499 .. rubric:: Footnotes
501
500
502 .. [1] When the default domain contains a :rst:dir:`class` directive, this directive
501 .. [1] When the default domain contains a :rst:dir:`class` directive, this directive
503 will be shadowed. Therefore, Sphinx re-exports it as :rst:dir:`rst-class`.
502 will be shadowed. Therefore, Sphinx re-exports it as :rst:dir:`rst-class`.
504 </textarea></form>
503 </textarea></form>
505
504
506 <script>
505 <script>
507 var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
506 var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
508 lineNumbers: true,
507 lineNumbers: true,
509 });
508 });
510 </script>
509 </script>
511 <p>The reStructuredText mode supports one configuration parameter:</p>
510 <p>The reStructuredText mode supports one configuration parameter:</p>
512 <dl>
511 <dl>
513 <dt><code>verbatim (string)</code></dt>
512 <dt><code>verbatim (string)</code></dt>
514 <dd>A name or MIME type of a mode that will be used for highlighting
513 <dd>A name or MIME type of a mode that will be used for highlighting
515 verbatim blocks. By default, reStructuredText mode uses uniform color
514 verbatim blocks. By default, reStructuredText mode uses uniform color
516 for whole block of verbatim text if no mode is given.</dd>
515 for whole block of verbatim text if no mode is given.</dd>
517 </dl>
516 </dl>
518 <p>If <code>python</code> mode is available (not a part of CodeMirror 2 yet),
517 <p>If <code>python</code> mode is available,
519 it will be used for highlighting blocks containing Python/IPython terminal
518 it will be used for highlighting blocks containing Python/IPython terminal
520 sessions (blocks starting with <code>&gt;&gt;&gt;</code> (for Python) or
519 sessions (blocks starting with <code>&gt;&gt;&gt;</code> (for Python) or
521 <code>In [num]:</code> (for IPython).
520 <code>In [num]:</code> (for IPython).
522
521
523 <p><strong>MIME types defined:</strong> <code>text/x-rst</code>.</p>
522 <p><strong>MIME types defined:</strong> <code>text/x-rst</code>.</p>
524 </body>
523 </body>
525 </html>
524 </html>
525
@@ -1,333 +1,326 b''
1 CodeMirror.defineMode('rst', function(config, options) {
1 CodeMirror.defineMode('rst', function(config, options) {
2 function setState(state, fn, ctx) {
2 function setState(state, fn, ctx) {
3 state.fn = fn;
3 state.fn = fn;
4 setCtx(state, ctx);
4 setCtx(state, ctx);
5 }
5 }
6
6
7 function setCtx(state, ctx) {
7 function setCtx(state, ctx) {
8 state.ctx = ctx || {};
8 state.ctx = ctx || {};
9 }
9 }
10
10
11 function setNormal(state, ch) {
11 function setNormal(state, ch) {
12 if (ch && (typeof ch !== 'string')) {
12 if (ch && (typeof ch !== 'string')) {
13 var str = ch.current();
13 var str = ch.current();
14 ch = str[str.length-1];
14 ch = str[str.length-1];
15 }
15 }
16
16
17 setState(state, normal, {back: ch});
17 setState(state, normal, {back: ch});
18 }
18 }
19
19
20 function hasMode(mode) {
20 function hasMode(mode) {
21 if (mode) {
21 if (mode) {
22 var modes = CodeMirror.listModes();
22 var modes = CodeMirror.listModes();
23
23
24 for (var i in modes) {
24 for (var i in modes) {
25 if (modes[i] == mode) {
25 if (modes[i] == mode) {
26 return true;
26 return true;
27 }
27 }
28 }
28 }
29 }
29 }
30
30
31 return false;
31 return false;
32 }
32 }
33
33
34 function getMode(mode) {
34 function getMode(mode) {
35 if (hasMode(mode)) {
35 if (hasMode(mode)) {
36 return CodeMirror.getMode(config, mode);
36 return CodeMirror.getMode(config, mode);
37 } else {
37 } else {
38 return null;
38 return null;
39 }
39 }
40 }
40 }
41
41
42 var verbatimMode = getMode(options.verbatim);
42 var verbatimMode = getMode(options.verbatim);
43 var pythonMode = getMode('python');
43 var pythonMode = getMode('python');
44
44
45 var reSection = /^[!"#$%&'()*+,-./:;<=>?@[\\\]^_`{|}~]/;
45 var reSection = /^[!"#$%&'()*+,-./:;<=>?@[\\\]^_`{|}~]/;
46 var reDirective = /^\s*\w([-:.\w]*\w)?::(\s|$)/;
46 var reDirective = /^\s*\w([-:.\w]*\w)?::(\s|$)/;
47 var reHyperlink = /^\s*_[\w-]+:(\s|$)/;
47 var reHyperlink = /^\s*_[\w-]+:(\s|$)/;
48 var reFootnote = /^\s*\[(\d+|#)\](\s|$)/;
48 var reFootnote = /^\s*\[(\d+|#)\](\s|$)/;
49 var reCitation = /^\s*\[[A-Za-z][\w-]*\](\s|$)/;
49 var reCitation = /^\s*\[[A-Za-z][\w-]*\](\s|$)/;
50 var reFootnoteRef = /^\[(\d+|#)\]_/;
50 var reFootnoteRef = /^\[(\d+|#)\]_/;
51 var reCitationRef = /^\[[A-Za-z][\w-]*\]_/;
51 var reCitationRef = /^\[[A-Za-z][\w-]*\]_/;
52 var reDirectiveMarker = /^\.\.(\s|$)/;
52 var reDirectiveMarker = /^\.\.(\s|$)/;
53 var reVerbatimMarker = /^::\s*$/;
53 var reVerbatimMarker = /^::\s*$/;
54 var rePreInline = /^[-\s"([{</:]/;
54 var rePreInline = /^[-\s"([{</:]/;
55 var rePostInline = /^[-\s`'")\]}>/:.,;!?\\_]/;
55 var rePostInline = /^[-\s`'")\]}>/:.,;!?\\_]/;
56 var reEnumeratedList = /^\s*((\d+|[A-Za-z#])[.)]|\((\d+|[A-Z-a-z#])\))\s/;
56 var reEnumeratedList = /^\s*((\d+|[A-Za-z#])[.)]|\((\d+|[A-Z-a-z#])\))\s/;
57 var reBulletedList = /^\s*[-\+\*]\s/;
57 var reBulletedList = /^\s*[-\+\*]\s/;
58 var reExamples = /^\s+(>>>|In \[\d+\]:)\s/;
58 var reExamples = /^\s+(>>>|In \[\d+\]:)\s/;
59
59
60 function normal(stream, state) {
60 function normal(stream, state) {
61 var ch, sol, i;
61 var ch, sol, i;
62
62
63 if (stream.eat(/\\/)) {
63 if (stream.eat(/\\/)) {
64 ch = stream.next();
64 ch = stream.next();
65 setNormal(state, ch);
65 setNormal(state, ch);
66 return null;
66 return null;
67 }
67 }
68
68
69 sol = stream.sol();
69 sol = stream.sol();
70
70
71 if (sol && (ch = stream.eat(reSection))) {
71 if (sol && (ch = stream.eat(reSection))) {
72 for (i = 0; stream.eat(ch); i++);
72 for (i = 0; stream.eat(ch); i++);
73
73
74 if (i >= 3 && stream.match(/^\s*$/)) {
74 if (i >= 3 && stream.match(/^\s*$/)) {
75 setNormal(state, null);
75 setNormal(state, null);
76 return 'section';
76 return 'header';
77 } else {
77 } else {
78 stream.backUp(i + 1);
78 stream.backUp(i + 1);
79 }
79 }
80 }
80 }
81
81
82 if (sol && stream.match(reDirectiveMarker)) {
82 if (sol && stream.match(reDirectiveMarker)) {
83 if (!stream.eol()) {
83 if (!stream.eol()) {
84 setState(state, directive);
84 setState(state, directive);
85 }
85 }
86
86 return 'meta';
87 return 'directive-marker';
88 }
87 }
89
88
90 if (stream.match(reVerbatimMarker)) {
89 if (stream.match(reVerbatimMarker)) {
91 if (!verbatimMode) {
90 if (!verbatimMode) {
92 setState(state, verbatim);
91 setState(state, verbatim);
93 } else {
92 } else {
94 var mode = verbatimMode;
93 var mode = verbatimMode;
95
94
96 setState(state, verbatim, {
95 setState(state, verbatim, {
97 mode: mode,
96 mode: mode,
98 local: mode.startState()
97 local: mode.startState()
99 });
98 });
100 }
99 }
101
100 return 'meta';
102 return 'verbatim-marker';
103 }
101 }
104
102
105 if (sol && stream.match(reExamples, false)) {
103 if (sol && stream.match(reExamples, false)) {
106 if (!pythonMode) {
104 if (!pythonMode) {
107 setState(state, verbatim);
105 setState(state, verbatim);
108 return 'verbatim-marker';
106 return 'meta';
109 } else {
107 } else {
110 var mode = pythonMode;
108 var mode = pythonMode;
111
109
112 setState(state, verbatim, {
110 setState(state, verbatim, {
113 mode: mode,
111 mode: mode,
114 local: mode.startState()
112 local: mode.startState()
115 });
113 });
116
114
117 return null;
115 return null;
118 }
116 }
119 }
117 }
120
118
121 if (sol && (stream.match(reEnumeratedList) ||
122 stream.match(reBulletedList))) {
123 setNormal(state, stream);
124 return 'list';
125 }
126
127 function testBackward(re) {
119 function testBackward(re) {
128 return sol || !state.ctx.back || re.test(state.ctx.back);
120 return sol || !state.ctx.back || re.test(state.ctx.back);
129 }
121 }
130
122
131 function testForward(re) {
123 function testForward(re) {
132 return stream.eol() || stream.match(re, false);
124 return stream.eol() || stream.match(re, false);
133 }
125 }
134
126
135 function testInline(re) {
127 function testInline(re) {
136 return stream.match(re) && testBackward(/\W/) && testForward(/\W/);
128 return stream.match(re) && testBackward(/\W/) && testForward(/\W/);
137 }
129 }
138
130
139 if (testInline(reFootnoteRef)) {
131 if (testInline(reFootnoteRef)) {
140 setNormal(state, stream);
132 setNormal(state, stream);
141 return 'footnote';
133 return 'footnote';
142 }
134 }
143
135
144 if (testInline(reCitationRef)) {
136 if (testInline(reCitationRef)) {
145 setNormal(state, stream);
137 setNormal(state, stream);
146 return 'citation';
138 return 'citation';
147 }
139 }
148
140
149 ch = stream.next();
141 ch = stream.next();
150
142
151 if (testBackward(rePreInline)) {
143 if (testBackward(rePreInline)) {
152 if ((ch === ':' || ch === '|') && stream.eat(/\S/)) {
144 if ((ch === ':' || ch === '|') && stream.eat(/\S/)) {
153 var token;
145 var token;
154
146
155 if (ch === ':') {
147 if (ch === ':') {
156 token = 'role';
148 token = 'builtin';
157 } else {
149 } else {
158 token = 'replacement';
150 token = 'atom';
159 }
151 }
160
152
161 setState(state, inline, {
153 setState(state, inline, {
162 ch: ch,
154 ch: ch,
163 wide: false,
155 wide: false,
164 prev: null,
156 prev: null,
165 token: token
157 token: token
166 });
158 });
167
159
168 return token;
160 return token;
169 }
161 }
170
162
171 if (ch === '*' || ch === '`') {
163 if (ch === '*' || ch === '`') {
172 var orig = ch,
164 var orig = ch,
173 wide = false;
165 wide = false;
174
166
175 ch = stream.next();
167 ch = stream.next();
176
168
177 if (ch == orig) {
169 if (ch == orig) {
178 wide = true;
170 wide = true;
179 ch = stream.next();
171 ch = stream.next();
180 }
172 }
181
173
182 if (ch && !/\s/.test(ch)) {
174 if (ch && !/\s/.test(ch)) {
183 var token;
175 var token;
184
176
185 if (orig === '*') {
177 if (orig === '*') {
186 token = wide ? 'strong' : 'emphasis';
178 token = wide ? 'strong' : 'em';
187 } else {
179 } else {
188 token = wide ? 'inline' : 'interpreted';
180 token = wide ? 'string' : 'string-2';
189 }
181 }
190
182
191 setState(state, inline, {
183 setState(state, inline, {
192 ch: orig, // inline() has to know what to search for
184 ch: orig, // inline() has to know what to search for
193 wide: wide, // are we looking for `ch` or `chch`
185 wide: wide, // are we looking for `ch` or `chch`
194 prev: null, // terminator must not be preceeded with whitespace
186 prev: null, // terminator must not be preceeded with whitespace
195 token: token // I don't want to recompute this all the time
187 token: token // I don't want to recompute this all the time
196 });
188 });
197
189
198 return token;
190 return token;
199 }
191 }
200 }
192 }
201 }
193 }
202
194
203 setNormal(state, ch);
195 setNormal(state, ch);
204 return null;
196 return null;
205 }
197 }
206
198
207 function inline(stream, state) {
199 function inline(stream, state) {
208 var ch = stream.next(),
200 var ch = stream.next(),
209 token = state.ctx.token;
201 token = state.ctx.token;
210
202
211 function finish(ch) {
203 function finish(ch) {
212 state.ctx.prev = ch;
204 state.ctx.prev = ch;
213 return token;
205 return token;
214 }
206 }
215
207
216 if (ch != state.ctx.ch) {
208 if (ch != state.ctx.ch) {
217 return finish(ch);
209 return finish(ch);
218 }
210 }
219
211
220 if (/\s/.test(state.ctx.prev)) {
212 if (/\s/.test(state.ctx.prev)) {
221 return finish(ch);
213 return finish(ch);
222 }
214 }
223
215
224 if (state.ctx.wide) {
216 if (state.ctx.wide) {
225 ch = stream.next();
217 ch = stream.next();
226
218
227 if (ch != state.ctx.ch) {
219 if (ch != state.ctx.ch) {
228 return finish(ch);
220 return finish(ch);
229 }
221 }
230 }
222 }
231
223
232 if (!stream.eol() && !rePostInline.test(stream.peek())) {
224 if (!stream.eol() && !rePostInline.test(stream.peek())) {
233 if (state.ctx.wide) {
225 if (state.ctx.wide) {
234 stream.backUp(1);
226 stream.backUp(1);
235 }
227 }
236
228
237 return finish(ch);
229 return finish(ch);
238 }
230 }
239
231
240 setState(state, normal);
232 setState(state, normal);
241 setNormal(state, ch);
233 setNormal(state, ch);
242
234
243 return token;
235 return token;
244 }
236 }
245
237
246 function directive(stream, state) {
238 function directive(stream, state) {
247 var token = null;
239 var token = null;
248
240
249 if (stream.match(reDirective)) {
241 if (stream.match(reDirective)) {
250 token = 'directive';
242 token = 'attribute';
251 } else if (stream.match(reHyperlink)) {
243 } else if (stream.match(reHyperlink)) {
252 token = 'hyperlink';
244 token = 'link';
253 } else if (stream.match(reFootnote)) {
245 } else if (stream.match(reFootnote)) {
254 token = 'footnote';
246 token = 'quote';
255 } else if (stream.match(reCitation)) {
247 } else if (stream.match(reCitation)) {
256 token = 'citation';
248 token = 'quote';
257 } else {
249 } else {
258 stream.eatSpace();
250 stream.eatSpace();
259
251
260 if (stream.eol()) {
252 if (stream.eol()) {
261 setNormal(state, stream);
253 setNormal(state, stream);
262 return null;
254 return null;
263 } else {
255 } else {
264 stream.skipToEnd();
256 stream.skipToEnd();
265 setState(state, comment);
257 setState(state, comment);
266 return 'comment';
258 return 'comment';
267 }
259 }
268 }
260 }
269
261
262 // FIXME this is unreachable
270 setState(state, body, {start: true});
263 setState(state, body, {start: true});
271 return token;
264 return token;
272 }
265 }
273
266
274 function body(stream, state) {
267 function body(stream, state) {
275 var token = 'body';
268 var token = 'body';
276
269
277 if (!state.ctx.start || stream.sol()) {
270 if (!state.ctx.start || stream.sol()) {
278 return block(stream, state, token);
271 return block(stream, state, token);
279 }
272 }
280
273
281 stream.skipToEnd();
274 stream.skipToEnd();
282 setCtx(state);
275 setCtx(state);
283
276
284 return token;
277 return token;
285 }
278 }
286
279
287 function comment(stream, state) {
280 function comment(stream, state) {
288 return block(stream, state, 'comment');
281 return block(stream, state, 'comment');
289 }
282 }
290
283
291 function verbatim(stream, state) {
284 function verbatim(stream, state) {
292 if (!verbatimMode) {
285 if (!verbatimMode) {
293 return block(stream, state, 'verbatim');
286 return block(stream, state, 'meta');
294 } else {
287 } else {
295 if (stream.sol()) {
288 if (stream.sol()) {
296 if (!stream.eatSpace()) {
289 if (!stream.eatSpace()) {
297 setNormal(state, stream);
290 setNormal(state, stream);
298 }
291 }
299
292
300 return null;
293 return null;
301 }
294 }
302
295
303 return verbatimMode.token(stream, state.ctx.local);
296 return verbatimMode.token(stream, state.ctx.local);
304 }
297 }
305 }
298 }
306
299
307 function block(stream, state, token) {
300 function block(stream, state, token) {
308 if (stream.eol() || stream.eatSpace()) {
301 if (stream.eol() || stream.eatSpace()) {
309 stream.skipToEnd();
302 stream.skipToEnd();
310 return token;
303 return token;
311 } else {
304 } else {
312 setNormal(state, stream);
305 setNormal(state, stream);
313 return null;
306 return null;
314 }
307 }
315 }
308 }
316
309
317 return {
310 return {
318 startState: function() {
311 startState: function() {
319 return {fn: normal, ctx: {}};
312 return {fn: normal, ctx: {}};
320 },
313 },
321
314
322 copyState: function(state) {
315 copyState: function(state) {
323 return {fn: state.fn, ctx: state.ctx};
316 return {fn: state.fn, ctx: state.ctx};
324 },
317 },
325
318
326 token: function(stream, state) {
319 token: function(stream, state) {
327 var token = state.fn(stream, state);
320 var token = state.fn(stream, state);
328 return token;
321 return token;
329 }
322 }
330 };
323 };
331 });
324 });
332
325
333 CodeMirror.defineMIME("text/x-rst", "rst");
326 CodeMirror.defineMIME("text/x-rst", "rst");
@@ -1,42 +1,44 b''
1 <!doctype html>
1 <!doctype html>
2 <html>
2 <html>
3 <head>
3 <head>
4 <title>CodeMirror 2: XML mode</title>
4 <title>CodeMirror: XML mode</title>
5 <link rel="stylesheet" href="../../lib/codemirror.css">
5 <link rel="stylesheet" href="../../lib/codemirror.css">
6 <script src="../../lib/codemirror.js"></script>
6 <script src="../../lib/codemirror.js"></script>
7 <script src="xml.js"></script>
7 <script src="xml.js"></script>
8 <link rel="stylesheet" href="../../theme/default.css">
9 <style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
8 <style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
10 <link rel="stylesheet" href="../../css/docs.css">
9 <link rel="stylesheet" href="../../doc/docs.css">
11 </head>
10 </head>
12 <body>
11 <body>
13 <h1>CodeMirror 2: XML mode</h1>
12 <h1>CodeMirror: XML mode</h1>
14 <form><textarea id="code" name="code">
13 <form><textarea id="code" name="code">
15 &lt;html style="color: green"&gt;
14 &lt;html style="color: green"&gt;
16 &lt;!-- this is a comment --&gt;
15 &lt;!-- this is a comment --&gt;
17 &lt;head&gt;
16 &lt;head&gt;
18 &lt;title&gt;HTML Example&lt;/title&gt;
17 &lt;title&gt;HTML Example&lt;/title&gt;
19 &lt;/head&gt;
18 &lt;/head&gt;
20 &lt;body&gt;
19 &lt;body&gt;
21 The indentation tries to be &lt;em&gt;somewhat &amp;quot;do what
20 The indentation tries to be &lt;em&gt;somewhat &amp;quot;do what
22 I mean&amp;quot;&lt;/em&gt;... but might not match your style.
21 I mean&amp;quot;&lt;/em&gt;... but might not match your style.
23 &lt;/body&gt;
22 &lt;/body&gt;
24 &lt;/html&gt;
23 &lt;/html&gt;
25 </textarea></form>
24 </textarea></form>
26 <script>
25 <script>
27 var editor = CodeMirror.fromTextArea(document.getElementById("code"), {mode: {name: "xml", htmlMode: true}});
26 var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
27 mode: {name: "xml", alignCDATA: true},
28 lineNumbers: true
29 });
28 </script>
30 </script>
29 <p>The XML mode supports two configuration parameters:</p>
31 <p>The XML mode supports two configuration parameters:</p>
30 <dl>
32 <dl>
31 <dt><code>htmlMode (boolean)</code></dt>
33 <dt><code>htmlMode (boolean)</code></dt>
32 <dd>This switches the mode to parse HTML instead of XML. This
34 <dd>This switches the mode to parse HTML instead of XML. This
33 means attributes do not have to be quoted, and some elements
35 means attributes do not have to be quoted, and some elements
34 (such as <code>br</code>) do not require a closing tag.</dd>
36 (such as <code>br</code>) do not require a closing tag.</dd>
35 <dt><code>alignCDATA (boolean)</code></dt>
37 <dt><code>alignCDATA (boolean)</code></dt>
36 <dd>Setting this to true will force the opening tag of CDATA
38 <dd>Setting this to true will force the opening tag of CDATA
37 blocks to not be indented.</dd>
39 blocks to not be indented.</dd>
38 </dl>
40 </dl>
39
41
40 <p><strong>MIME types defined:</strong> <code>application/xml</code>, <code>text/html</code>.</p>
42 <p><strong>MIME types defined:</strong> <code>application/xml</code>, <code>text/html</code>.</p>
41 </body>
43 </body>
42 </html>
44 </html>
@@ -1,231 +1,252 b''
1 CodeMirror.defineMode("xml", function(config, parserConfig) {
1 CodeMirror.defineMode("xml", function(config, parserConfig) {
2 var indentUnit = config.indentUnit;
2 var indentUnit = config.indentUnit;
3 var Kludges = parserConfig.htmlMode ? {
3 var Kludges = parserConfig.htmlMode ? {
4 autoSelfClosers: {"br": true, "img": true, "hr": true, "link": true, "input": true,
4 autoSelfClosers: {"br": true, "img": true, "hr": true, "link": true, "input": true,
5 "meta": true, "col": true, "frame": true, "base": true, "area": true},
5 "meta": true, "col": true, "frame": true, "base": true, "area": true},
6 doNotIndent: {"pre": true, "!cdata": true},
6 doNotIndent: {"pre": true},
7 allowUnquoted: true
7 allowUnquoted: true
8 } : {autoSelfClosers: {}, doNotIndent: {"!cdata": true}, allowUnquoted: false};
8 } : {autoSelfClosers: {}, doNotIndent: {}, allowUnquoted: false};
9 var alignCDATA = parserConfig.alignCDATA;
9 var alignCDATA = parserConfig.alignCDATA;
10
10
11 // Return variables for tokenizers
11 // Return variables for tokenizers
12 var tagName, type;
12 var tagName, type;
13
13
14 function inText(stream, state) {
14 function inText(stream, state) {
15 function chain(parser) {
15 function chain(parser) {
16 state.tokenize = parser;
16 state.tokenize = parser;
17 return parser(stream, state);
17 return parser(stream, state);
18 }
18 }
19
19
20 var ch = stream.next();
20 var ch = stream.next();
21 if (ch == "<") {
21 if (ch == "<") {
22 if (stream.eat("!")) {
22 if (stream.eat("!")) {
23 if (stream.eat("[")) {
23 if (stream.eat("[")) {
24 if (stream.match("CDATA[")) return chain(inBlock("atom", "]]>"));
24 if (stream.match("CDATA[")) return chain(inBlock("atom", "]]>"));
25 else return null;
25 else return null;
26 }
26 }
27 else if (stream.match("--")) return chain(inBlock("comment", "-->"));
27 else if (stream.match("--")) return chain(inBlock("comment", "-->"));
28 else if (stream.match("DOCTYPE", true, true)) {
28 else if (stream.match("DOCTYPE", true, true)) {
29 stream.eatWhile(/[\w\._\-]/);
29 stream.eatWhile(/[\w\._\-]/);
30 return chain(inBlock("meta", ">"));
30 return chain(doctype(1));
31 }
31 }
32 else return null;
32 else return null;
33 }
33 }
34 else if (stream.eat("?")) {
34 else if (stream.eat("?")) {
35 stream.eatWhile(/[\w\._\-]/);
35 stream.eatWhile(/[\w\._\-]/);
36 state.tokenize = inBlock("meta", "?>");
36 state.tokenize = inBlock("meta", "?>");
37 return "meta";
37 return "meta";
38 }
38 }
39 else {
39 else {
40 type = stream.eat("/") ? "closeTag" : "openTag";
40 type = stream.eat("/") ? "closeTag" : "openTag";
41 stream.eatSpace();
41 stream.eatSpace();
42 tagName = "";
42 tagName = "";
43 var c;
43 var c;
44 while ((c = stream.eat(/[^\s\u00a0=<>\"\'\/?]/))) tagName += c;
44 while ((c = stream.eat(/[^\s\u00a0=<>\"\'\/?]/))) tagName += c;
45 state.tokenize = inTag;
45 state.tokenize = inTag;
46 return "tag";
46 return "tag";
47 }
47 }
48 }
48 }
49 else if (ch == "&") {
49 else if (ch == "&") {
50 stream.eatWhile(/[^;]/);
50 stream.eatWhile(/[^;]/);
51 stream.eat(";");
51 stream.eat(";");
52 return "atom";
52 return "atom";
53 }
53 }
54 else {
54 else {
55 stream.eatWhile(/[^&<]/);
55 stream.eatWhile(/[^&<]/);
56 return null;
56 return null;
57 }
57 }
58 }
58 }
59
59
60 function inTag(stream, state) {
60 function inTag(stream, state) {
61 var ch = stream.next();
61 var ch = stream.next();
62 if (ch == ">" || (ch == "/" && stream.eat(">"))) {
62 if (ch == ">" || (ch == "/" && stream.eat(">"))) {
63 state.tokenize = inText;
63 state.tokenize = inText;
64 type = ch == ">" ? "endTag" : "selfcloseTag";
64 type = ch == ">" ? "endTag" : "selfcloseTag";
65 return "tag";
65 return "tag";
66 }
66 }
67 else if (ch == "=") {
67 else if (ch == "=") {
68 type = "equals";
68 type = "equals";
69 return null;
69 return null;
70 }
70 }
71 else if (/[\'\"]/.test(ch)) {
71 else if (/[\'\"]/.test(ch)) {
72 state.tokenize = inAttribute(ch);
72 state.tokenize = inAttribute(ch);
73 return state.tokenize(stream, state);
73 return state.tokenize(stream, state);
74 }
74 }
75 else {
75 else {
76 stream.eatWhile(/[^\s\u00a0=<>\"\'\/?]/);
76 stream.eatWhile(/[^\s\u00a0=<>\"\'\/?]/);
77 return "word";
77 return "word";
78 }
78 }
79 }
79 }
80
80
81 function inAttribute(quote) {
81 function inAttribute(quote) {
82 return function(stream, state) {
82 return function(stream, state) {
83 while (!stream.eol()) {
83 while (!stream.eol()) {
84 if (stream.next() == quote) {
84 if (stream.next() == quote) {
85 state.tokenize = inTag;
85 state.tokenize = inTag;
86 break;
86 break;
87 }
87 }
88 }
88 }
89 return "string";
89 return "string";
90 };
90 };
91 }
91 }
92
92
93 function inBlock(style, terminator) {
93 function inBlock(style, terminator) {
94 return function(stream, state) {
94 return function(stream, state) {
95 while (!stream.eol()) {
95 while (!stream.eol()) {
96 if (stream.match(terminator)) {
96 if (stream.match(terminator)) {
97 state.tokenize = inText;
97 state.tokenize = inText;
98 break;
98 break;
99 }
99 }
100 stream.next();
100 stream.next();
101 }
101 }
102 return style;
102 return style;
103 };
103 };
104 }
104 }
105 function doctype(depth) {
106 return function(stream, state) {
107 var ch;
108 while ((ch = stream.next()) != null) {
109 if (ch == "<") {
110 state.tokenize = doctype(depth + 1);
111 return state.tokenize(stream, state);
112 } else if (ch == ">") {
113 if (depth == 1) {
114 state.tokenize = inText;
115 break;
116 } else {
117 state.tokenize = doctype(depth - 1);
118 return state.tokenize(stream, state);
119 }
120 }
121 }
122 return "meta";
123 };
124 }
105
125
106 var curState, setStyle;
126 var curState, setStyle;
107 function pass() {
127 function pass() {
108 for (var i = arguments.length - 1; i >= 0; i--) curState.cc.push(arguments[i]);
128 for (var i = arguments.length - 1; i >= 0; i--) curState.cc.push(arguments[i]);
109 }
129 }
110 function cont() {
130 function cont() {
111 pass.apply(null, arguments);
131 pass.apply(null, arguments);
112 return true;
132 return true;
113 }
133 }
114
134
115 function pushContext(tagName, startOfLine) {
135 function pushContext(tagName, startOfLine) {
116 var noIndent = Kludges.doNotIndent.hasOwnProperty(tagName) || (curState.context && curState.context.noIndent);
136 var noIndent = Kludges.doNotIndent.hasOwnProperty(tagName) || (curState.context && curState.context.noIndent);
117 curState.context = {
137 curState.context = {
118 prev: curState.context,
138 prev: curState.context,
119 tagName: tagName,
139 tagName: tagName,
120 indent: curState.indented,
140 indent: curState.indented,
121 startOfLine: startOfLine,
141 startOfLine: startOfLine,
122 noIndent: noIndent
142 noIndent: noIndent
123 };
143 };
124 }
144 }
125 function popContext() {
145 function popContext() {
126 if (curState.context) curState.context = curState.context.prev;
146 if (curState.context) curState.context = curState.context.prev;
127 }
147 }
128
148
129 function element(type) {
149 function element(type) {
130 if (type == "openTag") {curState.tagName = tagName; return cont(attributes, endtag(curState.startOfLine));}
150 if (type == "openTag") {
131 else if (type == "closeTag") {
151 curState.tagName = tagName;
152 return cont(attributes, endtag(curState.startOfLine));
153 } else if (type == "closeTag") {
132 var err = false;
154 var err = false;
133 if (curState.context) {
155 if (curState.context) {
134 err = curState.context.tagName != tagName;
156 err = curState.context.tagName != tagName;
135 } else {
157 } else {
136 err = true;
158 err = true;
137 }
159 }
138 if (err) setStyle = "error";
160 if (err) setStyle = "error";
139 return cont(endclosetag(err));
161 return cont(endclosetag(err));
140 }
162 }
141 else if (type == "string") {
142 if (!curState.context || curState.context.name != "!cdata") pushContext("!cdata");
143 if (curState.tokenize == inText) popContext();
144 return cont();
163 return cont();
145 }
164 }
146 else return cont();
147 }
148 function endtag(startOfLine) {
165 function endtag(startOfLine) {
149 return function(type) {
166 return function(type) {
150 if (type == "selfcloseTag" ||
167 if (type == "selfcloseTag" ||
151 (type == "endTag" && Kludges.autoSelfClosers.hasOwnProperty(curState.tagName.toLowerCase())))
168 (type == "endTag" && Kludges.autoSelfClosers.hasOwnProperty(curState.tagName.toLowerCase())))
152 return cont();
169 return cont();
153 if (type == "endTag") {pushContext(curState.tagName, startOfLine); return cont();}
170 if (type == "endTag") {pushContext(curState.tagName, startOfLine); return cont();}
154 return cont();
171 return cont();
155 };
172 };
156 }
173 }
157 function endclosetag(err) {
174 function endclosetag(err) {
158 return function(type) {
175 return function(type) {
159 if (err) setStyle = "error";
176 if (err) setStyle = "error";
160 if (type == "endTag") { popContext(); return cont(); }
177 if (type == "endTag") { popContext(); return cont(); }
161 setStyle = "error";
178 setStyle = "error";
162 return cont(arguments.callee);
179 return cont(arguments.callee);
163 }
180 }
164 }
181 }
165
182
166 function attributes(type) {
183 function attributes(type) {
167 if (type == "word") {setStyle = "attribute"; return cont(attributes);}
184 if (type == "word") {setStyle = "attribute"; return cont(attributes);}
168 if (type == "equals") return cont(attvalue, attributes);
185 if (type == "equals") return cont(attvalue, attributes);
186 if (type == "string") {setStyle = "error"; return cont(attributes);}
169 return pass();
187 return pass();
170 }
188 }
171 function attvalue(type) {
189 function attvalue(type) {
172 if (type == "word" && Kludges.allowUnquoted) {setStyle = "string"; return cont();}
190 if (type == "word" && Kludges.allowUnquoted) {setStyle = "string"; return cont();}
173 if (type == "string") return cont(attvaluemaybe);
191 if (type == "string") return cont(attvaluemaybe);
174 return pass();
192 return pass();
175 }
193 }
176 function attvaluemaybe(type) {
194 function attvaluemaybe(type) {
177 if (type == "string") return cont(attvaluemaybe);
195 if (type == "string") return cont(attvaluemaybe);
178 else return pass();
196 else return pass();
179 }
197 }
180
198
181 return {
199 return {
182 startState: function() {
200 startState: function() {
183 return {tokenize: inText, cc: [], indented: 0, startOfLine: true, tagName: null, context: null};
201 return {tokenize: inText, cc: [], indented: 0, startOfLine: true, tagName: null, context: null};
184 },
202 },
185
203
186 token: function(stream, state) {
204 token: function(stream, state) {
187 if (stream.sol()) {
205 if (stream.sol()) {
188 state.startOfLine = true;
206 state.startOfLine = true;
189 state.indented = stream.indentation();
207 state.indented = stream.indentation();
190 }
208 }
191 if (stream.eatSpace()) return null;
209 if (stream.eatSpace()) return null;
192
210
193 setStyle = type = tagName = null;
211 setStyle = type = tagName = null;
194 var style = state.tokenize(stream, state);
212 var style = state.tokenize(stream, state);
213 state.type = type;
195 if ((style || type) && style != "comment") {
214 if ((style || type) && style != "comment") {
196 curState = state;
215 curState = state;
197 while (true) {
216 while (true) {
198 var comb = state.cc.pop() || element;
217 var comb = state.cc.pop() || element;
199 if (comb(type || style)) break;
218 if (comb(type || style)) break;
200 }
219 }
201 }
220 }
202 state.startOfLine = false;
221 state.startOfLine = false;
203 return setStyle || style;
222 return setStyle || style;
204 },
223 },
205
224
206 indent: function(state, textAfter) {
225 indent: function(state, textAfter, fullLine) {
207 var context = state.context;
226 var context = state.context;
208 if (context && context.noIndent) return 0;
227 if ((state.tokenize != inTag && state.tokenize != inText) ||
228 context && context.noIndent)
229 return fullLine ? fullLine.match(/^(\s*)/)[0].length : 0;
209 if (alignCDATA && /<!\[CDATA\[/.test(textAfter)) return 0;
230 if (alignCDATA && /<!\[CDATA\[/.test(textAfter)) return 0;
210 if (context && /^<\//.test(textAfter))
231 if (context && /^<\//.test(textAfter))
211 context = context.prev;
232 context = context.prev;
212 while (context && !context.startOfLine)
233 while (context && !context.startOfLine)
213 context = context.prev;
234 context = context.prev;
214 if (context) return context.indent + indentUnit;
235 if (context) return context.indent + indentUnit;
215 else return 0;
236 else return 0;
216 },
237 },
217
238
218 compareStates: function(a, b) {
239 compareStates: function(a, b) {
219 if (a.indented != b.indented) return false;
240 if (a.indented != b.indented || a.tokenize != b.tokenize) return false;
220 for (var ca = a.context, cb = b.context; ; ca = ca.prev, cb = cb.prev) {
241 for (var ca = a.context, cb = b.context; ; ca = ca.prev, cb = cb.prev) {
221 if (!ca || !cb) return ca == cb;
242 if (!ca || !cb) return ca == cb;
222 if (ca.tagName != cb.tagName) return false;
243 if (ca.tagName != cb.tagName) return false;
223 }
244 }
224 },
245 },
225
246
226 electricChars: "/"
247 electricChars: "/"
227 };
248 };
228 });
249 });
229
250
230 CodeMirror.defineMIME("application/xml", "xml");
251 CodeMirror.defineMIME("application/xml", "xml");
231 CodeMirror.defineMIME("text/html", {name: "xml", htmlMode: true});
252 CodeMirror.defineMIME("text/html", {name: "xml", htmlMode: true});
@@ -1,9 +1,10 b''
1 .cm-s-elegant span.cm-number, .cm-s-elegant span.cm-string, .cm-s-elegant span.cm-atom {color: #762;}
1 .cm-s-elegant span.cm-number, .cm-s-elegant span.cm-string, .cm-s-elegant span.cm-atom {color: #762;}
2 .cm-s-elegant span.cm-comment {color: #262;font-style: italic;}
2 .cm-s-elegant span.cm-comment {color: #262;font-style: italic;}
3 .cm-s-elegant span.cm-meta {color: #555;font-style: italic;}
3 .cm-s-elegant span.cm-meta {color: #555;font-style: italic;}
4 .cm-s-elegant span.cm-variable {color: black;}
4 .cm-s-elegant span.cm-variable {color: black;}
5 .cm-s-elegant span.cm-variable-2 {color: #b11;}
5 .cm-s-elegant span.cm-variable-2 {color: #b11;}
6 .cm-s-elegant span.cm-qualifier {color: #555;}
6 .cm-s-elegant span.cm-qualifier {color: #555;}
7 .cm-s-elegant span.cm-keyword {color: #730;}
7 .cm-s-elegant span.cm-keyword {color: #730;}
8 .cm-s-elegant span.cm-builtin {color: #30a;}
8 .cm-s-elegant span.cm-builtin {color: #30a;}
9 .cm-s-elegant span.cm-error {background-color: #fdd;}
9 .cm-s-elegant span.cm-error {background-color: #fdd;}
10 .cm-s-elegant span.cm-link {color: #762;}
@@ -1,8 +1,9 b''
1 .cm-s-neat span.cm-comment { color: #a86; }
1 .cm-s-neat span.cm-comment { color: #a86; }
2 .cm-s-neat span.cm-keyword { font-weight: bold; color: blue; }
2 .cm-s-neat span.cm-keyword { font-weight: bold; color: blue; }
3 .cm-s-neat span.cm-string { color: #a22; }
3 .cm-s-neat span.cm-string { color: #a22; }
4 .cm-s-neat span.cm-builtin { font-weight: bold; color: #077; }
4 .cm-s-neat span.cm-builtin { font-weight: bold; color: #077; }
5 .cm-s-neat span.cm-special { font-weight: bold; color: #0aa; }
5 .cm-s-neat span.cm-special { font-weight: bold; color: #0aa; }
6 .cm-s-neat span.cm-variable { color: black; }
6 .cm-s-neat span.cm-variable { color: black; }
7 .cm-s-neat span.cm-number, .cm-s-neat span.cm-atom { color: #3a3; }
7 .cm-s-neat span.cm-number, .cm-s-neat span.cm-atom { color: #3a3; }
8 .cm-s-neat span.cm-meta {color: #555;}
8 .cm-s-neat span.cm-meta {color: #555;}
9 .cm-s-neat span.cm-link { color: #3a3; }
@@ -1,20 +1,21 b''
1 /* Loosely based on the Midnight Textmate theme */
1 /* Loosely based on the Midnight Textmate theme */
2
2
3 .cm-s-night { background: #0a001f; color: #f8f8f8; }
3 .cm-s-night { background: #0a001f; color: #f8f8f8; }
4 .cm-s-night span.CodeMirror-selected { background: #a8f !important; }
4 .cm-s-night span.CodeMirror-selected { background: #a8f !important; }
5 .cm-s-night .CodeMirror-gutter { background: #0a001f; border-right: 1px solid #aaa; }
5 .cm-s-night .CodeMirror-gutter { background: #0a001f; border-right: 1px solid #aaa; }
6 .cm-s-night .CodeMirror-gutter-text { color: #f8f8f8; }
6 .cm-s-night .CodeMirror-gutter-text { color: #f8f8f8; }
7 .cm-s-night .CodeMirror-cursor { border-left: 1px solid white !important; }
7 .cm-s-night .CodeMirror-cursor { border-left: 1px solid white !important; }
8
8
9 .cm-s-night span.cm-comment { color: #6900a1; }
9 .cm-s-night span.cm-comment { color: #6900a1; }
10 .cm-s-night span.cm-atom { color: #845dc4; }
10 .cm-s-night span.cm-atom { color: #845dc4; }
11 .cm-s-night span.cm-number, .cm-s-night span.cm-attribute { color: #ffd500; }
11 .cm-s-night span.cm-number, .cm-s-night span.cm-attribute { color: #ffd500; }
12 .cm-s-night span.cm-keyword { color: #599eff; }
12 .cm-s-night span.cm-keyword { color: #599eff; }
13 .cm-s-night span.cm-string { color: #37f14a; }
13 .cm-s-night span.cm-string { color: #37f14a; }
14 .cm-s-night span.cm-meta { color: #7678e2; }
14 .cm-s-night span.cm-meta { color: #7678e2; }
15 .cm-s-night span.cm-variable-2, .cm-s-night span.cm-tag { color: #99b2ff; }
15 .cm-s-night span.cm-variable-2, .cm-s-night span.cm-tag { color: #99b2ff; }
16 .cm-s-night span.cm-variable-3, .cm-s-night span.cm-def { color: white; }
16 .cm-s-night span.cm-variable-3, .cm-s-night span.cm-def { color: white; }
17 .cm-s-night span.cm-error { color: #9d1e15; }
17 .cm-s-night span.cm-error { color: #9d1e15; }
18 .cm-s-night span.cm-bracket { color: #8da6ce; }
18 .cm-s-night span.cm-bracket { color: #8da6ce; }
19 .cm-s-night span.cm-comment { color: #6900a1; }
19 .cm-s-night span.cm-comment { color: #6900a1; }
20 .cm-s-night span.cm-builtin, .cm-s-night span.cm-special { color: #ff9e59; }
20 .cm-s-night span.cm-builtin, .cm-s-night span.cm-special { color: #ff9e59; }
21 .cm-s-night span.cm-link { color: #845dc4; }
@@ -1,208 +1,205 b''
1 <!DOCTYPE HTML>
1 <!DOCTYPE HTML>
2 <html>
2 <html>
3
3
4 <head>
4 <head>
5 <meta charset="utf-8">
5 <meta charset="utf-8">
6
6
7 <title>IPython Notebook</title>
7 <title>IPython Notebook</title>
8
8
9 {% if mathjax_url %}
9 {% if mathjax_url %}
10 <script type="text/javascript" src="{{mathjax_url}}?config=TeX-AMS_HTML" charset="utf-8"></script>
10 <script type="text/javascript" src="{{mathjax_url}}?config=TeX-AMS_HTML" charset="utf-8"></script>
11 {% end %}
11 {% end %}
12 <script type="text/javascript">
12 <script type="text/javascript">
13 // MathJax disabled, set as null to distingish from *missing* MathJax,
13 // MathJax disabled, set as null to distingish from *missing* MathJax,
14 // where it will be undefined, and should prompt a dialog later.
14 // where it will be undefined, and should prompt a dialog later.
15 window.mathjax_url = "{{mathjax_url}}";
15 window.mathjax_url = "{{mathjax_url}}";
16 </script>
16 </script>
17
17
18 <link rel="stylesheet" href="/static/jquery/css/themes/base/jquery-ui.min.css" type="text/css" />
18 <link rel="stylesheet" href="/static/jquery/css/themes/base/jquery-ui.min.css" type="text/css" />
19 <link rel="stylesheet" href="/static/codemirror/lib/codemirror.css">
19 <link rel="stylesheet" href="/static/codemirror/lib/codemirror.css">
20 <link rel="stylesheet" href="/static/codemirror/mode/markdown/markdown.css">
21 <link rel="stylesheet" href="/static/codemirror/mode/rst/rst.css">
22 <link rel="stylesheet" href="/static/codemirror/theme/ipython.css">
20 <link rel="stylesheet" href="/static/codemirror/theme/ipython.css">
23 <link rel="stylesheet" href="/static/codemirror/theme/default.css">
24
21
25 <link rel="stylesheet" href="/static/prettify/prettify.css"/>
22 <link rel="stylesheet" href="/static/prettify/prettify.css"/>
26
23
27 <link rel="stylesheet" href="/static/css/boilerplate.css" type="text/css" />
24 <link rel="stylesheet" href="/static/css/boilerplate.css" type="text/css" />
28 <link rel="stylesheet" href="/static/css/layout.css" type="text/css" />
25 <link rel="stylesheet" href="/static/css/layout.css" type="text/css" />
29 <link rel="stylesheet" href="/static/css/base.css" type="text/css" />
26 <link rel="stylesheet" href="/static/css/base.css" type="text/css" />
30 <link rel="stylesheet" href="/static/css/notebook.css" type="text/css" />
27 <link rel="stylesheet" href="/static/css/notebook.css" type="text/css" />
31 <link rel="stylesheet" href="/static/css/renderedhtml.css" type="text/css" />
28 <link rel="stylesheet" href="/static/css/renderedhtml.css" type="text/css" />
32
29
33 {% comment In the notebook, the read-only flag is used to determine %}
30 {% comment In the notebook, the read-only flag is used to determine %}
34 {% comment whether to hide the side panels and switch off input %}
31 {% comment whether to hide the side panels and switch off input %}
35 <meta name="read_only" content="{{read_only and not logged_in}}"/>
32 <meta name="read_only" content="{{read_only and not logged_in}}"/>
36
33
37 </head>
34 </head>
38
35
39 <body
36 <body
40 data-project={{project}} data-notebook-id={{notebook_id}}
37 data-project={{project}} data-notebook-id={{notebook_id}}
41 data-base-project-url={{base_project_url}} data-base-kernel-url={{base_kernel_url}}
38 data-base-project-url={{base_project_url}} data-base-kernel-url={{base_kernel_url}}
42 >
39 >
43
40
44 <div id="header">
41 <div id="header">
45 <span id="ipython_notebook"><h1><a href='..' alt='dashboard'><img src='/static/ipynblogo.png' alt='IPython Notebook'/></a></h1></span>
42 <span id="ipython_notebook"><h1><a href='..' alt='dashboard'><img src='/static/ipynblogo.png' alt='IPython Notebook'/></a></h1></span>
46 <span id="save_widget">
43 <span id="save_widget">
47 <span id="notebook_name"></span>
44 <span id="notebook_name"></span>
48 <span id="save_status"></span>
45 <span id="save_status"></span>
49 </span>
46 </span>
50
47
51 <span id="login_widget">
48 <span id="login_widget">
52 {% comment This is a temporary workaround to hide the logout button %}
49 {% comment This is a temporary workaround to hide the logout button %}
53 {% comment when appropriate until notebook.html is templated %}
50 {% comment when appropriate until notebook.html is templated %}
54 {% if logged_in %}
51 {% if logged_in %}
55 <button id="logout">Logout</button>
52 <button id="logout">Logout</button>
56 {% elif not logged_in and login_available %}
53 {% elif not logged_in and login_available %}
57 <button id="login">Login</button>
54 <button id="login">Login</button>
58 {% end %}
55 {% end %}
59 </span>
56 </span>
60
57
61 <span id="kernel_status">Idle</span>
58 <span id="kernel_status">Idle</span>
62 </div>
59 </div>
63
60
64 <div id="menubar">
61 <div id="menubar">
65 <ul id="menus">
62 <ul id="menus">
66 <li><a href="#">File</a>
63 <li><a href="#">File</a>
67 <ul>
64 <ul>
68 <li id="new_notebook"><a href="#">New</a></li>
65 <li id="new_notebook"><a href="#">New</a></li>
69 <li id="open_notebook"><a href="#">Open...</a></li>
66 <li id="open_notebook"><a href="#">Open...</a></li>
70 <hr/>
67 <hr/>
71 <li id="copy_notebook"><a href="#">Make a Copy...</a></li>
68 <li id="copy_notebook"><a href="#">Make a Copy...</a></li>
72 <li id="rename_notebook"><a href="#">Rename...</a></li>
69 <li id="rename_notebook"><a href="#">Rename...</a></li>
73 <li id="save_notebook"><a href="#">Save</a></li>
70 <li id="save_notebook"><a href="#">Save</a></li>
74 <hr/>
71 <hr/>
75 <li><a href="#">Download as</a>
72 <li><a href="#">Download as</a>
76 <ul>
73 <ul>
77 <li id="download_ipynb"><a href="#">IPython (.ipynb)</a></li>
74 <li id="download_ipynb"><a href="#">IPython (.ipynb)</a></li>
78 <li id="download_py"><a href="#">Python (.py)</a></li>
75 <li id="download_py"><a href="#">Python (.py)</a></li>
79 </ul>
76 </ul>
80 </li>
77 </li>
81 <hr/>
78 <hr/>
82 <li id="print_notebook"><a href="/{{notebook_id}}/print" target="_blank">Print View</a></li>
79 <li id="print_notebook"><a href="/{{notebook_id}}/print" target="_blank">Print View</a></li>
83 </ul>
80 </ul>
84 </li>
81 </li>
85 <li><a href="#">Edit</a>
82 <li><a href="#">Edit</a>
86 <ul>
83 <ul>
87 <li id="cut_cell"><a href="#">Cut</a></li>
84 <li id="cut_cell"><a href="#">Cut</a></li>
88 <li id="copy_cell"><a href="#">Copy</a></li>
85 <li id="copy_cell"><a href="#">Copy</a></li>
89 <li id="paste_cell" class="ui-state-disabled"><a href="#">Paste</a></li>
86 <li id="paste_cell" class="ui-state-disabled"><a href="#">Paste</a></li>
90 <li id="paste_cell_above" class="ui-state-disabled"><a href="#">Paste Above</a></li>
87 <li id="paste_cell_above" class="ui-state-disabled"><a href="#">Paste Above</a></li>
91 <li id="paste_cell_below" class="ui-state-disabled"><a href="#">Paste Below</a></li>
88 <li id="paste_cell_below" class="ui-state-disabled"><a href="#">Paste Below</a></li>
92 <li id="delete_cell"><a href="#">Delete</a></li>
89 <li id="delete_cell"><a href="#">Delete</a></li>
93 <hr/>
90 <hr/>
94 <li id="split_cell"><a href="#">Split</a></li>
91 <li id="split_cell"><a href="#">Split</a></li>
95 <li id="merge_cell_above"><a href="#">Merge Above</a></li>
92 <li id="merge_cell_above"><a href="#">Merge Above</a></li>
96 <li id="merge_cell_below"><a href="#">Merge Below</a></li>
93 <li id="merge_cell_below"><a href="#">Merge Below</a></li>
97 <hr/>
94 <hr/>
98 <li id="move_cell_up"><a href="#">Move Up</a></li>
95 <li id="move_cell_up"><a href="#">Move Up</a></li>
99 <li id="move_cell_down"><a href="#">Move Down</a></li>
96 <li id="move_cell_down"><a href="#">Move Down</a></li>
100 <hr/>
97 <hr/>
101 <li id="select_previous"><a href="#">Select Previous</a></li>
98 <li id="select_previous"><a href="#">Select Previous</a></li>
102 <li id="select_next"><a href="#">Select Next</a></li>
99 <li id="select_next"><a href="#">Select Next</a></li>
103 </ul>
100 </ul>
104 </li>
101 </li>
105 <li><a href="#">Insert</a>
102 <li><a href="#">Insert</a>
106 <ul>
103 <ul>
107 <li id="insert_cell_above"><a href="#">Insert Cell Above</a></li>
104 <li id="insert_cell_above"><a href="#">Insert Cell Above</a></li>
108 <li id="insert_cell_below"><a href="#">Insert Cell Below</a></li>
105 <li id="insert_cell_below"><a href="#">Insert Cell Below</a></li>
109 </ul>
106 </ul>
110 </li>
107 </li>
111 <li><a href="#">Cell</a>
108 <li><a href="#">Cell</a>
112 <ul>
109 <ul>
113 <li id="full_edit_cell"><a href="#">Edit in Ace</a></li>
110 <li id="full_edit_cell"><a href="#">Edit in Ace</a></li>
114 <hr/>
111 <hr/>
115 <li id="run_cell"><a href="#">Run</a></li>
112 <li id="run_cell"><a href="#">Run</a></li>
116 <li id="run_cell_in_place"><a href="#">Run in Place</a></li>
113 <li id="run_cell_in_place"><a href="#">Run in Place</a></li>
117 <li id="run_all_cells"><a href="#">Run All</a></li>
114 <li id="run_all_cells"><a href="#">Run All</a></li>
118 <hr/>
115 <hr/>
119 <li id="to_code"><a href="#">Code Cell</a></li>
116 <li id="to_code"><a href="#">Code Cell</a></li>
120 <li id="to_markdown"><a href="#">Markdown Cell</a></li>
117 <li id="to_markdown"><a href="#">Markdown Cell</a></li>
121 <hr/>
118 <hr/>
122 <li id="toggle_output"><a href="#">Toggle Output</a></li>
119 <li id="toggle_output"><a href="#">Toggle Output</a></li>
123 <li id="clear_all_output"><a href="#">Clear All Output</a></li>
120 <li id="clear_all_output"><a href="#">Clear All Output</a></li>
124 </ul>
121 </ul>
125 </li>
122 </li>
126 <li><a href="#">Kernel</a>
123 <li><a href="#">Kernel</a>
127 <ul>
124 <ul>
128 <li id="int_kernel"><a href="#">Interrupt</a></li>
125 <li id="int_kernel"><a href="#">Interrupt</a></li>
129 <li id="restart_kernel"><a href="#">Restart</a></li>
126 <li id="restart_kernel"><a href="#">Restart</a></li>
130 </ul>
127 </ul>
131 </li>
128 </li>
132 <li><a href="#">Help</a>
129 <li><a href="#">Help</a>
133 <ul>
130 <ul>
134 <li><a href="http://ipython.org/documentation.html" target="_blank">IPython Help</a></li>
131 <li><a href="http://ipython.org/documentation.html" target="_blank">IPython Help</a></li>
135 <li><a href="http://ipython.org/ipython-doc/stable/interactive/htmlnotebook.html" target="_blank">Notebook Help</a></li>
132 <li><a href="http://ipython.org/ipython-doc/stable/interactive/htmlnotebook.html" target="_blank">Notebook Help</a></li>
136 <li id="keyboard_shortcuts"><a href="#">Keyboard Shortcuts</a></li>
133 <li id="keyboard_shortcuts"><a href="#">Keyboard Shortcuts</a></li>
137 <hr/>
134 <hr/>
138 <li><a href="https://github.com/ajaxorg/ace/wiki/Default-Keyboard-Shortcuts" target="_blank">Ace Keyboard Shortcuts</a></li>
135 <li><a href="https://github.com/ajaxorg/ace/wiki/Default-Keyboard-Shortcuts" target="_blank">Ace Keyboard Shortcuts</a></li>
139 <hr/>
136 <hr/>
140 <li><a href="http://docs.python.org" target="_blank">Python</a></li>
137 <li><a href="http://docs.python.org" target="_blank">Python</a></li>
141 <li><a href="http://docs.scipy.org/doc/numpy/reference/" target="_blank">NumPy</a></li>
138 <li><a href="http://docs.scipy.org/doc/numpy/reference/" target="_blank">NumPy</a></li>
142 <li><a href="http://docs.scipy.org/doc/scipy/reference/" target="_blank">SciPy</a></li>
139 <li><a href="http://docs.scipy.org/doc/scipy/reference/" target="_blank">SciPy</a></li>
143 <li><a href="http://docs.sympy.org/dev/index.html" target="_blank">SymPy</a></li>
140 <li><a href="http://docs.sympy.org/dev/index.html" target="_blank">SymPy</a></li>
144 <li><a href="http://matplotlib.sourceforge.net/" target="_blank">Matplotlib</a></li>
141 <li><a href="http://matplotlib.sourceforge.net/" target="_blank">Matplotlib</a></li>
145 </ul>
142 </ul>
146 </li>
143 </li>
147 </ul>
144 </ul>
148
145
149 </div>
146 </div>
150
147
151 <div id="main_app">
148 <div id="main_app">
152
149
153 <div id="notebook_panel">
150 <div id="notebook_panel">
154 <div id="notebook"></div>
151 <div id="notebook"></div>
155 <div id="pager_splitter"></div>
152 <div id="pager_splitter"></div>
156 <div id="pager"></div>
153 <div id="pager"></div>
157 </div>
154 </div>
158
155
159 </div>
156 </div>
160
157
161 <div id="fulledit_widget" >
158 <div id="fulledit_widget" >
162 <div id="fulledit_header">
159 <div id="fulledit_header">
163 <button id="close_fulledit">Close</button>
160 <button id="close_fulledit">Close</button>
164 </div>
161 </div>
165 <div id="fulledit_editor">some text</div>
162 <div id="fulledit_editor">some text</div>
166 </div>
163 </div>
167
164
168 <script src="/static/jquery/js/jquery-1.7.1.min.js" type="text/javascript" charset="utf-8"></script>
165 <script src="/static/jquery/js/jquery-1.7.1.min.js" type="text/javascript" charset="utf-8"></script>
169 <script src="/static/jquery/js/jquery-ui.min.js" type="text/javascript" charset="utf-8"></script>
166 <script src="/static/jquery/js/jquery-ui.min.js" type="text/javascript" charset="utf-8"></script>
170
167
171 <script src="/static/codemirror/lib/codemirror.js" charset="utf-8"></script>
168 <script src="/static/codemirror/lib/codemirror.js" charset="utf-8"></script>
172 <script src="/static/codemirror/mode/python/python.js" charset="utf-8"></script>
169 <script src="/static/codemirror/mode/python/python.js" charset="utf-8"></script>
173 <script src="/static/codemirror/mode/htmlmixed/htmlmixed.js" charset="utf-8"></script>
170 <script src="/static/codemirror/mode/htmlmixed/htmlmixed.js" charset="utf-8"></script>
174 <script src="/static/codemirror/mode/xml/xml.js" charset="utf-8"></script>
171 <script src="/static/codemirror/mode/xml/xml.js" charset="utf-8"></script>
175 <script src="/static/codemirror/mode/javascript/javascript.js" charset="utf-8"></script>
172 <script src="/static/codemirror/mode/javascript/javascript.js" charset="utf-8"></script>
176 <script src="/static/codemirror/mode/css/css.js" charset="utf-8"></script>
173 <script src="/static/codemirror/mode/css/css.js" charset="utf-8"></script>
177 <script src="/static/codemirror/mode/rst/rst.js" charset="utf-8"></script>
174 <script src="/static/codemirror/mode/rst/rst.js" charset="utf-8"></script>
178 <script src="/static/codemirror/mode/markdown/markdown.js" charset="utf-8"></script>
175 <script src="/static/codemirror/mode/markdown/markdown.js" charset="utf-8"></script>
179
176
180 <script src="/static/ace/ace.js" type="text/javascript" charset="utf-8"></script>
177 <script src="/static/ace/ace.js" type="text/javascript" charset="utf-8"></script>
181 <script src="/static/ace/mode-python.js" type="text/javascript" charset="utf-8"></script>
178 <script src="/static/ace/mode-python.js" type="text/javascript" charset="utf-8"></script>
182 <script src="/static/ace/theme-textmate.js" type="text/javascript" charset="utf-8"></script>
179 <script src="/static/ace/theme-textmate.js" type="text/javascript" charset="utf-8"></script>
183
180
184 <script src="/static/pagedown/Markdown.Converter.js" charset="utf-8"></script>
181 <script src="/static/pagedown/Markdown.Converter.js" charset="utf-8"></script>
185
182
186 <script src="/static/prettify/prettify.js" charset="utf-8"></script>
183 <script src="/static/prettify/prettify.js" charset="utf-8"></script>
187 <script src="/static/dateformat/date.format.js" charset="utf-8"></script>
184 <script src="/static/dateformat/date.format.js" charset="utf-8"></script>
188
185
189 <script src="/static/js/namespace.js" type="text/javascript" charset="utf-8"></script>
186 <script src="/static/js/namespace.js" type="text/javascript" charset="utf-8"></script>
190 <script src="/static/js/utils.js" type="text/javascript" charset="utf-8"></script>
187 <script src="/static/js/utils.js" type="text/javascript" charset="utf-8"></script>
191 <script src="/static/js/cell.js" type="text/javascript" charset="utf-8"></script>
188 <script src="/static/js/cell.js" type="text/javascript" charset="utf-8"></script>
192 <script src="/static/js/codecell.js" type="text/javascript" charset="utf-8"></script>
189 <script src="/static/js/codecell.js" type="text/javascript" charset="utf-8"></script>
193 <script src="/static/js/textcell.js" type="text/javascript" charset="utf-8"></script>
190 <script src="/static/js/textcell.js" type="text/javascript" charset="utf-8"></script>
194 <script src="/static/js/kernel.js" type="text/javascript" charset="utf-8"></script>
191 <script src="/static/js/kernel.js" type="text/javascript" charset="utf-8"></script>
195 <script src="/static/js/kernelstatus.js" type="text/javascript" charset="utf-8"></script>
192 <script src="/static/js/kernelstatus.js" type="text/javascript" charset="utf-8"></script>
196 <script src="/static/js/layout.js" type="text/javascript" charset="utf-8"></script>
193 <script src="/static/js/layout.js" type="text/javascript" charset="utf-8"></script>
197 <script src="/static/js/savewidget.js" type="text/javascript" charset="utf-8"></script>
194 <script src="/static/js/savewidget.js" type="text/javascript" charset="utf-8"></script>
198 <script src="/static/js/quickhelp.js" type="text/javascript" charset="utf-8"></script>
195 <script src="/static/js/quickhelp.js" type="text/javascript" charset="utf-8"></script>
199 <script src="/static/js/loginwidget.js" type="text/javascript" charset="utf-8"></script>
196 <script src="/static/js/loginwidget.js" type="text/javascript" charset="utf-8"></script>
200 <script src="/static/js/pager.js" type="text/javascript" charset="utf-8"></script>
197 <script src="/static/js/pager.js" type="text/javascript" charset="utf-8"></script>
201 <script src="/static/js/menubar.js" type="text/javascript" charset="utf-8"></script>
198 <script src="/static/js/menubar.js" type="text/javascript" charset="utf-8"></script>
202 <script src="/static/js/notebook.js" type="text/javascript" charset="utf-8"></script>
199 <script src="/static/js/notebook.js" type="text/javascript" charset="utf-8"></script>
203 <script src="/static/js/notebookmain.js" type="text/javascript" charset="utf-8"></script>
200 <script src="/static/js/notebookmain.js" type="text/javascript" charset="utf-8"></script>
204 <script src="/static/js/fulleditwidget.js" type="text/javascript" charset="utf-8"></script>
201 <script src="/static/js/fulleditwidget.js" type="text/javascript" charset="utf-8"></script>
205
202
206 </body>
203 </body>
207
204
208 </html>
205 </html>
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now