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