##// END OF EJS Templates
update codemirror to latest version
marcink -
r4042:af3c746b default
parent child Browse files
Show More
@@ -1,246 +1,248 b''
1 /* BASICS */
1 /* BASICS */
2
2
3 .CodeMirror {
3 .CodeMirror {
4 /* Set height, width, borders, and global font properties here */
4 /* Set height, width, borders, and global font properties here */
5 font-family: monospace;
5 font-family: monospace;
6 height: 300px;
6 height: 300px;
7 }
7 }
8 .CodeMirror-scroll {
8 .CodeMirror-scroll {
9 /* Set scrolling behaviour here */
9 /* Set scrolling behaviour here */
10 overflow: auto;
10 overflow: auto;
11 }
11 }
12
12
13 /* PADDING */
13 /* PADDING */
14
14
15 .CodeMirror-lines {
15 .CodeMirror-lines {
16 padding: 4px 0; /* Vertical padding around content */
16 padding: 4px 0; /* Vertical padding around content */
17 }
17 }
18 .CodeMirror pre {
18 .CodeMirror pre {
19 padding: 0 4px; /* Horizontal padding of content */
19 padding: 0 4px; /* Horizontal padding of content */
20 }
20 }
21
21
22 .CodeMirror-scrollbar-filler {
22 .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
23 background-color: white; /* The little square between H and V scrollbars */
23 background-color: white; /* The little square between H and V scrollbars */
24 }
24 }
25
25
26 /* GUTTER */
26 /* GUTTER */
27
27
28 .CodeMirror-gutters {
28 .CodeMirror-gutters {
29 border-right: 1px solid #ddd;
29 border-right: 1px solid #ddd;
30 background-color: #f7f7f7;
30 background-color: #f7f7f7;
31 white-space: nowrap;
31 }
32 }
32 .CodeMirror-linenumbers {}
33 .CodeMirror-linenumbers {}
33 .CodeMirror-linenumber {
34 .CodeMirror-linenumber {
34 padding: 0 3px 0 5px;
35 padding: 0 3px 0 5px;
35 min-width: 20px;
36 min-width: 20px;
36 text-align: right;
37 text-align: right;
37 color: #999;
38 color: #999;
38 }
39 }
39
40
40 /* CURSOR */
41 /* CURSOR */
41
42
42 .CodeMirror div.CodeMirror-cursor {
43 .CodeMirror div.CodeMirror-cursor {
43 border-left: 1px solid black;
44 border-left: 1px solid black;
44 z-index: 3;
45 z-index: 3;
45 }
46 }
46 /* Shown when moving in bi-directional text */
47 /* Shown when moving in bi-directional text */
47 .CodeMirror div.CodeMirror-secondarycursor {
48 .CodeMirror div.CodeMirror-secondarycursor {
48 border-left: 1px solid silver;
49 border-left: 1px solid silver;
49 }
50 }
50 .CodeMirror.cm-keymap-fat-cursor div.CodeMirror-cursor {
51 .CodeMirror.cm-keymap-fat-cursor div.CodeMirror-cursor {
51 width: auto;
52 width: auto;
52 border: 0;
53 border: 0;
53 background: #7e7;
54 background: #7e7;
54 z-index: 1;
55 z-index: 1;
55 }
56 }
56 /* Can style cursor different in overwrite (non-insert) mode */
57 /* Can style cursor different in overwrite (non-insert) mode */
57 .CodeMirror div.CodeMirror-cursor.CodeMirror-overwrite {}
58 .CodeMirror div.CodeMirror-cursor.CodeMirror-overwrite {}
58
59
59 .cm-tab { display: inline-block; }
60 .cm-tab { display: inline-block; }
60
61
61 /* DEFAULT THEME */
62 /* DEFAULT THEME */
62
63
63 .cm-s-default .cm-keyword {color: #708;}
64 .cm-s-default .cm-keyword {color: #708;}
64 .cm-s-default .cm-atom {color: #219;}
65 .cm-s-default .cm-atom {color: #219;}
65 .cm-s-default .cm-number {color: #164;}
66 .cm-s-default .cm-number {color: #164;}
66 .cm-s-default .cm-def {color: #00f;}
67 .cm-s-default .cm-def {color: #00f;}
67 .cm-s-default .cm-variable {color: black;}
68 .cm-s-default .cm-variable {color: black;}
68 .cm-s-default .cm-variable-2 {color: #05a;}
69 .cm-s-default .cm-variable-2 {color: #05a;}
69 .cm-s-default .cm-variable-3 {color: #085;}
70 .cm-s-default .cm-variable-3 {color: #085;}
70 .cm-s-default .cm-property {color: black;}
71 .cm-s-default .cm-property {color: black;}
71 .cm-s-default .cm-operator {color: black;}
72 .cm-s-default .cm-operator {color: black;}
72 .cm-s-default .cm-comment {color: #a50;}
73 .cm-s-default .cm-comment {color: #a50;}
73 .cm-s-default .cm-string {color: #a11;}
74 .cm-s-default .cm-string {color: #a11;}
74 .cm-s-default .cm-string-2 {color: #f50;}
75 .cm-s-default .cm-string-2 {color: #f50;}
75 .cm-s-default .cm-meta {color: #555;}
76 .cm-s-default .cm-meta {color: #555;}
76 .cm-s-default .cm-error {color: #f00;}
77 .cm-s-default .cm-error {color: #f00;}
77 .cm-s-default .cm-qualifier {color: #555;}
78 .cm-s-default .cm-qualifier {color: #555;}
78 .cm-s-default .cm-builtin {color: #30a;}
79 .cm-s-default .cm-builtin {color: #30a;}
79 .cm-s-default .cm-bracket {color: #997;}
80 .cm-s-default .cm-bracket {color: #997;}
80 .cm-s-default .cm-tag {color: #170;}
81 .cm-s-default .cm-tag {color: #170;}
81 .cm-s-default .cm-attribute {color: #00c;}
82 .cm-s-default .cm-attribute {color: #00c;}
82 .cm-s-default .cm-header {color: blue;}
83 .cm-s-default .cm-header {color: blue;}
83 .cm-s-default .cm-quote {color: #090;}
84 .cm-s-default .cm-quote {color: #090;}
84 .cm-s-default .cm-hr {color: #999;}
85 .cm-s-default .cm-hr {color: #999;}
85 .cm-s-default .cm-link {color: #00c;}
86 .cm-s-default .cm-link {color: #00c;}
86
87
87 .cm-negative {color: #d44;}
88 .cm-negative {color: #d44;}
88 .cm-positive {color: #292;}
89 .cm-positive {color: #292;}
89 .cm-header, .cm-strong {font-weight: bold;}
90 .cm-header, .cm-strong {font-weight: bold;}
90 .cm-em {font-style: italic;}
91 .cm-em {font-style: italic;}
91 .cm-link {text-decoration: underline;}
92 .cm-link {text-decoration: underline;}
92
93
93 .cm-invalidchar {color: #f00;}
94 .cm-invalidchar {color: #f00;}
94
95
95 div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
96 div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
96 div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
97 div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
97
98
98 /* STOP */
99 /* STOP */
99
100
100 /* The rest of this file contains styles related to the mechanics of
101 /* The rest of this file contains styles related to the mechanics of
101 the editor. You probably shouldn't touch them. */
102 the editor. You probably shouldn't touch them. */
102
103
103 .CodeMirror {
104 .CodeMirror {
104 line-height: 1;
105 line-height: 1;
105 position: relative;
106 position: relative;
106 overflow: hidden;
107 overflow: hidden;
107 background: white;
108 background: white;
108 color: black;
109 color: black;
109 }
110 }
110
111
111 .CodeMirror-scroll {
112 .CodeMirror-scroll {
112 /* 30px is the magic margin used to hide the element's real scrollbars */
113 /* 30px is the magic margin used to hide the element's real scrollbars */
113 /* See overflow: hidden in .CodeMirror */
114 /* See overflow: hidden in .CodeMirror */
114 margin-bottom: -30px; margin-right: -30px;
115 margin-bottom: -30px; margin-right: -30px;
115 padding-bottom: 30px; padding-right: 30px;
116 padding-bottom: 30px; padding-right: 30px;
116 height: 100%;
117 height: 100%;
117 outline: none; /* Prevent dragging from highlighting the element */
118 outline: none; /* Prevent dragging from highlighting the element */
118 position: relative;
119 position: relative;
119 }
120 }
120 .CodeMirror-sizer {
121 .CodeMirror-sizer {
121 position: relative;
122 position: relative;
122 }
123 }
123
124
124 /* The fake, visible scrollbars. Used to force redraw during scrolling
125 /* The fake, visible scrollbars. Used to force redraw during scrolling
125 before actuall scrolling happens, thus preventing shaking and
126 before actuall scrolling happens, thus preventing shaking and
126 flickering artifacts. */
127 flickering artifacts. */
127 .CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler {
128 .CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
128 position: absolute;
129 position: absolute;
129 z-index: 6;
130 z-index: 6;
130 display: none;
131 display: none;
131 }
132 }
132 .CodeMirror-vscrollbar {
133 .CodeMirror-vscrollbar {
133 right: 0; top: 0;
134 right: 0; top: 0;
134 overflow-x: hidden;
135 overflow-x: hidden;
135 overflow-y: scroll;
136 overflow-y: scroll;
136 }
137 }
137 .CodeMirror-hscrollbar {
138 .CodeMirror-hscrollbar {
138 bottom: 0; left: 0;
139 bottom: 0; left: 0;
139 overflow-y: hidden;
140 overflow-y: hidden;
140 overflow-x: scroll;
141 overflow-x: scroll;
141 }
142 }
142 .CodeMirror-scrollbar-filler {
143 .CodeMirror-scrollbar-filler {
143 right: 0; bottom: 0;
144 right: 0; bottom: 0;
144 z-index: 6;
145 }
146 .CodeMirror-gutter-filler {
147 left: 0; bottom: 0;
145 }
148 }
146
149
147 .CodeMirror-gutters {
150 .CodeMirror-gutters {
148 position: absolute; left: 0; top: 0;
151 position: absolute; left: 0; top: 0;
149 height: 100%;
150 padding-bottom: 30px;
152 padding-bottom: 30px;
151 z-index: 3;
153 z-index: 3;
152 }
154 }
153 .CodeMirror-gutter {
155 .CodeMirror-gutter {
156 white-space: normal;
154 height: 100%;
157 height: 100%;
155 padding-bottom: 30px;
158 padding-bottom: 30px;
156 margin-bottom: -32px;
159 margin-bottom: -32px;
157 display: inline-block;
160 display: inline-block;
158 /* Hack to make IE7 behave */
161 /* Hack to make IE7 behave */
159 *zoom:1;
162 *zoom:1;
160 *display:inline;
163 *display:inline;
161 }
164 }
162 .CodeMirror-gutter-elt {
165 .CodeMirror-gutter-elt {
163 position: absolute;
166 position: absolute;
164 cursor: default;
167 cursor: default;
165 z-index: 4;
168 z-index: 4;
166 }
169 }
167
170
168 .CodeMirror-lines {
171 .CodeMirror-lines {
169 cursor: text;
172 cursor: text;
170 }
173 }
171 .CodeMirror pre {
174 .CodeMirror pre {
172 /* Reset some styles that the rest of the page might have set */
175 /* Reset some styles that the rest of the page might have set */
173 -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0;
176 -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0;
174 border-width: 0;
177 border-width: 0;
175 background: transparent;
178 background: transparent;
176 font-family: inherit;
179 font-family: inherit;
177 font-size: inherit;
180 font-size: inherit;
178 margin: 0;
181 margin: 0;
179 white-space: pre;
182 white-space: pre;
180 word-wrap: normal;
183 word-wrap: normal;
181 line-height: inherit;
184 line-height: inherit;
182 color: inherit;
185 color: inherit;
183 z-index: 2;
186 z-index: 2;
184 position: relative;
187 position: relative;
185 overflow: visible;
188 overflow: visible;
186 }
189 }
187 .CodeMirror-wrap pre {
190 .CodeMirror-wrap pre {
188 word-wrap: break-word;
191 word-wrap: break-word;
189 white-space: pre-wrap;
192 white-space: pre-wrap;
190 word-break: normal;
193 word-break: normal;
191 }
194 }
192 .CodeMirror-linebackground {
195 .CodeMirror-linebackground {
193 position: absolute;
196 position: absolute;
194 left: 0; right: 0; top: 0; bottom: 0;
197 left: 0; right: 0; top: 0; bottom: 0;
195 z-index: 0;
198 z-index: 0;
196 }
199 }
197
200
198 .CodeMirror-linewidget {
201 .CodeMirror-linewidget {
199 position: relative;
202 position: relative;
200 z-index: 2;
203 z-index: 2;
201 overflow: auto;
204 overflow: auto;
202 }
205 }
203
206
204 .CodeMirror-widget {
207 .CodeMirror-widget {
205 display: inline-block;
206 }
208 }
207
209
208 .CodeMirror-wrap .CodeMirror-scroll {
210 .CodeMirror-wrap .CodeMirror-scroll {
209 overflow-x: hidden;
211 overflow-x: hidden;
210 }
212 }
211
213
212 .CodeMirror-measure {
214 .CodeMirror-measure {
213 position: absolute;
215 position: absolute;
214 width: 100%; height: 0px;
216 width: 100%; height: 0px;
215 overflow: hidden;
217 overflow: hidden;
216 visibility: hidden;
218 visibility: hidden;
217 }
219 }
218 .CodeMirror-measure pre { position: static; }
220 .CodeMirror-measure pre { position: static; }
219
221
220 .CodeMirror div.CodeMirror-cursor {
222 .CodeMirror div.CodeMirror-cursor {
221 position: absolute;
223 position: absolute;
222 visibility: hidden;
224 visibility: hidden;
223 border-right: none;
225 border-right: none;
224 width: 0;
226 width: 0;
225 }
227 }
226 .CodeMirror-focused div.CodeMirror-cursor {
228 .CodeMirror-focused div.CodeMirror-cursor {
227 visibility: visible;
229 visibility: visible;
228 }
230 }
229
231
230 .CodeMirror-selected { background: #d9d9d9; }
232 .CodeMirror-selected { background: #d9d9d9; }
231 .CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
233 .CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
232
234
233 .cm-searching {
235 .cm-searching {
234 background: #ffa;
236 background: #ffa;
235 background: rgba(255, 255, 0, .4);
237 background: rgba(255, 255, 0, .4);
236 }
238 }
237
239
238 /* IE7 hack to prevent it from returning funny offsetTops on the spans */
240 /* IE7 hack to prevent it from returning funny offsetTops on the spans */
239 .CodeMirror span { *vertical-align: text-bottom; }
241 .CodeMirror span { *vertical-align: text-bottom; }
240
242
241 @media print {
243 @media print {
242 /* Hide the cursor when printing */
244 /* Hide the cursor when printing */
243 .CodeMirror div.CodeMirror-cursor {
245 .CodeMirror div.CodeMirror-cursor {
244 visibility: hidden;
246 visibility: hidden;
245 }
247 }
246 }
248 }
This diff has been collapsed as it changes many lines, (870 lines changed) Show them Hide them
@@ -1,5583 +1,5715 b''
1 // CodeMirror version 3.14
2 //
1 // CodeMirror is the only global var we claim
3 // CodeMirror is the only global var we claim
2 window.CodeMirror = (function() {
4 window.CodeMirror = (function() {
3 "use strict";
5 "use strict";
4
6
5 // BROWSER SNIFFING
7 // BROWSER SNIFFING
6
8
7 // Crude, but necessary to handle a number of hard-to-feature-detect
9 // Crude, but necessary to handle a number of hard-to-feature-detect
8 // bugs and behavior differences.
10 // bugs and behavior differences.
9 var gecko = /gecko\/\d/i.test(navigator.userAgent);
11 var gecko = /gecko\/\d/i.test(navigator.userAgent);
10 var ie = /MSIE \d/.test(navigator.userAgent);
12 var ie = /MSIE \d/.test(navigator.userAgent);
11 var ie_lt8 = ie && (document.documentMode == null || document.documentMode < 8);
13 var ie_lt8 = ie && (document.documentMode == null || document.documentMode < 8);
12 var ie_lt9 = ie && (document.documentMode == null || document.documentMode < 9);
14 var ie_lt9 = ie && (document.documentMode == null || document.documentMode < 9);
13 var webkit = /WebKit\//.test(navigator.userAgent);
15 var webkit = /WebKit\//.test(navigator.userAgent);
14 var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(navigator.userAgent);
16 var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(navigator.userAgent);
15 var chrome = /Chrome\//.test(navigator.userAgent);
17 var chrome = /Chrome\//.test(navigator.userAgent);
16 var opera = /Opera\//.test(navigator.userAgent);
18 var opera = /Opera\//.test(navigator.userAgent);
17 var safari = /Apple Computer/.test(navigator.vendor);
19 var safari = /Apple Computer/.test(navigator.vendor);
18 var khtml = /KHTML\//.test(navigator.userAgent);
20 var khtml = /KHTML\//.test(navigator.userAgent);
19 var mac_geLion = /Mac OS X 1\d\D([7-9]|\d\d)\D/.test(navigator.userAgent);
21 var mac_geLion = /Mac OS X 1\d\D([7-9]|\d\d)\D/.test(navigator.userAgent);
20 var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(navigator.userAgent);
22 var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(navigator.userAgent);
21 var phantom = /PhantomJS/.test(navigator.userAgent);
23 var phantom = /PhantomJS/.test(navigator.userAgent);
22
24
23 var ios = /AppleWebKit/.test(navigator.userAgent) && /Mobile\/\w+/.test(navigator.userAgent);
25 var ios = /AppleWebKit/.test(navigator.userAgent) && /Mobile\/\w+/.test(navigator.userAgent);
24 // This is woefully incomplete. Suggestions for alternative methods welcome.
26 // This is woefully incomplete. Suggestions for alternative methods welcome.
25 var mobile = ios || /Android|webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(navigator.userAgent);
27 var mobile = ios || /Android|webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(navigator.userAgent);
26 var mac = ios || /Mac/.test(navigator.platform);
28 var mac = ios || /Mac/.test(navigator.platform);
27 var windows = /windows/i.test(navigator.platform);
29 var windows = /windows/i.test(navigator.platform);
28
30
29 var opera_version = opera && navigator.userAgent.match(/Version\/(\d*\.\d*)/);
31 var opera_version = opera && navigator.userAgent.match(/Version\/(\d*\.\d*)/);
30 if (opera_version) opera_version = Number(opera_version[1]);
32 if (opera_version) opera_version = Number(opera_version[1]);
31 // Some browsers use the wrong event properties to signal cmd/ctrl on OS X
33 // Some browsers use the wrong event properties to signal cmd/ctrl on OS X
32 var flipCtrlCmd = mac && (qtwebkit || opera && (opera_version == null || opera_version < 12.11));
34 var flipCtrlCmd = mac && (qtwebkit || opera && (opera_version == null || opera_version < 12.11));
33 var captureMiddleClick = gecko || (ie && !ie_lt9);
35 var captureMiddleClick = gecko || (ie && !ie_lt9);
34
36
35 // Optimize some code when these features are not used
37 // Optimize some code when these features are not used
36 var sawReadOnlySpans = false, sawCollapsedSpans = false;
38 var sawReadOnlySpans = false, sawCollapsedSpans = false;
37
39
38 // CONSTRUCTOR
40 // CONSTRUCTOR
39
41
40 function CodeMirror(place, options) {
42 function CodeMirror(place, options) {
41 if (!(this instanceof CodeMirror)) return new CodeMirror(place, options);
43 if (!(this instanceof CodeMirror)) return new CodeMirror(place, options);
42
44
43 this.options = options = options || {};
45 this.options = options = options || {};
44 // Determine effective options based on given values and defaults.
46 // Determine effective options based on given values and defaults.
45 for (var opt in defaults) if (!options.hasOwnProperty(opt) && defaults.hasOwnProperty(opt))
47 for (var opt in defaults) if (!options.hasOwnProperty(opt) && defaults.hasOwnProperty(opt))
46 options[opt] = defaults[opt];
48 options[opt] = defaults[opt];
47 setGuttersForLineNumbers(options);
49 setGuttersForLineNumbers(options);
48
50
49 var docStart = typeof options.value == "string" ? 0 : options.value.first;
51 var docStart = typeof options.value == "string" ? 0 : options.value.first;
50 var display = this.display = makeDisplay(place, docStart);
52 var display = this.display = makeDisplay(place, docStart);
51 display.wrapper.CodeMirror = this;
53 display.wrapper.CodeMirror = this;
52 updateGutters(this);
54 updateGutters(this);
53 if (options.autofocus && !mobile) focusInput(this);
55 if (options.autofocus && !mobile) focusInput(this);
54
56
55 this.state = {keyMaps: [],
57 this.state = {keyMaps: [],
56 overlays: [],
58 overlays: [],
57 modeGen: 0,
59 modeGen: 0,
58 overwrite: false, focused: false,
60 overwrite: false, focused: false,
59 suppressEdits: false, pasteIncoming: false,
61 suppressEdits: false, pasteIncoming: false,
60 draggingText: false,
62 draggingText: false,
61 highlight: new Delayed()};
63 highlight: new Delayed()};
62
64
63 themeChanged(this);
65 themeChanged(this);
64 if (options.lineWrapping)
66 if (options.lineWrapping)
65 this.display.wrapper.className += " CodeMirror-wrap";
67 this.display.wrapper.className += " CodeMirror-wrap";
66
68
67 var doc = options.value;
69 var doc = options.value;
68 if (typeof doc == "string") doc = new Doc(options.value, options.mode);
70 if (typeof doc == "string") doc = new Doc(options.value, options.mode);
69 operation(this, attachDoc)(this, doc);
71 operation(this, attachDoc)(this, doc);
70
72
71 // Override magic textarea content restore that IE sometimes does
73 // Override magic textarea content restore that IE sometimes does
72 // on our hidden textarea on reload
74 // on our hidden textarea on reload
73 if (ie) setTimeout(bind(resetInput, this, true), 20);
75 if (ie) setTimeout(bind(resetInput, this, true), 20);
74
76
75 registerEventHandlers(this);
77 registerEventHandlers(this);
76 // IE throws unspecified error in certain cases, when
78 // IE throws unspecified error in certain cases, when
77 // trying to access activeElement before onload
79 // trying to access activeElement before onload
78 var hasFocus; try { hasFocus = (document.activeElement == display.input); } catch(e) { }
80 var hasFocus; try { hasFocus = (document.activeElement == display.input); } catch(e) { }
79 if (hasFocus || (options.autofocus && !mobile)) setTimeout(bind(onFocus, this), 20);
81 if (hasFocus || (options.autofocus && !mobile)) setTimeout(bind(onFocus, this), 20);
80 else onBlur(this);
82 else onBlur(this);
81
83
82 operation(this, function() {
84 operation(this, function() {
83 for (var opt in optionHandlers)
85 for (var opt in optionHandlers)
84 if (optionHandlers.propertyIsEnumerable(opt))
86 if (optionHandlers.propertyIsEnumerable(opt))
85 optionHandlers[opt](this, options[opt], Init);
87 optionHandlers[opt](this, options[opt], Init);
86 for (var i = 0; i < initHooks.length; ++i) initHooks[i](this);
88 for (var i = 0; i < initHooks.length; ++i) initHooks[i](this);
87 })();
89 })();
88 }
90 }
89
91
90 // DISPLAY CONSTRUCTOR
92 // DISPLAY CONSTRUCTOR
91
93
92 function makeDisplay(place, docStart) {
94 function makeDisplay(place, docStart) {
93 var d = {};
95 var d = {};
94
96
95 var input = d.input = elt("textarea", null, null, "position: absolute; padding: 0; width: 1px; height: 1em; outline: none; font-size: 4px;");
97 var input = d.input = elt("textarea", null, null, "position: absolute; padding: 0; width: 1px; height: 1em; outline: none; font-size: 4px;");
96 if (webkit) input.style.width = "1000px";
98 if (webkit) input.style.width = "1000px";
97 else input.setAttribute("wrap", "off");
99 else input.setAttribute("wrap", "off");
98 // if border: 0; -- iOS fails to open keyboard (issue #1287)
100 // if border: 0; -- iOS fails to open keyboard (issue #1287)
99 if (ios) input.style.border = "1px solid black";
101 if (ios) input.style.border = "1px solid black";
100 input.setAttribute("autocorrect", "off"); input.setAttribute("autocapitalize", "off");
102 input.setAttribute("autocorrect", "off"); input.setAttribute("autocapitalize", "off"); input.setAttribute("spellcheck", "false");
101
103
102 // Wraps and hides input textarea
104 // Wraps and hides input textarea
103 d.inputDiv = elt("div", [input], null, "overflow: hidden; position: relative; width: 3px; height: 0px;");
105 d.inputDiv = elt("div", [input], null, "overflow: hidden; position: relative; width: 3px; height: 0px;");
104 // The actual fake scrollbars.
106 // The actual fake scrollbars.
105 d.scrollbarH = elt("div", [elt("div", null, null, "height: 1px")], "CodeMirror-hscrollbar");
107 d.scrollbarH = elt("div", [elt("div", null, null, "height: 1px")], "CodeMirror-hscrollbar");
106 d.scrollbarV = elt("div", [elt("div", null, null, "width: 1px")], "CodeMirror-vscrollbar");
108 d.scrollbarV = elt("div", [elt("div", null, null, "width: 1px")], "CodeMirror-vscrollbar");
107 d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler");
109 d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler");
110 d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler");
108 // DIVs containing the selection and the actual code
111 // DIVs containing the selection and the actual code
109 d.lineDiv = elt("div");
112 d.lineDiv = elt("div", null, "CodeMirror-code");
110 d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1");
113 d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1");
111 // Blinky cursor, and element used to ensure cursor fits at the end of a line
114 // Blinky cursor, and element used to ensure cursor fits at the end of a line
112 d.cursor = elt("div", "\u00a0", "CodeMirror-cursor");
115 d.cursor = elt("div", "\u00a0", "CodeMirror-cursor");
113 // Secondary cursor, shown when on a 'jump' in bi-directional text
116 // Secondary cursor, shown when on a 'jump' in bi-directional text
114 d.otherCursor = elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor");
117 d.otherCursor = elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor");
115 // Used to measure text size
118 // Used to measure text size
116 d.measure = elt("div", null, "CodeMirror-measure");
119 d.measure = elt("div", null, "CodeMirror-measure");
117 // Wraps everything that needs to exist inside the vertically-padded coordinate system
120 // Wraps everything that needs to exist inside the vertically-padded coordinate system
118 d.lineSpace = elt("div", [d.measure, d.selectionDiv, d.lineDiv, d.cursor, d.otherCursor],
121 d.lineSpace = elt("div", [d.measure, d.selectionDiv, d.lineDiv, d.cursor, d.otherCursor],
119 null, "position: relative; outline: none");
122 null, "position: relative; outline: none");
120 // Moved around its parent to cover visible view
123 // Moved around its parent to cover visible view
121 d.mover = elt("div", [elt("div", [d.lineSpace], "CodeMirror-lines")], null, "position: relative");
124 d.mover = elt("div", [elt("div", [d.lineSpace], "CodeMirror-lines")], null, "position: relative");
122 // Set to the height of the text, causes scrolling
125 // Set to the height of the text, causes scrolling
123 d.sizer = elt("div", [d.mover], "CodeMirror-sizer");
126 d.sizer = elt("div", [d.mover], "CodeMirror-sizer");
124 // D is needed because behavior of elts with overflow: auto and padding is inconsistent across browsers
127 // D is needed because behavior of elts with overflow: auto and padding is inconsistent across browsers
125 d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerCutOff + "px; width: 1px;");
128 d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerCutOff + "px; width: 1px;");
126 // Will contain the gutters, if any
129 // Will contain the gutters, if any
127 d.gutters = elt("div", null, "CodeMirror-gutters");
130 d.gutters = elt("div", null, "CodeMirror-gutters");
128 d.lineGutter = null;
131 d.lineGutter = null;
129 // Helper element to properly size the gutter backgrounds
130 var scrollerInner = elt("div", [d.sizer, d.heightForcer, d.gutters], null, "position: relative; min-height: 100%");
131 // Provides scrolling
132 // Provides scrolling
132 d.scroller = elt("div", [scrollerInner], "CodeMirror-scroll");
133 d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-scroll");
133 d.scroller.setAttribute("tabIndex", "-1");
134 d.scroller.setAttribute("tabIndex", "-1");
134 // The element in which the editor lives.
135 // The element in which the editor lives.
135 d.wrapper = elt("div", [d.inputDiv, d.scrollbarH, d.scrollbarV,
136 d.wrapper = elt("div", [d.inputDiv, d.scrollbarH, d.scrollbarV,
136 d.scrollbarFiller, d.scroller], "CodeMirror");
137 d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror");
137 // Work around IE7 z-index bug
138 // Work around IE7 z-index bug
138 if (ie_lt8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; }
139 if (ie_lt8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; }
139 if (place.appendChild) place.appendChild(d.wrapper); else place(d.wrapper);
140 if (place.appendChild) place.appendChild(d.wrapper); else place(d.wrapper);
140
141
141 // Needed to hide big blue blinking cursor on Mobile Safari
142 // Needed to hide big blue blinking cursor on Mobile Safari
142 if (ios) input.style.width = "0px";
143 if (ios) input.style.width = "0px";
143 if (!webkit) d.scroller.draggable = true;
144 if (!webkit) d.scroller.draggable = true;
144 // Needed to handle Tab key in KHTML
145 // Needed to handle Tab key in KHTML
145 if (khtml) { d.inputDiv.style.height = "1px"; d.inputDiv.style.position = "absolute"; }
146 if (khtml) { d.inputDiv.style.height = "1px"; d.inputDiv.style.position = "absolute"; }
146 // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
147 // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
147 else if (ie_lt8) d.scrollbarH.style.minWidth = d.scrollbarV.style.minWidth = "18px";
148 else if (ie_lt8) d.scrollbarH.style.minWidth = d.scrollbarV.style.minWidth = "18px";
148
149
149 // Current visible range (may be bigger than the view window).
150 // Current visible range (may be bigger than the view window).
150 d.viewOffset = d.lastSizeC = 0;
151 d.viewOffset = d.lastSizeC = 0;
151 d.showingFrom = d.showingTo = docStart;
152 d.showingFrom = d.showingTo = docStart;
152
153
153 // Used to only resize the line number gutter when necessary (when
154 // Used to only resize the line number gutter when necessary (when
154 // the amount of lines crosses a boundary that makes its width change)
155 // the amount of lines crosses a boundary that makes its width change)
155 d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null;
156 d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null;
156 // See readInput and resetInput
157 // See readInput and resetInput
157 d.prevInput = "";
158 d.prevInput = "";
158 // Set to true when a non-horizontal-scrolling widget is added. As
159 // Set to true when a non-horizontal-scrolling widget is added. As
159 // an optimization, widget aligning is skipped when d is false.
160 // an optimization, widget aligning is skipped when d is false.
160 d.alignWidgets = false;
161 d.alignWidgets = false;
161 // Flag that indicates whether we currently expect input to appear
162 // Flag that indicates whether we currently expect input to appear
162 // (after some event like 'keypress' or 'input') and are polling
163 // (after some event like 'keypress' or 'input') and are polling
163 // intensively.
164 // intensively.
164 d.pollingFast = false;
165 d.pollingFast = false;
165 // Self-resetting timeout for the poller
166 // Self-resetting timeout for the poller
166 d.poll = new Delayed();
167 d.poll = new Delayed();
167
168
168 d.cachedCharWidth = d.cachedTextHeight = null;
169 d.cachedCharWidth = d.cachedTextHeight = null;
169 d.measureLineCache = [];
170 d.measureLineCache = [];
170 d.measureLineCachePos = 0;
171 d.measureLineCachePos = 0;
171
172
172 // Tracks when resetInput has punted to just putting a short
173 // Tracks when resetInput has punted to just putting a short
173 // string instead of the (large) selection.
174 // string instead of the (large) selection.
174 d.inaccurateSelection = false;
175 d.inaccurateSelection = false;
175
176
176 // Tracks the maximum line length so that the horizontal scrollbar
177 // Tracks the maximum line length so that the horizontal scrollbar
177 // can be kept static when scrolling.
178 // can be kept static when scrolling.
178 d.maxLine = null;
179 d.maxLine = null;
179 d.maxLineLength = 0;
180 d.maxLineLength = 0;
180 d.maxLineChanged = false;
181 d.maxLineChanged = false;
181
182
182 // Used for measuring wheel scrolling granularity
183 // Used for measuring wheel scrolling granularity
183 d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null;
184 d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null;
184
185
185 return d;
186 return d;
186 }
187 }
187
188
188 // STATE UPDATES
189 // STATE UPDATES
189
190
190 // Used to get the editor into a consistent state again when options change.
191 // Used to get the editor into a consistent state again when options change.
191
192
192 function loadMode(cm) {
193 function loadMode(cm) {
193 cm.doc.mode = CodeMirror.getMode(cm.options, cm.doc.modeOption);
194 cm.doc.mode = CodeMirror.getMode(cm.options, cm.doc.modeOption);
194 cm.doc.iter(function(line) {
195 cm.doc.iter(function(line) {
195 if (line.stateAfter) line.stateAfter = null;
196 if (line.stateAfter) line.stateAfter = null;
196 if (line.styles) line.styles = null;
197 if (line.styles) line.styles = null;
197 });
198 });
198 cm.doc.frontier = cm.doc.first;
199 cm.doc.frontier = cm.doc.first;
199 startWorker(cm, 100);
200 startWorker(cm, 100);
200 cm.state.modeGen++;
201 cm.state.modeGen++;
201 if (cm.curOp) regChange(cm);
202 if (cm.curOp) regChange(cm);
202 }
203 }
203
204
204 function wrappingChanged(cm) {
205 function wrappingChanged(cm) {
205 if (cm.options.lineWrapping) {
206 if (cm.options.lineWrapping) {
206 cm.display.wrapper.className += " CodeMirror-wrap";
207 cm.display.wrapper.className += " CodeMirror-wrap";
207 cm.display.sizer.style.minWidth = "";
208 cm.display.sizer.style.minWidth = "";
208 } else {
209 } else {
209 cm.display.wrapper.className = cm.display.wrapper.className.replace(" CodeMirror-wrap", "");
210 cm.display.wrapper.className = cm.display.wrapper.className.replace(" CodeMirror-wrap", "");
210 computeMaxLength(cm);
211 computeMaxLength(cm);
211 }
212 }
212 estimateLineHeights(cm);
213 estimateLineHeights(cm);
213 regChange(cm);
214 regChange(cm);
214 clearCaches(cm);
215 clearCaches(cm);
215 setTimeout(function(){updateScrollbars(cm.display, cm.doc.height);}, 100);
216 setTimeout(function(){updateScrollbars(cm);}, 100);
216 }
217 }
217
218
218 function estimateHeight(cm) {
219 function estimateHeight(cm) {
219 var th = textHeight(cm.display), wrapping = cm.options.lineWrapping;
220 var th = textHeight(cm.display), wrapping = cm.options.lineWrapping;
220 var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3);
221 var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3);
221 return function(line) {
222 return function(line) {
222 if (lineIsHidden(cm.doc, line))
223 if (lineIsHidden(cm.doc, line))
223 return 0;
224 return 0;
224 else if (wrapping)
225 else if (wrapping)
225 return (Math.ceil(line.text.length / perLine) || 1) * th;
226 return (Math.ceil(line.text.length / perLine) || 1) * th;
226 else
227 else
227 return th;
228 return th;
228 };
229 };
229 }
230 }
230
231
231 function estimateLineHeights(cm) {
232 function estimateLineHeights(cm) {
232 var doc = cm.doc, est = estimateHeight(cm);
233 var doc = cm.doc, est = estimateHeight(cm);
233 doc.iter(function(line) {
234 doc.iter(function(line) {
234 var estHeight = est(line);
235 var estHeight = est(line);
235 if (estHeight != line.height) updateLineHeight(line, estHeight);
236 if (estHeight != line.height) updateLineHeight(line, estHeight);
236 });
237 });
237 }
238 }
238
239
239 function keyMapChanged(cm) {
240 function keyMapChanged(cm) {
240 var style = keyMap[cm.options.keyMap].style;
241 var map = keyMap[cm.options.keyMap], style = map.style;
241 cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-keymap-\S+/g, "") +
242 cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-keymap-\S+/g, "") +
242 (style ? " cm-keymap-" + style : "");
243 (style ? " cm-keymap-" + style : "");
244 cm.state.disableInput = map.disableInput;
243 }
245 }
244
246
245 function themeChanged(cm) {
247 function themeChanged(cm) {
246 cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") +
248 cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") +
247 cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-");
249 cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-");
248 clearCaches(cm);
250 clearCaches(cm);
249 }
251 }
250
252
251 function guttersChanged(cm) {
253 function guttersChanged(cm) {
252 updateGutters(cm);
254 updateGutters(cm);
253 regChange(cm);
255 regChange(cm);
256 setTimeout(function(){alignHorizontally(cm);}, 20);
254 }
257 }
255
258
256 function updateGutters(cm) {
259 function updateGutters(cm) {
257 var gutters = cm.display.gutters, specs = cm.options.gutters;
260 var gutters = cm.display.gutters, specs = cm.options.gutters;
258 removeChildren(gutters);
261 removeChildren(gutters);
259 for (var i = 0; i < specs.length; ++i) {
262 for (var i = 0; i < specs.length; ++i) {
260 var gutterClass = specs[i];
263 var gutterClass = specs[i];
261 var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + gutterClass));
264 var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + gutterClass));
262 if (gutterClass == "CodeMirror-linenumbers") {
265 if (gutterClass == "CodeMirror-linenumbers") {
263 cm.display.lineGutter = gElt;
266 cm.display.lineGutter = gElt;
264 gElt.style.width = (cm.display.lineNumWidth || 1) + "px";
267 gElt.style.width = (cm.display.lineNumWidth || 1) + "px";
265 }
268 }
266 }
269 }
267 gutters.style.display = i ? "" : "none";
270 gutters.style.display = i ? "" : "none";
268 }
271 }
269
272
270 function lineLength(doc, line) {
273 function lineLength(doc, line) {
271 if (line.height == 0) return 0;
274 if (line.height == 0) return 0;
272 var len = line.text.length, merged, cur = line;
275 var len = line.text.length, merged, cur = line;
273 while (merged = collapsedSpanAtStart(cur)) {
276 while (merged = collapsedSpanAtStart(cur)) {
274 var found = merged.find();
277 var found = merged.find();
275 cur = getLine(doc, found.from.line);
278 cur = getLine(doc, found.from.line);
276 len += found.from.ch - found.to.ch;
279 len += found.from.ch - found.to.ch;
277 }
280 }
278 cur = line;
281 cur = line;
279 while (merged = collapsedSpanAtEnd(cur)) {
282 while (merged = collapsedSpanAtEnd(cur)) {
280 var found = merged.find();
283 var found = merged.find();
281 len -= cur.text.length - found.from.ch;
284 len -= cur.text.length - found.from.ch;
282 cur = getLine(doc, found.to.line);
285 cur = getLine(doc, found.to.line);
283 len += cur.text.length - found.to.ch;
286 len += cur.text.length - found.to.ch;
284 }
287 }
285 return len;
288 return len;
286 }
289 }
287
290
288 function computeMaxLength(cm) {
291 function computeMaxLength(cm) {
289 var d = cm.display, doc = cm.doc;
292 var d = cm.display, doc = cm.doc;
290 d.maxLine = getLine(doc, doc.first);
293 d.maxLine = getLine(doc, doc.first);
291 d.maxLineLength = lineLength(doc, d.maxLine);
294 d.maxLineLength = lineLength(doc, d.maxLine);
292 d.maxLineChanged = true;
295 d.maxLineChanged = true;
293 doc.iter(function(line) {
296 doc.iter(function(line) {
294 var len = lineLength(doc, line);
297 var len = lineLength(doc, line);
295 if (len > d.maxLineLength) {
298 if (len > d.maxLineLength) {
296 d.maxLineLength = len;
299 d.maxLineLength = len;
297 d.maxLine = line;
300 d.maxLine = line;
298 }
301 }
299 });
302 });
300 }
303 }
301
304
302 // Make sure the gutters options contains the element
305 // Make sure the gutters options contains the element
303 // "CodeMirror-linenumbers" when the lineNumbers option is true.
306 // "CodeMirror-linenumbers" when the lineNumbers option is true.
304 function setGuttersForLineNumbers(options) {
307 function setGuttersForLineNumbers(options) {
305 var found = false;
308 var found = false;
306 for (var i = 0; i < options.gutters.length; ++i) {
309 for (var i = 0; i < options.gutters.length; ++i) {
307 if (options.gutters[i] == "CodeMirror-linenumbers") {
310 if (options.gutters[i] == "CodeMirror-linenumbers") {
308 if (options.lineNumbers) found = true;
311 if (options.lineNumbers) found = true;
309 else options.gutters.splice(i--, 1);
312 else options.gutters.splice(i--, 1);
310 }
313 }
311 }
314 }
312 if (!found && options.lineNumbers)
315 if (!found && options.lineNumbers)
313 options.gutters.push("CodeMirror-linenumbers");
316 options.gutters.push("CodeMirror-linenumbers");
314 }
317 }
315
318
316 // SCROLLBARS
319 // SCROLLBARS
317
320
318 // Re-synchronize the fake scrollbars with the actual size of the
321 // Re-synchronize the fake scrollbars with the actual size of the
319 // content. Optionally force a scrollTop.
322 // content. Optionally force a scrollTop.
320 function updateScrollbars(d /* display */, docHeight) {
323 function updateScrollbars(cm) {
324 var d = cm.display, docHeight = cm.doc.height;
321 var totalHeight = docHeight + paddingVert(d);
325 var totalHeight = docHeight + paddingVert(d);
322 d.sizer.style.minHeight = d.heightForcer.style.top = totalHeight + "px";
326 d.sizer.style.minHeight = d.heightForcer.style.top = totalHeight + "px";
327 d.gutters.style.height = Math.max(totalHeight, d.scroller.clientHeight - scrollerCutOff) + "px";
323 var scrollHeight = Math.max(totalHeight, d.scroller.scrollHeight);
328 var scrollHeight = Math.max(totalHeight, d.scroller.scrollHeight);
324 var needsH = d.scroller.scrollWidth > d.scroller.clientWidth;
329 var needsH = d.scroller.scrollWidth > (d.scroller.clientWidth + 1);
325 var needsV = scrollHeight > d.scroller.clientHeight;
330 var needsV = scrollHeight > (d.scroller.clientHeight + 1);
326 if (needsV) {
331 if (needsV) {
327 d.scrollbarV.style.display = "block";
332 d.scrollbarV.style.display = "block";
328 d.scrollbarV.style.bottom = needsH ? scrollbarWidth(d.measure) + "px" : "0";
333 d.scrollbarV.style.bottom = needsH ? scrollbarWidth(d.measure) + "px" : "0";
329 d.scrollbarV.firstChild.style.height =
334 d.scrollbarV.firstChild.style.height =
330 (scrollHeight - d.scroller.clientHeight + d.scrollbarV.clientHeight) + "px";
335 (scrollHeight - d.scroller.clientHeight + d.scrollbarV.clientHeight) + "px";
331 } else d.scrollbarV.style.display = "";
336 } else d.scrollbarV.style.display = "";
332 if (needsH) {
337 if (needsH) {
333 d.scrollbarH.style.display = "block";
338 d.scrollbarH.style.display = "block";
334 d.scrollbarH.style.right = needsV ? scrollbarWidth(d.measure) + "px" : "0";
339 d.scrollbarH.style.right = needsV ? scrollbarWidth(d.measure) + "px" : "0";
335 d.scrollbarH.firstChild.style.width =
340 d.scrollbarH.firstChild.style.width =
336 (d.scroller.scrollWidth - d.scroller.clientWidth + d.scrollbarH.clientWidth) + "px";
341 (d.scroller.scrollWidth - d.scroller.clientWidth + d.scrollbarH.clientWidth) + "px";
337 } else d.scrollbarH.style.display = "";
342 } else d.scrollbarH.style.display = "";
338 if (needsH && needsV) {
343 if (needsH && needsV) {
339 d.scrollbarFiller.style.display = "block";
344 d.scrollbarFiller.style.display = "block";
340 d.scrollbarFiller.style.height = d.scrollbarFiller.style.width = scrollbarWidth(d.measure) + "px";
345 d.scrollbarFiller.style.height = d.scrollbarFiller.style.width = scrollbarWidth(d.measure) + "px";
341 } else d.scrollbarFiller.style.display = "";
346 } else d.scrollbarFiller.style.display = "";
347 if (needsH && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) {
348 d.gutterFiller.style.display = "block";
349 d.gutterFiller.style.height = scrollbarWidth(d.measure) + "px";
350 d.gutterFiller.style.width = d.gutters.offsetWidth + "px";
351 } else d.gutterFiller.style.display = "";
342
352
343 if (mac_geLion && scrollbarWidth(d.measure) === 0)
353 if (mac_geLion && scrollbarWidth(d.measure) === 0)
344 d.scrollbarV.style.minWidth = d.scrollbarH.style.minHeight = mac_geMountainLion ? "18px" : "12px";
354 d.scrollbarV.style.minWidth = d.scrollbarH.style.minHeight = mac_geMountainLion ? "18px" : "12px";
345 }
355 }
346
356
347 function visibleLines(display, doc, viewPort) {
357 function visibleLines(display, doc, viewPort) {
348 var top = display.scroller.scrollTop, height = display.wrapper.clientHeight;
358 var top = display.scroller.scrollTop, height = display.wrapper.clientHeight;
349 if (typeof viewPort == "number") top = viewPort;
359 if (typeof viewPort == "number") top = viewPort;
350 else if (viewPort) {top = viewPort.top; height = viewPort.bottom - viewPort.top;}
360 else if (viewPort) {top = viewPort.top; height = viewPort.bottom - viewPort.top;}
351 top = Math.floor(top - paddingTop(display));
361 top = Math.floor(top - paddingTop(display));
352 var bottom = Math.ceil(top + height);
362 var bottom = Math.ceil(top + height);
353 return {from: lineAtHeight(doc, top), to: lineAtHeight(doc, bottom)};
363 return {from: lineAtHeight(doc, top), to: lineAtHeight(doc, bottom)};
354 }
364 }
355
365
356 // LINE NUMBERS
366 // LINE NUMBERS
357
367
358 function alignHorizontally(cm) {
368 function alignHorizontally(cm) {
359 var display = cm.display;
369 var display = cm.display;
360 if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) return;
370 if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) return;
361 var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft;
371 var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft;
362 var gutterW = display.gutters.offsetWidth, l = comp + "px";
372 var gutterW = display.gutters.offsetWidth, l = comp + "px";
363 for (var n = display.lineDiv.firstChild; n; n = n.nextSibling) if (n.alignable) {
373 for (var n = display.lineDiv.firstChild; n; n = n.nextSibling) if (n.alignable) {
364 for (var i = 0, a = n.alignable; i < a.length; ++i) a[i].style.left = l;
374 for (var i = 0, a = n.alignable; i < a.length; ++i) a[i].style.left = l;
365 }
375 }
366 if (cm.options.fixedGutter)
376 if (cm.options.fixedGutter)
367 display.gutters.style.left = (comp + gutterW) + "px";
377 display.gutters.style.left = (comp + gutterW) + "px";
368 }
378 }
369
379
370 function maybeUpdateLineNumberWidth(cm) {
380 function maybeUpdateLineNumberWidth(cm) {
371 if (!cm.options.lineNumbers) return false;
381 if (!cm.options.lineNumbers) return false;
372 var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display;
382 var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display;
373 if (last.length != display.lineNumChars) {
383 if (last.length != display.lineNumChars) {
374 var test = display.measure.appendChild(elt("div", [elt("div", last)],
384 var test = display.measure.appendChild(elt("div", [elt("div", last)],
375 "CodeMirror-linenumber CodeMirror-gutter-elt"));
385 "CodeMirror-linenumber CodeMirror-gutter-elt"));
376 var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW;
386 var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW;
377 display.lineGutter.style.width = "";
387 display.lineGutter.style.width = "";
378 display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding);
388 display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding);
379 display.lineNumWidth = display.lineNumInnerWidth + padding;
389 display.lineNumWidth = display.lineNumInnerWidth + padding;
380 display.lineNumChars = display.lineNumInnerWidth ? last.length : -1;
390 display.lineNumChars = display.lineNumInnerWidth ? last.length : -1;
381 display.lineGutter.style.width = display.lineNumWidth + "px";
391 display.lineGutter.style.width = display.lineNumWidth + "px";
382 return true;
392 return true;
383 }
393 }
384 return false;
394 return false;
385 }
395 }
386
396
387 function lineNumberFor(options, i) {
397 function lineNumberFor(options, i) {
388 return String(options.lineNumberFormatter(i + options.firstLineNumber));
398 return String(options.lineNumberFormatter(i + options.firstLineNumber));
389 }
399 }
390 function compensateForHScroll(display) {
400 function compensateForHScroll(display) {
391 return getRect(display.scroller).left - getRect(display.sizer).left;
401 return getRect(display.scroller).left - getRect(display.sizer).left;
392 }
402 }
393
403
394 // DISPLAY DRAWING
404 // DISPLAY DRAWING
395
405
396 function updateDisplay(cm, changes, viewPort) {
406 function updateDisplay(cm, changes, viewPort) {
397 var oldFrom = cm.display.showingFrom, oldTo = cm.display.showingTo, updated;
407 var oldFrom = cm.display.showingFrom, oldTo = cm.display.showingTo, updated;
398 var visible = visibleLines(cm.display, cm.doc, viewPort);
408 var visible = visibleLines(cm.display, cm.doc, viewPort);
399 for (;;) {
409 for (;;) {
400 if (updateDisplayInner(cm, changes, visible)) {
410 if (!updateDisplayInner(cm, changes, visible)) break;
401 updated = true;
411 updated = true;
402 signalLater(cm, "update", cm);
403 if (cm.display.showingFrom != oldFrom || cm.display.showingTo != oldTo)
404 signalLater(cm, "viewportChange", cm, cm.display.showingFrom, cm.display.showingTo);
405 } else break;
406 updateSelection(cm);
412 updateSelection(cm);
407 updateScrollbars(cm.display, cm.doc.height);
413 updateScrollbars(cm);
408
414
409 // Clip forced viewport to actual scrollable area
415 // Clip forced viewport to actual scrollable area
410 if (viewPort)
416 if (viewPort)
411 viewPort = Math.min(cm.display.scroller.scrollHeight - cm.display.scroller.clientHeight,
417 viewPort = Math.min(cm.display.scroller.scrollHeight - cm.display.scroller.clientHeight,
412 typeof viewPort == "number" ? viewPort : viewPort.top);
418 typeof viewPort == "number" ? viewPort : viewPort.top);
413 visible = visibleLines(cm.display, cm.doc, viewPort);
419 visible = visibleLines(cm.display, cm.doc, viewPort);
414 if (visible.from >= cm.display.showingFrom && visible.to <= cm.display.showingTo)
420 if (visible.from >= cm.display.showingFrom && visible.to <= cm.display.showingTo)
415 break;
421 break;
416 changes = [];
422 changes = [];
417 }
423 }
418
424
425 if (updated) {
426 signalLater(cm, "update", cm);
427 if (cm.display.showingFrom != oldFrom || cm.display.showingTo != oldTo)
428 signalLater(cm, "viewportChange", cm, cm.display.showingFrom, cm.display.showingTo);
429 }
419 return updated;
430 return updated;
420 }
431 }
421
432
422 // Uses a set of changes plus the current scroll position to
433 // Uses a set of changes plus the current scroll position to
423 // determine which DOM updates have to be made, and makes the
434 // determine which DOM updates have to be made, and makes the
424 // updates.
435 // updates.
425 function updateDisplayInner(cm, changes, visible) {
436 function updateDisplayInner(cm, changes, visible) {
426 var display = cm.display, doc = cm.doc;
437 var display = cm.display, doc = cm.doc;
427 if (!display.wrapper.clientWidth) {
438 if (!display.wrapper.clientWidth) {
428 display.showingFrom = display.showingTo = doc.first;
439 display.showingFrom = display.showingTo = doc.first;
429 display.viewOffset = 0;
440 display.viewOffset = 0;
430 return;
441 return;
431 }
442 }
432
443
433 // Bail out if the visible area is already rendered and nothing changed.
444 // Bail out if the visible area is already rendered and nothing changed.
434 if (changes.length == 0 &&
445 if (changes.length == 0 &&
435 visible.from > display.showingFrom && visible.to < display.showingTo)
446 visible.from > display.showingFrom && visible.to < display.showingTo)
436 return;
447 return;
437
448
438 if (maybeUpdateLineNumberWidth(cm))
449 if (maybeUpdateLineNumberWidth(cm))
439 changes = [{from: doc.first, to: doc.first + doc.size}];
450 changes = [{from: doc.first, to: doc.first + doc.size}];
440 var gutterW = display.sizer.style.marginLeft = display.gutters.offsetWidth + "px";
451 var gutterW = display.sizer.style.marginLeft = display.gutters.offsetWidth + "px";
441 display.scrollbarH.style.left = cm.options.fixedGutter ? gutterW : "0";
452 display.scrollbarH.style.left = cm.options.fixedGutter ? gutterW : "0";
442
453
443 // Used to determine which lines need their line numbers updated
454 // Used to determine which lines need their line numbers updated
444 var positionsChangedFrom = Infinity;
455 var positionsChangedFrom = Infinity;
445 if (cm.options.lineNumbers)
456 if (cm.options.lineNumbers)
446 for (var i = 0; i < changes.length; ++i)
457 for (var i = 0; i < changes.length; ++i)
447 if (changes[i].diff) { positionsChangedFrom = changes[i].from; break; }
458 if (changes[i].diff) { positionsChangedFrom = changes[i].from; break; }
448
459
449 var end = doc.first + doc.size;
460 var end = doc.first + doc.size;
450 var from = Math.max(visible.from - cm.options.viewportMargin, doc.first);
461 var from = Math.max(visible.from - cm.options.viewportMargin, doc.first);
451 var to = Math.min(end, visible.to + cm.options.viewportMargin);
462 var to = Math.min(end, visible.to + cm.options.viewportMargin);
452 if (display.showingFrom < from && from - display.showingFrom < 20) from = Math.max(doc.first, display.showingFrom);
463 if (display.showingFrom < from && from - display.showingFrom < 20) from = Math.max(doc.first, display.showingFrom);
453 if (display.showingTo > to && display.showingTo - to < 20) to = Math.min(end, display.showingTo);
464 if (display.showingTo > to && display.showingTo - to < 20) to = Math.min(end, display.showingTo);
454 if (sawCollapsedSpans) {
465 if (sawCollapsedSpans) {
455 from = lineNo(visualLine(doc, getLine(doc, from)));
466 from = lineNo(visualLine(doc, getLine(doc, from)));
456 while (to < end && lineIsHidden(doc, getLine(doc, to))) ++to;
467 while (to < end && lineIsHidden(doc, getLine(doc, to))) ++to;
457 }
468 }
458
469
459 // Create a range of theoretically intact lines, and punch holes
470 // Create a range of theoretically intact lines, and punch holes
460 // in that using the change info.
471 // in that using the change info.
461 var intact = [{from: Math.max(display.showingFrom, doc.first),
472 var intact = [{from: Math.max(display.showingFrom, doc.first),
462 to: Math.min(display.showingTo, end)}];
473 to: Math.min(display.showingTo, end)}];
463 if (intact[0].from >= intact[0].to) intact = [];
474 if (intact[0].from >= intact[0].to) intact = [];
464 else intact = computeIntact(intact, changes);
475 else intact = computeIntact(intact, changes);
465 // When merged lines are present, we might have to reduce the
476 // When merged lines are present, we might have to reduce the
466 // intact ranges because changes in continued fragments of the
477 // intact ranges because changes in continued fragments of the
467 // intact lines do require the lines to be redrawn.
478 // intact lines do require the lines to be redrawn.
468 if (sawCollapsedSpans)
479 if (sawCollapsedSpans)
469 for (var i = 0; i < intact.length; ++i) {
480 for (var i = 0; i < intact.length; ++i) {
470 var range = intact[i], merged;
481 var range = intact[i], merged;
471 while (merged = collapsedSpanAtEnd(getLine(doc, range.to - 1))) {
482 while (merged = collapsedSpanAtEnd(getLine(doc, range.to - 1))) {
472 var newTo = merged.find().from.line;
483 var newTo = merged.find().from.line;
473 if (newTo > range.from) range.to = newTo;
484 if (newTo > range.from) range.to = newTo;
474 else { intact.splice(i--, 1); break; }
485 else { intact.splice(i--, 1); break; }
475 }
486 }
476 }
487 }
477
488
478 // Clip off the parts that won't be visible
489 // Clip off the parts that won't be visible
479 var intactLines = 0;
490 var intactLines = 0;
480 for (var i = 0; i < intact.length; ++i) {
491 for (var i = 0; i < intact.length; ++i) {
481 var range = intact[i];
492 var range = intact[i];
482 if (range.from < from) range.from = from;
493 if (range.from < from) range.from = from;
483 if (range.to > to) range.to = to;
494 if (range.to > to) range.to = to;
484 if (range.from >= range.to) intact.splice(i--, 1);
495 if (range.from >= range.to) intact.splice(i--, 1);
485 else intactLines += range.to - range.from;
496 else intactLines += range.to - range.from;
486 }
497 }
487 if (intactLines == to - from && from == display.showingFrom && to == display.showingTo) {
498 if (intactLines == to - from && from == display.showingFrom && to == display.showingTo) {
488 updateViewOffset(cm);
499 updateViewOffset(cm);
489 return;
500 return;
490 }
501 }
491 intact.sort(function(a, b) {return a.from - b.from;});
502 intact.sort(function(a, b) {return a.from - b.from;});
492
503
493 // Avoid crashing on IE's "unspecified error" when in iframes
504 // Avoid crashing on IE's "unspecified error" when in iframes
494 try {
505 try {
495 var focused = document.activeElement;
506 var focused = document.activeElement;
496 } catch(e) {}
507 } catch(e) {}
497 if (intactLines < (to - from) * .7) display.lineDiv.style.display = "none";
508 if (intactLines < (to - from) * .7) display.lineDiv.style.display = "none";
498 patchDisplay(cm, from, to, intact, positionsChangedFrom);
509 patchDisplay(cm, from, to, intact, positionsChangedFrom);
499 display.lineDiv.style.display = "";
510 display.lineDiv.style.display = "";
500 if (focused && document.activeElement != focused && focused.offsetHeight) focused.focus();
511 if (focused && document.activeElement != focused && focused.offsetHeight) focused.focus();
501
512
502 var different = from != display.showingFrom || to != display.showingTo ||
513 var different = from != display.showingFrom || to != display.showingTo ||
503 display.lastSizeC != display.wrapper.clientHeight;
514 display.lastSizeC != display.wrapper.clientHeight;
504 // This is just a bogus formula that detects when the editor is
515 // This is just a bogus formula that detects when the editor is
505 // resized or the font size changes.
516 // resized or the font size changes.
506 if (different) display.lastSizeC = display.wrapper.clientHeight;
517 if (different) {
518 display.lastSizeC = display.wrapper.clientHeight;
519 startWorker(cm, 400);
520 }
507 display.showingFrom = from; display.showingTo = to;
521 display.showingFrom = from; display.showingTo = to;
508 startWorker(cm, 100);
509
522
510 var prevBottom = display.lineDiv.offsetTop;
523 var prevBottom = display.lineDiv.offsetTop;
511 for (var node = display.lineDiv.firstChild, height; node; node = node.nextSibling) if (node.lineObj) {
524 for (var node = display.lineDiv.firstChild, height; node; node = node.nextSibling) if (node.lineObj) {
512 if (ie_lt8) {
525 if (ie_lt8) {
513 var bot = node.offsetTop + node.offsetHeight;
526 var bot = node.offsetTop + node.offsetHeight;
514 height = bot - prevBottom;
527 height = bot - prevBottom;
515 prevBottom = bot;
528 prevBottom = bot;
516 } else {
529 } else {
517 var box = getRect(node);
530 var box = getRect(node);
518 height = box.bottom - box.top;
531 height = box.bottom - box.top;
519 }
532 }
520 var diff = node.lineObj.height - height;
533 var diff = node.lineObj.height - height;
521 if (height < 2) height = textHeight(display);
534 if (height < 2) height = textHeight(display);
522 if (diff > .001 || diff < -.001) {
535 if (diff > .001 || diff < -.001) {
523 updateLineHeight(node.lineObj, height);
536 updateLineHeight(node.lineObj, height);
524 var widgets = node.lineObj.widgets;
537 var widgets = node.lineObj.widgets;
525 if (widgets) for (var i = 0; i < widgets.length; ++i)
538 if (widgets) for (var i = 0; i < widgets.length; ++i)
526 widgets[i].height = widgets[i].node.offsetHeight;
539 widgets[i].height = widgets[i].node.offsetHeight;
527 }
540 }
528 }
541 }
529 updateViewOffset(cm);
542 updateViewOffset(cm);
530
543
531 return true;
544 return true;
532 }
545 }
533
546
534 function updateViewOffset(cm) {
547 function updateViewOffset(cm) {
535 var off = cm.display.viewOffset = heightAtLine(cm, getLine(cm.doc, cm.display.showingFrom));
548 var off = cm.display.viewOffset = heightAtLine(cm, getLine(cm.doc, cm.display.showingFrom));
536 // Position the mover div to align with the current virtual scroll position
549 // Position the mover div to align with the current virtual scroll position
537 cm.display.mover.style.top = off + "px";
550 cm.display.mover.style.top = off + "px";
538 }
551 }
539
552
540 function computeIntact(intact, changes) {
553 function computeIntact(intact, changes) {
541 for (var i = 0, l = changes.length || 0; i < l; ++i) {
554 for (var i = 0, l = changes.length || 0; i < l; ++i) {
542 var change = changes[i], intact2 = [], diff = change.diff || 0;
555 var change = changes[i], intact2 = [], diff = change.diff || 0;
543 for (var j = 0, l2 = intact.length; j < l2; ++j) {
556 for (var j = 0, l2 = intact.length; j < l2; ++j) {
544 var range = intact[j];
557 var range = intact[j];
545 if (change.to <= range.from && change.diff) {
558 if (change.to <= range.from && change.diff) {
546 intact2.push({from: range.from + diff, to: range.to + diff});
559 intact2.push({from: range.from + diff, to: range.to + diff});
547 } else if (change.to <= range.from || change.from >= range.to) {
560 } else if (change.to <= range.from || change.from >= range.to) {
548 intact2.push(range);
561 intact2.push(range);
549 } else {
562 } else {
550 if (change.from > range.from)
563 if (change.from > range.from)
551 intact2.push({from: range.from, to: change.from});
564 intact2.push({from: range.from, to: change.from});
552 if (change.to < range.to)
565 if (change.to < range.to)
553 intact2.push({from: change.to + diff, to: range.to + diff});
566 intact2.push({from: change.to + diff, to: range.to + diff});
554 }
567 }
555 }
568 }
556 intact = intact2;
569 intact = intact2;
557 }
570 }
558 return intact;
571 return intact;
559 }
572 }
560
573
561 function getDimensions(cm) {
574 function getDimensions(cm) {
562 var d = cm.display, left = {}, width = {};
575 var d = cm.display, left = {}, width = {};
563 for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) {
576 for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) {
564 left[cm.options.gutters[i]] = n.offsetLeft;
577 left[cm.options.gutters[i]] = n.offsetLeft;
565 width[cm.options.gutters[i]] = n.offsetWidth;
578 width[cm.options.gutters[i]] = n.offsetWidth;
566 }
579 }
567 return {fixedPos: compensateForHScroll(d),
580 return {fixedPos: compensateForHScroll(d),
568 gutterTotalWidth: d.gutters.offsetWidth,
581 gutterTotalWidth: d.gutters.offsetWidth,
569 gutterLeft: left,
582 gutterLeft: left,
570 gutterWidth: width,
583 gutterWidth: width,
571 wrapperWidth: d.wrapper.clientWidth};
584 wrapperWidth: d.wrapper.clientWidth};
572 }
585 }
573
586
574 function patchDisplay(cm, from, to, intact, updateNumbersFrom) {
587 function patchDisplay(cm, from, to, intact, updateNumbersFrom) {
575 var dims = getDimensions(cm);
588 var dims = getDimensions(cm);
576 var display = cm.display, lineNumbers = cm.options.lineNumbers;
589 var display = cm.display, lineNumbers = cm.options.lineNumbers;
577 if (!intact.length && (!webkit || !cm.display.currentWheelTarget))
590 if (!intact.length && (!webkit || !cm.display.currentWheelTarget))
578 removeChildren(display.lineDiv);
591 removeChildren(display.lineDiv);
579 var container = display.lineDiv, cur = container.firstChild;
592 var container = display.lineDiv, cur = container.firstChild;
580
593
581 function rm(node) {
594 function rm(node) {
582 var next = node.nextSibling;
595 var next = node.nextSibling;
583 if (webkit && mac && cm.display.currentWheelTarget == node) {
596 if (webkit && mac && cm.display.currentWheelTarget == node) {
584 node.style.display = "none";
597 node.style.display = "none";
585 node.lineObj = null;
598 node.lineObj = null;
586 } else {
599 } else {
587 node.parentNode.removeChild(node);
600 node.parentNode.removeChild(node);
588 }
601 }
589 return next;
602 return next;
590 }
603 }
591
604
592 var nextIntact = intact.shift(), lineN = from;
605 var nextIntact = intact.shift(), lineN = from;
593 cm.doc.iter(from, to, function(line) {
606 cm.doc.iter(from, to, function(line) {
594 if (nextIntact && nextIntact.to == lineN) nextIntact = intact.shift();
607 if (nextIntact && nextIntact.to == lineN) nextIntact = intact.shift();
595 if (lineIsHidden(cm.doc, line)) {
608 if (lineIsHidden(cm.doc, line)) {
596 if (line.height != 0) updateLineHeight(line, 0);
609 if (line.height != 0) updateLineHeight(line, 0);
597 if (line.widgets && cur.previousSibling) for (var i = 0; i < line.widgets.length; ++i)
610 if (line.widgets && cur.previousSibling) for (var i = 0; i < line.widgets.length; ++i) {
598 if (line.widgets[i].showIfHidden) {
611 var w = line.widgets[i];
612 if (w.showIfHidden) {
599 var prev = cur.previousSibling;
613 var prev = cur.previousSibling;
600 if (/pre/i.test(prev.nodeName)) {
614 if (/pre/i.test(prev.nodeName)) {
601 var wrap = elt("div", null, null, "position: relative");
615 var wrap = elt("div", null, null, "position: relative");
602 prev.parentNode.replaceChild(wrap, prev);
616 prev.parentNode.replaceChild(wrap, prev);
603 wrap.appendChild(prev);
617 wrap.appendChild(prev);
604 prev = wrap;
618 prev = wrap;
605 }
619 }
606 var wnode = prev.appendChild(elt("div", [line.widgets[i].node], "CodeMirror-linewidget"));
620 var wnode = prev.appendChild(elt("div", [w.node], "CodeMirror-linewidget"));
607 positionLineWidget(line.widgets[i], wnode, prev, dims);
621 if (!w.handleMouseEvents) wnode.ignoreEvents = true;
622 positionLineWidget(w, wnode, prev, dims);
608 }
623 }
624 }
609 } else if (nextIntact && nextIntact.from <= lineN && nextIntact.to > lineN) {
625 } else if (nextIntact && nextIntact.from <= lineN && nextIntact.to > lineN) {
610 // This line is intact. Skip to the actual node. Update its
626 // This line is intact. Skip to the actual node. Update its
611 // line number if needed.
627 // line number if needed.
612 while (cur.lineObj != line) cur = rm(cur);
628 while (cur.lineObj != line) cur = rm(cur);
613 if (lineNumbers && updateNumbersFrom <= lineN && cur.lineNumber)
629 if (lineNumbers && updateNumbersFrom <= lineN && cur.lineNumber)
614 setTextContent(cur.lineNumber, lineNumberFor(cm.options, lineN));
630 setTextContent(cur.lineNumber, lineNumberFor(cm.options, lineN));
615 cur = cur.nextSibling;
631 cur = cur.nextSibling;
616 } else {
632 } else {
617 // For lines with widgets, make an attempt to find and reuse
633 // For lines with widgets, make an attempt to find and reuse
618 // the existing element, so that widgets aren't needlessly
634 // the existing element, so that widgets aren't needlessly
619 // removed and re-inserted into the dom
635 // removed and re-inserted into the dom
620 if (line.widgets) for (var j = 0, search = cur, reuse; search && j < 20; ++j, search = search.nextSibling)
636 if (line.widgets) for (var j = 0, search = cur, reuse; search && j < 20; ++j, search = search.nextSibling)
621 if (search.lineObj == line && /div/i.test(search.nodeName)) { reuse = search; break; }
637 if (search.lineObj == line && /div/i.test(search.nodeName)) { reuse = search; break; }
622 // This line needs to be generated.
638 // This line needs to be generated.
623 var lineNode = buildLineElement(cm, line, lineN, dims, reuse);
639 var lineNode = buildLineElement(cm, line, lineN, dims, reuse);
624 if (lineNode != reuse) {
640 if (lineNode != reuse) {
625 container.insertBefore(lineNode, cur);
641 container.insertBefore(lineNode, cur);
626 } else {
642 } else {
627 while (cur != reuse) cur = rm(cur);
643 while (cur != reuse) cur = rm(cur);
628 cur = cur.nextSibling;
644 cur = cur.nextSibling;
629 }
645 }
630
646
631 lineNode.lineObj = line;
647 lineNode.lineObj = line;
632 }
648 }
633 ++lineN;
649 ++lineN;
634 });
650 });
635 while (cur) cur = rm(cur);
651 while (cur) cur = rm(cur);
636 }
652 }
637
653
638 function buildLineElement(cm, line, lineNo, dims, reuse) {
654 function buildLineElement(cm, line, lineNo, dims, reuse) {
639 var lineElement = lineContent(cm, line);
655 var lineElement = lineContent(cm, line);
640 var markers = line.gutterMarkers, display = cm.display, wrap;
656 var markers = line.gutterMarkers, display = cm.display, wrap;
641
657
642 if (!cm.options.lineNumbers && !markers && !line.bgClass && !line.wrapClass && !line.widgets)
658 if (!cm.options.lineNumbers && !markers && !line.bgClass && !line.wrapClass && !line.widgets)
643 return lineElement;
659 return lineElement;
644
660
645 // Lines with gutter elements, widgets or a background class need
661 // Lines with gutter elements, widgets or a background class need
646 // to be wrapped again, and have the extra elements added to the
662 // to be wrapped again, and have the extra elements added to the
647 // wrapper div
663 // wrapper div
648
664
649 if (reuse) {
665 if (reuse) {
650 reuse.alignable = null;
666 reuse.alignable = null;
651 var isOk = true, widgetsSeen = 0;
667 var isOk = true, widgetsSeen = 0, insertBefore = null;
652 for (var n = reuse.firstChild, next; n; n = next) {
668 for (var n = reuse.firstChild, next; n; n = next) {
653 next = n.nextSibling;
669 next = n.nextSibling;
654 if (!/\bCodeMirror-linewidget\b/.test(n.className)) {
670 if (!/\bCodeMirror-linewidget\b/.test(n.className)) {
655 reuse.removeChild(n);
671 reuse.removeChild(n);
656 } else {
672 } else {
657 for (var i = 0, first = true; i < line.widgets.length; ++i) {
673 for (var i = 0, first = true; i < line.widgets.length; ++i) {
658 var widget = line.widgets[i], isFirst = false;
674 var widget = line.widgets[i];
659 if (!widget.above) { isFirst = first; first = false; }
675 if (!widget.above) { insertBefore = n; first = false; }
660 if (widget.node == n.firstChild) {
676 if (widget.node == n.firstChild) {
661 positionLineWidget(widget, n, reuse, dims);
677 positionLineWidget(widget, n, reuse, dims);
662 ++widgetsSeen;
678 ++widgetsSeen;
663 if (isFirst) reuse.insertBefore(lineElement, n);
664 break;
679 break;
665 }
680 }
666 }
681 }
667 if (i == line.widgets.length) { isOk = false; break; }
682 if (i == line.widgets.length) { isOk = false; break; }
668 }
683 }
669 }
684 }
685 reuse.insertBefore(lineElement, insertBefore);
670 if (isOk && widgetsSeen == line.widgets.length) {
686 if (isOk && widgetsSeen == line.widgets.length) {
671 wrap = reuse;
687 wrap = reuse;
672 reuse.className = line.wrapClass || "";
688 reuse.className = line.wrapClass || "";
673 }
689 }
674 }
690 }
675 if (!wrap) {
691 if (!wrap) {
676 wrap = elt("div", null, line.wrapClass, "position: relative");
692 wrap = elt("div", null, line.wrapClass, "position: relative");
677 wrap.appendChild(lineElement);
693 wrap.appendChild(lineElement);
678 }
694 }
679 // Kludge to make sure the styled element lies behind the selection (by z-index)
695 // Kludge to make sure the styled element lies behind the selection (by z-index)
680 if (line.bgClass)
696 if (line.bgClass)
681 wrap.insertBefore(elt("div", null, line.bgClass + " CodeMirror-linebackground"), wrap.firstChild);
697 wrap.insertBefore(elt("div", null, line.bgClass + " CodeMirror-linebackground"), wrap.firstChild);
682 if (cm.options.lineNumbers || markers) {
698 if (cm.options.lineNumbers || markers) {
683 var gutterWrap = wrap.insertBefore(elt("div", null, null, "position: absolute; left: " +
699 var gutterWrap = wrap.insertBefore(elt("div", null, null, "position: absolute; left: " +
684 (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px"),
700 (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px"),
685 wrap.firstChild);
701 wrap.firstChild);
686 if (cm.options.fixedGutter) (wrap.alignable || (wrap.alignable = [])).push(gutterWrap);
702 if (cm.options.fixedGutter) (wrap.alignable || (wrap.alignable = [])).push(gutterWrap);
687 if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"]))
703 if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"]))
688 wrap.lineNumber = gutterWrap.appendChild(
704 wrap.lineNumber = gutterWrap.appendChild(
689 elt("div", lineNumberFor(cm.options, lineNo),
705 elt("div", lineNumberFor(cm.options, lineNo),
690 "CodeMirror-linenumber CodeMirror-gutter-elt",
706 "CodeMirror-linenumber CodeMirror-gutter-elt",
691 "left: " + dims.gutterLeft["CodeMirror-linenumbers"] + "px; width: "
707 "left: " + dims.gutterLeft["CodeMirror-linenumbers"] + "px; width: "
692 + display.lineNumInnerWidth + "px"));
708 + display.lineNumInnerWidth + "px"));
693 if (markers)
709 if (markers)
694 for (var k = 0; k < cm.options.gutters.length; ++k) {
710 for (var k = 0; k < cm.options.gutters.length; ++k) {
695 var id = cm.options.gutters[k], found = markers.hasOwnProperty(id) && markers[id];
711 var id = cm.options.gutters[k], found = markers.hasOwnProperty(id) && markers[id];
696 if (found)
712 if (found)
697 gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt", "left: " +
713 gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt", "left: " +
698 dims.gutterLeft[id] + "px; width: " + dims.gutterWidth[id] + "px"));
714 dims.gutterLeft[id] + "px; width: " + dims.gutterWidth[id] + "px"));
699 }
715 }
700 }
716 }
701 if (ie_lt8) wrap.style.zIndex = 2;
717 if (ie_lt8) wrap.style.zIndex = 2;
702 if (line.widgets && wrap != reuse) for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
718 if (line.widgets && wrap != reuse) for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
703 var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget");
719 var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget");
720 if (!widget.handleMouseEvents) node.ignoreEvents = true;
704 positionLineWidget(widget, node, wrap, dims);
721 positionLineWidget(widget, node, wrap, dims);
705 if (widget.above)
722 if (widget.above)
706 wrap.insertBefore(node, cm.options.lineNumbers && line.height != 0 ? gutterWrap : lineElement);
723 wrap.insertBefore(node, cm.options.lineNumbers && line.height != 0 ? gutterWrap : lineElement);
707 else
724 else
708 wrap.appendChild(node);
725 wrap.appendChild(node);
709 signalLater(widget, "redraw");
726 signalLater(widget, "redraw");
710 }
727 }
711 return wrap;
728 return wrap;
712 }
729 }
713
730
714 function positionLineWidget(widget, node, wrap, dims) {
731 function positionLineWidget(widget, node, wrap, dims) {
715 if (widget.noHScroll) {
732 if (widget.noHScroll) {
716 (wrap.alignable || (wrap.alignable = [])).push(node);
733 (wrap.alignable || (wrap.alignable = [])).push(node);
717 var width = dims.wrapperWidth;
734 var width = dims.wrapperWidth;
718 node.style.left = dims.fixedPos + "px";
735 node.style.left = dims.fixedPos + "px";
719 if (!widget.coverGutter) {
736 if (!widget.coverGutter) {
720 width -= dims.gutterTotalWidth;
737 width -= dims.gutterTotalWidth;
721 node.style.paddingLeft = dims.gutterTotalWidth + "px";
738 node.style.paddingLeft = dims.gutterTotalWidth + "px";
722 }
739 }
723 node.style.width = width + "px";
740 node.style.width = width + "px";
724 }
741 }
725 if (widget.coverGutter) {
742 if (widget.coverGutter) {
726 node.style.zIndex = 5;
743 node.style.zIndex = 5;
727 node.style.position = "relative";
744 node.style.position = "relative";
728 if (!widget.noHScroll) node.style.marginLeft = -dims.gutterTotalWidth + "px";
745 if (!widget.noHScroll) node.style.marginLeft = -dims.gutterTotalWidth + "px";
729 }
746 }
730 }
747 }
731
748
732 // SELECTION / CURSOR
749 // SELECTION / CURSOR
733
750
734 function updateSelection(cm) {
751 function updateSelection(cm) {
735 var display = cm.display;
752 var display = cm.display;
736 var collapsed = posEq(cm.doc.sel.from, cm.doc.sel.to);
753 var collapsed = posEq(cm.doc.sel.from, cm.doc.sel.to);
737 if (collapsed || cm.options.showCursorWhenSelecting)
754 if (collapsed || cm.options.showCursorWhenSelecting)
738 updateSelectionCursor(cm);
755 updateSelectionCursor(cm);
739 else
756 else
740 display.cursor.style.display = display.otherCursor.style.display = "none";
757 display.cursor.style.display = display.otherCursor.style.display = "none";
741 if (!collapsed)
758 if (!collapsed)
742 updateSelectionRange(cm);
759 updateSelectionRange(cm);
743 else
760 else
744 display.selectionDiv.style.display = "none";
761 display.selectionDiv.style.display = "none";
745
762
746 // Move the hidden textarea near the cursor to prevent scrolling artifacts
763 // Move the hidden textarea near the cursor to prevent scrolling artifacts
747 if (cm.options.moveInputWithCursor) {
764 if (cm.options.moveInputWithCursor) {
748 var headPos = cursorCoords(cm, cm.doc.sel.head, "div");
765 var headPos = cursorCoords(cm, cm.doc.sel.head, "div");
749 var wrapOff = getRect(display.wrapper), lineOff = getRect(display.lineDiv);
766 var wrapOff = getRect(display.wrapper), lineOff = getRect(display.lineDiv);
750 display.inputDiv.style.top = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
767 display.inputDiv.style.top = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
751 headPos.top + lineOff.top - wrapOff.top)) + "px";
768 headPos.top + lineOff.top - wrapOff.top)) + "px";
752 display.inputDiv.style.left = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
769 display.inputDiv.style.left = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
753 headPos.left + lineOff.left - wrapOff.left)) + "px";
770 headPos.left + lineOff.left - wrapOff.left)) + "px";
754 }
771 }
755 }
772 }
756
773
757 // No selection, plain cursor
774 // No selection, plain cursor
758 function updateSelectionCursor(cm) {
775 function updateSelectionCursor(cm) {
759 var display = cm.display, pos = cursorCoords(cm, cm.doc.sel.head, "div");
776 var display = cm.display, pos = cursorCoords(cm, cm.doc.sel.head, "div");
760 display.cursor.style.left = pos.left + "px";
777 display.cursor.style.left = pos.left + "px";
761 display.cursor.style.top = pos.top + "px";
778 display.cursor.style.top = pos.top + "px";
762 display.cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px";
779 display.cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px";
763 display.cursor.style.display = "";
780 display.cursor.style.display = "";
764
781
765 if (pos.other) {
782 if (pos.other) {
766 display.otherCursor.style.display = "";
783 display.otherCursor.style.display = "";
767 display.otherCursor.style.left = pos.other.left + "px";
784 display.otherCursor.style.left = pos.other.left + "px";
768 display.otherCursor.style.top = pos.other.top + "px";
785 display.otherCursor.style.top = pos.other.top + "px";
769 display.otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px";
786 display.otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px";
770 } else { display.otherCursor.style.display = "none"; }
787 } else { display.otherCursor.style.display = "none"; }
771 }
788 }
772
789
773 // Highlight selection
790 // Highlight selection
774 function updateSelectionRange(cm) {
791 function updateSelectionRange(cm) {
775 var display = cm.display, doc = cm.doc, sel = cm.doc.sel;
792 var display = cm.display, doc = cm.doc, sel = cm.doc.sel;
776 var fragment = document.createDocumentFragment();
793 var fragment = document.createDocumentFragment();
777 var clientWidth = display.lineSpace.offsetWidth, pl = paddingLeft(cm.display);
794 var clientWidth = display.lineSpace.offsetWidth, pl = paddingLeft(cm.display);
778
795
779 function add(left, top, width, bottom) {
796 function add(left, top, width, bottom) {
780 if (top < 0) top = 0;
797 if (top < 0) top = 0;
781 fragment.appendChild(elt("div", null, "CodeMirror-selected", "position: absolute; left: " + left +
798 fragment.appendChild(elt("div", null, "CodeMirror-selected", "position: absolute; left: " + left +
782 "px; top: " + top + "px; width: " + (width == null ? clientWidth - left : width) +
799 "px; top: " + top + "px; width: " + (width == null ? clientWidth - left : width) +
783 "px; height: " + (bottom - top) + "px"));
800 "px; height: " + (bottom - top) + "px"));
784 }
801 }
785
802
786 function drawForLine(line, fromArg, toArg, retTop) {
803 function drawForLine(line, fromArg, toArg) {
787 var lineObj = getLine(doc, line);
804 var lineObj = getLine(doc, line);
788 var lineLen = lineObj.text.length, rVal = retTop ? Infinity : -Infinity;
805 var lineLen = lineObj.text.length;
789 function coords(ch) {
806 var start, end;
790 return charCoords(cm, Pos(line, ch), "div", lineObj);
807 function coords(ch, bias) {
808 return charCoords(cm, Pos(line, ch), "div", lineObj, bias);
791 }
809 }
792
810
793 iterateBidiSections(getOrder(lineObj), fromArg || 0, toArg == null ? lineLen : toArg, function(from, to, dir) {
811 iterateBidiSections(getOrder(lineObj), fromArg || 0, toArg == null ? lineLen : toArg, function(from, to, dir) {
794 var leftPos = coords(from), rightPos, left, right;
812 var leftPos = coords(from, "left"), rightPos, left, right;
795 if (from == to) {
813 if (from == to) {
796 rightPos = leftPos;
814 rightPos = leftPos;
797 left = right = leftPos.left;
815 left = right = leftPos.left;
798 } else {
816 } else {
799 rightPos = coords(to - 1);
817 rightPos = coords(to - 1, "right");
800 if (dir == "rtl") { var tmp = leftPos; leftPos = rightPos; rightPos = tmp; }
818 if (dir == "rtl") { var tmp = leftPos; leftPos = rightPos; rightPos = tmp; }
801 left = leftPos.left;
819 left = leftPos.left;
802 right = rightPos.right;
820 right = rightPos.right;
803 }
821 }
822 if (fromArg == null && from == 0) left = pl;
804 if (rightPos.top - leftPos.top > 3) { // Different lines, draw top part
823 if (rightPos.top - leftPos.top > 3) { // Different lines, draw top part
805 add(left, leftPos.top, null, leftPos.bottom);
824 add(left, leftPos.top, null, leftPos.bottom);
806 left = pl;
825 left = pl;
807 if (leftPos.bottom < rightPos.top) add(left, leftPos.bottom, null, rightPos.top);
826 if (leftPos.bottom < rightPos.top) add(left, leftPos.bottom, null, rightPos.top);
808 }
827 }
809 if (toArg == null && to == lineLen) right = clientWidth;
828 if (toArg == null && to == lineLen) right = clientWidth;
810 if (fromArg == null && from == 0) left = pl;
829 if (!start || leftPos.top < start.top || leftPos.top == start.top && leftPos.left < start.left)
811 rVal = retTop ? Math.min(rightPos.top, rVal) : Math.max(rightPos.bottom, rVal);
830 start = leftPos;
831 if (!end || rightPos.bottom > end.bottom || rightPos.bottom == end.bottom && rightPos.right > end.right)
832 end = rightPos;
812 if (left < pl + 1) left = pl;
833 if (left < pl + 1) left = pl;
813 add(left, rightPos.top, right - left, rightPos.bottom);
834 add(left, rightPos.top, right - left, rightPos.bottom);
814 });
835 });
815 return rVal;
836 return {start: start, end: end};
816 }
837 }
817
838
818 if (sel.from.line == sel.to.line) {
839 if (sel.from.line == sel.to.line) {
819 drawForLine(sel.from.line, sel.from.ch, sel.to.ch);
840 drawForLine(sel.from.line, sel.from.ch, sel.to.ch);
820 } else {
841 } else {
821 var fromObj = getLine(doc, sel.from.line);
842 var fromLine = getLine(doc, sel.from.line), toLine = getLine(doc, sel.to.line);
822 var cur = fromObj, merged, path = [sel.from.line, sel.from.ch], singleLine;
843 var singleVLine = visualLine(doc, fromLine) == visualLine(doc, toLine);
823 while (merged = collapsedSpanAtEnd(cur)) {
844 var leftEnd = drawForLine(sel.from.line, sel.from.ch, singleVLine ? fromLine.text.length : null).end;
824 var found = merged.find();
845 var rightStart = drawForLine(sel.to.line, singleVLine ? 0 : null, sel.to.ch).start;
825 path.push(found.from.ch, found.to.line, found.to.ch);
846 if (singleVLine) {
826 if (found.to.line == sel.to.line) {
847 if (leftEnd.top < rightStart.top - 2) {
827 path.push(sel.to.ch);
848 add(leftEnd.right, leftEnd.top, null, leftEnd.bottom);
828 singleLine = true;
849 add(pl, rightStart.top, rightStart.left, rightStart.bottom);
829 break;
850 } else {
851 add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom);
830 }
852 }
831 cur = getLine(doc, found.to.line);
832 }
853 }
833
854 if (leftEnd.bottom < rightStart.top)
834 // This is a single, merged line
855 add(pl, leftEnd.bottom, null, rightStart.top);
835 if (singleLine) {
836 for (var i = 0; i < path.length; i += 3)
837 drawForLine(path[i], path[i+1], path[i+2]);
838 } else {
839 var middleTop, middleBot, toObj = getLine(doc, sel.to.line);
840 if (sel.from.ch)
841 // Draw the first line of selection.
842 middleTop = drawForLine(sel.from.line, sel.from.ch, null, false);
843 else
844 // Simply include it in the middle block.
845 middleTop = heightAtLine(cm, fromObj) - display.viewOffset;
846
847 if (!sel.to.ch)
848 middleBot = heightAtLine(cm, toObj) - display.viewOffset;
849 else
850 middleBot = drawForLine(sel.to.line, collapsedSpanAtStart(toObj) ? null : 0, sel.to.ch, true);
851
852 if (middleTop < middleBot) add(pl, middleTop, null, middleBot);
853 }
854 }
856 }
855
857
856 removeChildrenAndAdd(display.selectionDiv, fragment);
858 removeChildrenAndAdd(display.selectionDiv, fragment);
857 display.selectionDiv.style.display = "";
859 display.selectionDiv.style.display = "";
858 }
860 }
859
861
860 // Cursor-blinking
862 // Cursor-blinking
861 function restartBlink(cm) {
863 function restartBlink(cm) {
862 if (!cm.state.focused) return;
864 if (!cm.state.focused) return;
863 var display = cm.display;
865 var display = cm.display;
864 clearInterval(display.blinker);
866 clearInterval(display.blinker);
865 var on = true;
867 var on = true;
866 display.cursor.style.visibility = display.otherCursor.style.visibility = "";
868 display.cursor.style.visibility = display.otherCursor.style.visibility = "";
867 display.blinker = setInterval(function() {
869 display.blinker = setInterval(function() {
868 display.cursor.style.visibility = display.otherCursor.style.visibility = (on = !on) ? "" : "hidden";
870 display.cursor.style.visibility = display.otherCursor.style.visibility = (on = !on) ? "" : "hidden";
869 }, cm.options.cursorBlinkRate);
871 }, cm.options.cursorBlinkRate);
870 }
872 }
871
873
872 // HIGHLIGHT WORKER
874 // HIGHLIGHT WORKER
873
875
874 function startWorker(cm, time) {
876 function startWorker(cm, time) {
875 if (cm.doc.mode.startState && cm.doc.frontier < cm.display.showingTo)
877 if (cm.doc.mode.startState && cm.doc.frontier < cm.display.showingTo)
876 cm.state.highlight.set(time, bind(highlightWorker, cm));
878 cm.state.highlight.set(time, bind(highlightWorker, cm));
877 }
879 }
878
880
879 function highlightWorker(cm) {
881 function highlightWorker(cm) {
880 var doc = cm.doc;
882 var doc = cm.doc;
881 if (doc.frontier < doc.first) doc.frontier = doc.first;
883 if (doc.frontier < doc.first) doc.frontier = doc.first;
882 if (doc.frontier >= cm.display.showingTo) return;
884 if (doc.frontier >= cm.display.showingTo) return;
883 var end = +new Date + cm.options.workTime;
885 var end = +new Date + cm.options.workTime;
884 var state = copyState(doc.mode, getStateBefore(cm, doc.frontier));
886 var state = copyState(doc.mode, getStateBefore(cm, doc.frontier));
885 var changed = [], prevChange;
887 var changed = [], prevChange;
886 doc.iter(doc.frontier, Math.min(doc.first + doc.size, cm.display.showingTo + 500), function(line) {
888 doc.iter(doc.frontier, Math.min(doc.first + doc.size, cm.display.showingTo + 500), function(line) {
887 if (doc.frontier >= cm.display.showingFrom) { // Visible
889 if (doc.frontier >= cm.display.showingFrom) { // Visible
888 var oldStyles = line.styles;
890 var oldStyles = line.styles;
889 line.styles = highlightLine(cm, line, state);
891 line.styles = highlightLine(cm, line, state);
890 var ischange = !oldStyles || oldStyles.length != line.styles.length;
892 var ischange = !oldStyles || oldStyles.length != line.styles.length;
891 for (var i = 0; !ischange && i < oldStyles.length; ++i) ischange = oldStyles[i] != line.styles[i];
893 for (var i = 0; !ischange && i < oldStyles.length; ++i) ischange = oldStyles[i] != line.styles[i];
892 if (ischange) {
894 if (ischange) {
893 if (prevChange && prevChange.end == doc.frontier) prevChange.end++;
895 if (prevChange && prevChange.end == doc.frontier) prevChange.end++;
894 else changed.push(prevChange = {start: doc.frontier, end: doc.frontier + 1});
896 else changed.push(prevChange = {start: doc.frontier, end: doc.frontier + 1});
895 }
897 }
896 line.stateAfter = copyState(doc.mode, state);
898 line.stateAfter = copyState(doc.mode, state);
897 } else {
899 } else {
898 processLine(cm, line, state);
900 processLine(cm, line, state);
899 line.stateAfter = doc.frontier % 5 == 0 ? copyState(doc.mode, state) : null;
901 line.stateAfter = doc.frontier % 5 == 0 ? copyState(doc.mode, state) : null;
900 }
902 }
901 ++doc.frontier;
903 ++doc.frontier;
902 if (+new Date > end) {
904 if (+new Date > end) {
903 startWorker(cm, cm.options.workDelay);
905 startWorker(cm, cm.options.workDelay);
904 return true;
906 return true;
905 }
907 }
906 });
908 });
907 if (changed.length)
909 if (changed.length)
908 operation(cm, function() {
910 operation(cm, function() {
909 for (var i = 0; i < changed.length; ++i)
911 for (var i = 0; i < changed.length; ++i)
910 regChange(this, changed[i].start, changed[i].end);
912 regChange(this, changed[i].start, changed[i].end);
911 })();
913 })();
912 }
914 }
913
915
914 // Finds the line to start with when starting a parse. Tries to
916 // Finds the line to start with when starting a parse. Tries to
915 // find a line with a stateAfter, so that it can start with a
917 // find a line with a stateAfter, so that it can start with a
916 // valid state. If that fails, it returns the line with the
918 // valid state. If that fails, it returns the line with the
917 // smallest indentation, which tends to need the least context to
919 // smallest indentation, which tends to need the least context to
918 // parse correctly.
920 // parse correctly.
919 function findStartLine(cm, n) {
921 function findStartLine(cm, n, precise) {
920 var minindent, minline, doc = cm.doc;
922 var minindent, minline, doc = cm.doc;
921 for (var search = n, lim = n - 100; search > lim; --search) {
923 for (var search = n, lim = n - 100; search > lim; --search) {
922 if (search <= doc.first) return doc.first;
924 if (search <= doc.first) return doc.first;
923 var line = getLine(doc, search - 1);
925 var line = getLine(doc, search - 1);
924 if (line.stateAfter) return search;
926 if (line.stateAfter && (!precise || search <= doc.frontier)) return search;
925 var indented = countColumn(line.text, null, cm.options.tabSize);
927 var indented = countColumn(line.text, null, cm.options.tabSize);
926 if (minline == null || minindent > indented) {
928 if (minline == null || minindent > indented) {
927 minline = search - 1;
929 minline = search - 1;
928 minindent = indented;
930 minindent = indented;
929 }
931 }
930 }
932 }
931 return minline;
933 return minline;
932 }
934 }
933
935
934 function getStateBefore(cm, n) {
936 function getStateBefore(cm, n, precise) {
935 var doc = cm.doc, display = cm.display;
937 var doc = cm.doc, display = cm.display;
936 if (!doc.mode.startState) return true;
938 if (!doc.mode.startState) return true;
937 var pos = findStartLine(cm, n), state = pos > doc.first && getLine(doc, pos-1).stateAfter;
939 var pos = findStartLine(cm, n, precise), state = pos > doc.first && getLine(doc, pos-1).stateAfter;
938 if (!state) state = startState(doc.mode);
940 if (!state) state = startState(doc.mode);
939 else state = copyState(doc.mode, state);
941 else state = copyState(doc.mode, state);
940 doc.iter(pos, n, function(line) {
942 doc.iter(pos, n, function(line) {
941 processLine(cm, line, state);
943 processLine(cm, line, state);
942 var save = pos == n - 1 || pos % 5 == 0 || pos >= display.showingFrom && pos < display.showingTo;
944 var save = pos == n - 1 || pos % 5 == 0 || pos >= display.showingFrom && pos < display.showingTo;
943 line.stateAfter = save ? copyState(doc.mode, state) : null;
945 line.stateAfter = save ? copyState(doc.mode, state) : null;
944 ++pos;
946 ++pos;
945 });
947 });
946 return state;
948 return state;
947 }
949 }
948
950
949 // POSITION MEASUREMENT
951 // POSITION MEASUREMENT
950
952
951 function paddingTop(display) {return display.lineSpace.offsetTop;}
953 function paddingTop(display) {return display.lineSpace.offsetTop;}
952 function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight;}
954 function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight;}
953 function paddingLeft(display) {
955 function paddingLeft(display) {
954 var e = removeChildrenAndAdd(display.measure, elt("pre", null, null, "text-align: left")).appendChild(elt("span", "x"));
956 var e = removeChildrenAndAdd(display.measure, elt("pre", null, null, "text-align: left")).appendChild(elt("span", "x"));
955 return e.offsetLeft;
957 return e.offsetLeft;
956 }
958 }
957
959
958 function measureChar(cm, line, ch, data) {
960 function measureChar(cm, line, ch, data, bias) {
959 var dir = -1;
961 var dir = -1;
960 data = data || measureLine(cm, line);
962 data = data || measureLine(cm, line);
961
963
962 for (var pos = ch;; pos += dir) {
964 for (var pos = ch;; pos += dir) {
963 var r = data[pos];
965 var r = data[pos];
964 if (r) break;
966 if (r) break;
965 if (dir < 0 && pos == 0) dir = 1;
967 if (dir < 0 && pos == 0) dir = 1;
966 }
968 }
969 var rightV = (pos < ch || bias == "right") && r.topRight != null;
967 return {left: pos < ch ? r.right : r.left,
970 return {left: pos < ch ? r.right : r.left,
968 right: pos > ch ? r.left : r.right,
971 right: pos > ch ? r.left : r.right,
969 top: r.top, bottom: r.bottom};
972 top: rightV ? r.topRight : r.top,
973 bottom: rightV ? r.bottomRight : r.bottom};
970 }
974 }
971
975
972 function findCachedMeasurement(cm, line) {
976 function findCachedMeasurement(cm, line) {
973 var cache = cm.display.measureLineCache;
977 var cache = cm.display.measureLineCache;
974 for (var i = 0; i < cache.length; ++i) {
978 for (var i = 0; i < cache.length; ++i) {
975 var memo = cache[i];
979 var memo = cache[i];
976 if (memo.text == line.text && memo.markedSpans == line.markedSpans &&
980 if (memo.text == line.text && memo.markedSpans == line.markedSpans &&
977 cm.display.scroller.clientWidth == memo.width &&
981 cm.display.scroller.clientWidth == memo.width &&
978 memo.classes == line.textClass + "|" + line.bgClass + "|" + line.wrapClass)
982 memo.classes == line.textClass + "|" + line.bgClass + "|" + line.wrapClass)
979 return memo.measure;
983 return memo;
980 }
984 }
985 }
986
987 function clearCachedMeasurement(cm, line) {
988 var exists = findCachedMeasurement(cm, line);
989 if (exists) exists.text = exists.measure = exists.markedSpans = null;
981 }
990 }
982
991
983 function measureLine(cm, line) {
992 function measureLine(cm, line) {
984 // First look in the cache
993 // First look in the cache
985 var measure = findCachedMeasurement(cm, line);
994 var cached = findCachedMeasurement(cm, line);
986 if (!measure) {
995 if (cached) return cached.measure;
987 // Failing that, recompute and store result in cache
996
988 measure = measureLineInner(cm, line);
997 // Failing that, recompute and store result in cache
989 var cache = cm.display.measureLineCache;
998 var measure = measureLineInner(cm, line);
990 var memo = {text: line.text, width: cm.display.scroller.clientWidth,
999 var cache = cm.display.measureLineCache;
991 markedSpans: line.markedSpans, measure: measure,
1000 var memo = {text: line.text, width: cm.display.scroller.clientWidth,
992 classes: line.textClass + "|" + line.bgClass + "|" + line.wrapClass};
1001 markedSpans: line.markedSpans, measure: measure,
993 if (cache.length == 16) cache[++cm.display.measureLineCachePos % 16] = memo;
1002 classes: line.textClass + "|" + line.bgClass + "|" + line.wrapClass};
994 else cache.push(memo);
1003 if (cache.length == 16) cache[++cm.display.measureLineCachePos % 16] = memo;
995 }
1004 else cache.push(memo);
996 return measure;
1005 return measure;
997 }
1006 }
998
1007
999 function measureLineInner(cm, line) {
1008 function measureLineInner(cm, line) {
1000 var display = cm.display, measure = emptyArray(line.text.length);
1009 var display = cm.display, measure = emptyArray(line.text.length);
1001 var pre = lineContent(cm, line, measure);
1010 var pre = lineContent(cm, line, measure);
1002
1011
1003 // IE does not cache element positions of inline elements between
1012 // IE does not cache element positions of inline elements between
1004 // calls to getBoundingClientRect. This makes the loop below,
1013 // calls to getBoundingClientRect. This makes the loop below,
1005 // which gathers the positions of all the characters on the line,
1014 // which gathers the positions of all the characters on the line,
1006 // do an amount of layout work quadratic to the number of
1015 // do an amount of layout work quadratic to the number of
1007 // characters. When line wrapping is off, we try to improve things
1016 // characters. When line wrapping is off, we try to improve things
1008 // by first subdividing the line into a bunch of inline blocks, so
1017 // by first subdividing the line into a bunch of inline blocks, so
1009 // that IE can reuse most of the layout information from caches
1018 // that IE can reuse most of the layout information from caches
1010 // for those blocks. This does interfere with line wrapping, so it
1019 // for those blocks. This does interfere with line wrapping, so it
1011 // doesn't work when wrapping is on, but in that case the
1020 // doesn't work when wrapping is on, but in that case the
1012 // situation is slightly better, since IE does cache line-wrapping
1021 // situation is slightly better, since IE does cache line-wrapping
1013 // information and only recomputes per-line.
1022 // information and only recomputes per-line.
1014 if (ie && !ie_lt8 && !cm.options.lineWrapping && pre.childNodes.length > 100) {
1023 if (ie && !ie_lt8 && !cm.options.lineWrapping && pre.childNodes.length > 100) {
1015 var fragment = document.createDocumentFragment();
1024 var fragment = document.createDocumentFragment();
1016 var chunk = 10, n = pre.childNodes.length;
1025 var chunk = 10, n = pre.childNodes.length;
1017 for (var i = 0, chunks = Math.ceil(n / chunk); i < chunks; ++i) {
1026 for (var i = 0, chunks = Math.ceil(n / chunk); i < chunks; ++i) {
1018 var wrap = elt("div", null, null, "display: inline-block");
1027 var wrap = elt("div", null, null, "display: inline-block");
1019 for (var j = 0; j < chunk && n; ++j) {
1028 for (var j = 0; j < chunk && n; ++j) {
1020 wrap.appendChild(pre.firstChild);
1029 wrap.appendChild(pre.firstChild);
1021 --n;
1030 --n;
1022 }
1031 }
1023 fragment.appendChild(wrap);
1032 fragment.appendChild(wrap);
1024 }
1033 }
1025 pre.appendChild(fragment);
1034 pre.appendChild(fragment);
1026 }
1035 }
1027
1036
1028 removeChildrenAndAdd(display.measure, pre);
1037 removeChildrenAndAdd(display.measure, pre);
1029
1038
1030 var outer = getRect(display.lineDiv);
1039 var outer = getRect(display.lineDiv);
1031 var vranges = [], data = emptyArray(line.text.length), maxBot = pre.offsetHeight;
1040 var vranges = [], data = emptyArray(line.text.length), maxBot = pre.offsetHeight;
1032 // Work around an IE7/8 bug where it will sometimes have randomly
1041 // Work around an IE7/8 bug where it will sometimes have randomly
1033 // replaced our pre with a clone at this point.
1042 // replaced our pre with a clone at this point.
1034 if (ie_lt9 && display.measure.first != pre)
1043 if (ie_lt9 && display.measure.first != pre)
1035 removeChildrenAndAdd(display.measure, pre);
1044 removeChildrenAndAdd(display.measure, pre);
1036
1045
1037 for (var i = 0, cur; i < measure.length; ++i) if (cur = measure[i]) {
1046 function categorizeVSpan(top, bot) {
1038 var size = getRect(cur);
1047 if (bot > maxBot) bot = maxBot;
1039 var top = Math.max(0, size.top - outer.top), bot = Math.min(size.bottom - outer.top, maxBot);
1048 if (top < 0) top = 0;
1040 for (var j = 0; j < vranges.length; j += 2) {
1049 for (var j = 0; j < vranges.length; j += 2) {
1041 var rtop = vranges[j], rbot = vranges[j+1];
1050 var rtop = vranges[j], rbot = vranges[j+1];
1042 if (rtop > bot || rbot < top) continue;
1051 if (rtop > bot || rbot < top) continue;
1043 if (rtop <= top && rbot >= bot ||
1052 if (rtop <= top && rbot >= bot ||
1044 top <= rtop && bot >= rbot ||
1053 top <= rtop && bot >= rbot ||
1045 Math.min(bot, rbot) - Math.max(top, rtop) >= (bot - top) >> 1) {
1054 Math.min(bot, rbot) - Math.max(top, rtop) >= (bot - top) >> 1) {
1046 vranges[j] = Math.min(top, rtop);
1055 vranges[j] = Math.min(top, rtop);
1047 vranges[j+1] = Math.max(bot, rbot);
1056 vranges[j+1] = Math.max(bot, rbot);
1048 break;
1057 return j;
1049 }
1058 }
1050 }
1059 }
1051 if (j == vranges.length) vranges.push(top, bot);
1060 vranges.push(top, bot);
1061 return j;
1062 }
1063
1064 for (var i = 0, cur; i < measure.length; ++i) if (cur = measure[i]) {
1065 var size, node = cur;
1066 // A widget might wrap, needs special care
1067 if (/\bCodeMirror-widget\b/.test(cur.className) && cur.getClientRects) {
1068 if (cur.firstChild.nodeType == 1) node = cur.firstChild;
1069 var rects = node.getClientRects(), rLeft = rects[0], rRight = rects[rects.length - 1];
1070 if (rects.length > 1) {
1071 var vCatLeft = categorizeVSpan(rLeft.top - outer.top, rLeft.bottom - outer.top);
1072 var vCatRight = categorizeVSpan(rRight.top - outer.top, rRight.bottom - outer.top);
1073 data[i] = {left: rLeft.left - outer.left, right: rRight.right - outer.left,
1074 top: vCatLeft, topRight: vCatRight};
1075 continue;
1076 }
1077 }
1078 size = getRect(node);
1079 var vCat = categorizeVSpan(size.top - outer.top, size.bottom - outer.top);
1052 var right = size.right;
1080 var right = size.right;
1053 if (cur.measureRight) right = getRect(cur.measureRight).left;
1081 if (cur.measureRight) right = getRect(cur.measureRight).left;
1054 data[i] = {left: size.left - outer.left, right: right - outer.left, top: j};
1082 data[i] = {left: size.left - outer.left, right: right - outer.left, top: vCat};
1055 }
1083 }
1056 for (var i = 0, cur; i < data.length; ++i) if (cur = data[i]) {
1084 for (var i = 0, cur; i < data.length; ++i) if (cur = data[i]) {
1057 var vr = cur.top;
1085 var vr = cur.top, vrRight = cur.topRight;
1058 cur.top = vranges[vr]; cur.bottom = vranges[vr+1];
1086 cur.top = vranges[vr]; cur.bottom = vranges[vr+1];
1059 }
1087 if (vrRight != null) { cur.topRight = vranges[vrRight]; cur.bottomRight = vranges[vrRight+1]; }
1060
1088 }
1061 return data;
1089 return data;
1062 }
1090 }
1063
1091
1064 function measureLineWidth(cm, line) {
1092 function measureLineWidth(cm, line) {
1065 var hasBadSpan = false;
1093 var hasBadSpan = false;
1066 if (line.markedSpans) for (var i = 0; i < line.markedSpans; ++i) {
1094 if (line.markedSpans) for (var i = 0; i < line.markedSpans; ++i) {
1067 var sp = line.markedSpans[i];
1095 var sp = line.markedSpans[i];
1068 if (sp.collapsed && (sp.to == null || sp.to == line.text.length)) hasBadSpan = true;
1096 if (sp.collapsed && (sp.to == null || sp.to == line.text.length)) hasBadSpan = true;
1069 }
1097 }
1070 var cached = !hasBadSpan && findCachedMeasurement(cm, line);
1098 var cached = !hasBadSpan && findCachedMeasurement(cm, line);
1071 if (cached) return measureChar(cm, line, line.text.length, cached).right;
1099 if (cached) return measureChar(cm, line, line.text.length, cached.measure, "right").right;
1072
1100
1073 var pre = lineContent(cm, line);
1101 var pre = lineContent(cm, line);
1074 var end = pre.appendChild(zeroWidthElement(cm.display.measure));
1102 var end = pre.appendChild(zeroWidthElement(cm.display.measure));
1075 removeChildrenAndAdd(cm.display.measure, pre);
1103 removeChildrenAndAdd(cm.display.measure, pre);
1076 return getRect(end).right - getRect(cm.display.lineDiv).left;
1104 return getRect(end).right - getRect(cm.display.lineDiv).left;
1077 }
1105 }
1078
1106
1079 function clearCaches(cm) {
1107 function clearCaches(cm) {
1080 cm.display.measureLineCache.length = cm.display.measureLineCachePos = 0;
1108 cm.display.measureLineCache.length = cm.display.measureLineCachePos = 0;
1081 cm.display.cachedCharWidth = cm.display.cachedTextHeight = null;
1109 cm.display.cachedCharWidth = cm.display.cachedTextHeight = null;
1082 if (!cm.options.lineWrapping) cm.display.maxLineChanged = true;
1110 if (!cm.options.lineWrapping) cm.display.maxLineChanged = true;
1083 cm.display.lineNumChars = null;
1111 cm.display.lineNumChars = null;
1084 }
1112 }
1085
1113
1114 function pageScrollX() { return window.pageXOffset || (document.documentElement || document.body).scrollLeft; }
1115 function pageScrollY() { return window.pageYOffset || (document.documentElement || document.body).scrollTop; }
1116
1086 // Context is one of "line", "div" (display.lineDiv), "local"/null (editor), or "page"
1117 // Context is one of "line", "div" (display.lineDiv), "local"/null (editor), or "page"
1087 function intoCoordSystem(cm, lineObj, rect, context) {
1118 function intoCoordSystem(cm, lineObj, rect, context) {
1088 if (lineObj.widgets) for (var i = 0; i < lineObj.widgets.length; ++i) if (lineObj.widgets[i].above) {
1119 if (lineObj.widgets) for (var i = 0; i < lineObj.widgets.length; ++i) if (lineObj.widgets[i].above) {
1089 var size = widgetHeight(lineObj.widgets[i]);
1120 var size = widgetHeight(lineObj.widgets[i]);
1090 rect.top += size; rect.bottom += size;
1121 rect.top += size; rect.bottom += size;
1091 }
1122 }
1092 if (context == "line") return rect;
1123 if (context == "line") return rect;
1093 if (!context) context = "local";
1124 if (!context) context = "local";
1094 var yOff = heightAtLine(cm, lineObj);
1125 var yOff = heightAtLine(cm, lineObj);
1095 if (context != "local") yOff -= cm.display.viewOffset;
1126 if (context == "local") yOff += paddingTop(cm.display);
1096 if (context == "page") {
1127 else yOff -= cm.display.viewOffset;
1128 if (context == "page" || context == "window") {
1097 var lOff = getRect(cm.display.lineSpace);
1129 var lOff = getRect(cm.display.lineSpace);
1098 yOff += lOff.top + (window.pageYOffset || (document.documentElement || document.body).scrollTop);
1130 yOff += lOff.top + (context == "window" ? 0 : pageScrollY());
1099 var xOff = lOff.left + (window.pageXOffset || (document.documentElement || document.body).scrollLeft);
1131 var xOff = lOff.left + (context == "window" ? 0 : pageScrollX());
1100 rect.left += xOff; rect.right += xOff;
1132 rect.left += xOff; rect.right += xOff;
1101 }
1133 }
1102 rect.top += yOff; rect.bottom += yOff;
1134 rect.top += yOff; rect.bottom += yOff;
1103 return rect;
1135 return rect;
1104 }
1136 }
1105
1137
1106 // Context may be "window", "page", "div", or "local"/null
1138 // Context may be "window", "page", "div", or "local"/null
1107 // Result is in "div" coords
1139 // Result is in "div" coords
1108 function fromCoordSystem(cm, coords, context) {
1140 function fromCoordSystem(cm, coords, context) {
1109 if (context == "div") return coords;
1141 if (context == "div") return coords;
1110 var left = coords.left, top = coords.top;
1142 var left = coords.left, top = coords.top;
1143 // First move into "page" coordinate system
1111 if (context == "page") {
1144 if (context == "page") {
1112 left -= window.pageXOffset || (document.documentElement || document.body).scrollLeft;
1145 left -= pageScrollX();
1113 top -= window.pageYOffset || (document.documentElement || document.body).scrollTop;
1146 top -= pageScrollY();
1114 }
1147 } else if (context == "local" || !context) {
1148 var localBox = getRect(cm.display.sizer);
1149 left += localBox.left;
1150 top += localBox.top;
1151 }
1152
1115 var lineSpaceBox = getRect(cm.display.lineSpace);
1153 var lineSpaceBox = getRect(cm.display.lineSpace);
1116 left -= lineSpaceBox.left;
1154 return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top};
1117 top -= lineSpaceBox.top;
1155 }
1118 if (context == "local" || !context) {
1156
1119 var editorBox = getRect(cm.display.wrapper);
1157 function charCoords(cm, pos, context, lineObj, bias) {
1120 left += editorBox.left;
1121 top += editorBox.top;
1122 }
1123 return {left: left, top: top};
1124 }
1125
1126 function charCoords(cm, pos, context, lineObj) {
1127 if (!lineObj) lineObj = getLine(cm.doc, pos.line);
1158 if (!lineObj) lineObj = getLine(cm.doc, pos.line);
1128 return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch), context);
1159 return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, null, bias), context);
1129 }
1160 }
1130
1161
1131 function cursorCoords(cm, pos, context, lineObj, measurement) {
1162 function cursorCoords(cm, pos, context, lineObj, measurement) {
1132 lineObj = lineObj || getLine(cm.doc, pos.line);
1163 lineObj = lineObj || getLine(cm.doc, pos.line);
1133 if (!measurement) measurement = measureLine(cm, lineObj);
1164 if (!measurement) measurement = measureLine(cm, lineObj);
1134 function get(ch, right) {
1165 function get(ch, right) {
1135 var m = measureChar(cm, lineObj, ch, measurement);
1166 var m = measureChar(cm, lineObj, ch, measurement, right ? "right" : "left");
1136 if (right) m.left = m.right; else m.right = m.left;
1167 if (right) m.left = m.right; else m.right = m.left;
1137 return intoCoordSystem(cm, lineObj, m, context);
1168 return intoCoordSystem(cm, lineObj, m, context);
1138 }
1169 }
1170 function getBidi(ch, partPos) {
1171 var part = order[partPos], right = part.level % 2;
1172 if (ch == bidiLeft(part) && partPos && part.level < order[partPos - 1].level) {
1173 part = order[--partPos];
1174 ch = bidiRight(part) - (part.level % 2 ? 0 : 1);
1175 right = true;
1176 } else if (ch == bidiRight(part) && partPos < order.length - 1 && part.level < order[partPos + 1].level) {
1177 part = order[++partPos];
1178 ch = bidiLeft(part) - part.level % 2;
1179 right = false;
1180 }
1181 if (right && ch == part.to && ch > part.from) return get(ch - 1);
1182 return get(ch, right);
1183 }
1139 var order = getOrder(lineObj), ch = pos.ch;
1184 var order = getOrder(lineObj), ch = pos.ch;
1140 if (!order) return get(ch);
1185 if (!order) return get(ch);
1141 var main, other, linedir = order[0].level;
1186 var partPos = getBidiPartAt(order, ch);
1142 for (var i = 0; i < order.length; ++i) {
1187 var val = getBidi(ch, partPos);
1143 var part = order[i], rtl = part.level % 2, nb, here;
1188 if (bidiOther != null) val.other = getBidi(ch, bidiOther);
1144 if (part.from < ch && part.to > ch) return get(ch, rtl);
1189 return val;
1145 var left = rtl ? part.to : part.from, right = rtl ? part.from : part.to;
1190 }
1146 if (left == ch) {
1191
1147 // IE returns bogus offsets and widths for edges where the
1192 function PosWithInfo(line, ch, outside, xRel) {
1148 // direction flips, but only for the side with the lower
1149 // level. So we try to use the side with the higher level.
1150 if (i && part.level < (nb = order[i-1]).level) here = get(nb.level % 2 ? nb.from : nb.to - 1, true);
1151 else here = get(rtl && part.from != part.to ? ch - 1 : ch);
1152 if (rtl == linedir) main = here; else other = here;
1153 } else if (right == ch) {
1154 var nb = i < order.length - 1 && order[i+1];
1155 if (!rtl && nb && nb.from == nb.to) continue;
1156 if (nb && part.level < nb.level) here = get(nb.level % 2 ? nb.to - 1 : nb.from);
1157 else here = get(rtl ? ch : ch - 1, true);
1158 if (rtl == linedir) main = here; else other = here;
1159 }
1160 }
1161 if (linedir && !ch) other = get(order[0].to - 1);
1162 if (!main) return other;
1163 if (other) main.other = other;
1164 return main;
1165 }
1166
1167 function PosMaybeOutside(line, ch, outside) {
1168 var pos = new Pos(line, ch);
1193 var pos = new Pos(line, ch);
1194 pos.xRel = xRel;
1169 if (outside) pos.outside = true;
1195 if (outside) pos.outside = true;
1170 return pos;
1196 return pos;
1171 }
1197 }
1172
1198
1173 // Coords must be lineSpace-local
1199 // Coords must be lineSpace-local
1174 function coordsChar(cm, x, y) {
1200 function coordsChar(cm, x, y) {
1175 var doc = cm.doc;
1201 var doc = cm.doc;
1176 y += cm.display.viewOffset;
1202 y += cm.display.viewOffset;
1177 if (y < 0) return PosMaybeOutside(doc.first, 0, true);
1203 if (y < 0) return PosWithInfo(doc.first, 0, true, -1);
1178 var lineNo = lineAtHeight(doc, y), last = doc.first + doc.size - 1;
1204 var lineNo = lineAtHeight(doc, y), last = doc.first + doc.size - 1;
1179 if (lineNo > last)
1205 if (lineNo > last)
1180 return PosMaybeOutside(doc.first + doc.size - 1, getLine(doc, last).text.length, true);
1206 return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, true, 1);
1181 if (x < 0) x = 0;
1207 if (x < 0) x = 0;
1182
1208
1183 for (;;) {
1209 for (;;) {
1184 var lineObj = getLine(doc, lineNo);
1210 var lineObj = getLine(doc, lineNo);
1185 var found = coordsCharInner(cm, lineObj, lineNo, x, y);
1211 var found = coordsCharInner(cm, lineObj, lineNo, x, y);
1186 var merged = collapsedSpanAtEnd(lineObj);
1212 var merged = collapsedSpanAtEnd(lineObj);
1187 var mergedPos = merged && merged.find();
1213 var mergedPos = merged && merged.find();
1188 if (merged && found.ch >= mergedPos.from.ch)
1214 if (merged && (found.ch > mergedPos.from.ch || found.ch == mergedPos.from.ch && found.xRel > 0))
1189 lineNo = mergedPos.to.line;
1215 lineNo = mergedPos.to.line;
1190 else
1216 else
1191 return found;
1217 return found;
1192 }
1218 }
1193 }
1219 }
1194
1220
1195 function coordsCharInner(cm, lineObj, lineNo, x, y) {
1221 function coordsCharInner(cm, lineObj, lineNo, x, y) {
1196 var innerOff = y - heightAtLine(cm, lineObj);
1222 var innerOff = y - heightAtLine(cm, lineObj);
1197 var wrongLine = false, adjust = 2 * cm.display.wrapper.clientWidth;
1223 var wrongLine = false, adjust = 2 * cm.display.wrapper.clientWidth;
1198 var measurement = measureLine(cm, lineObj);
1224 var measurement = measureLine(cm, lineObj);
1199
1225
1200 function getX(ch) {
1226 function getX(ch) {
1201 var sp = cursorCoords(cm, Pos(lineNo, ch), "line",
1227 var sp = cursorCoords(cm, Pos(lineNo, ch), "line",
1202 lineObj, measurement);
1228 lineObj, measurement);
1203 wrongLine = true;
1229 wrongLine = true;
1204 if (innerOff > sp.bottom) return sp.left - adjust;
1230 if (innerOff > sp.bottom) return sp.left - adjust;
1205 else if (innerOff < sp.top) return sp.left + adjust;
1231 else if (innerOff < sp.top) return sp.left + adjust;
1206 else wrongLine = false;
1232 else wrongLine = false;
1207 return sp.left;
1233 return sp.left;
1208 }
1234 }
1209
1235
1210 var bidi = getOrder(lineObj), dist = lineObj.text.length;
1236 var bidi = getOrder(lineObj), dist = lineObj.text.length;
1211 var from = lineLeft(lineObj), to = lineRight(lineObj);
1237 var from = lineLeft(lineObj), to = lineRight(lineObj);
1212 var fromX = getX(from), fromOutside = wrongLine, toX = getX(to), toOutside = wrongLine;
1238 var fromX = getX(from), fromOutside = wrongLine, toX = getX(to), toOutside = wrongLine;
1213
1239
1214 if (x > toX) return PosMaybeOutside(lineNo, to, toOutside);
1240 if (x > toX) return PosWithInfo(lineNo, to, toOutside, 1);
1215 // Do a binary search between these bounds.
1241 // Do a binary search between these bounds.
1216 for (;;) {
1242 for (;;) {
1217 if (bidi ? to == from || to == moveVisually(lineObj, from, 1) : to - from <= 1) {
1243 if (bidi ? to == from || to == moveVisually(lineObj, from, 1) : to - from <= 1) {
1218 var after = x - fromX < toX - x, ch = after ? from : to;
1244 var ch = x < fromX || x - fromX <= toX - x ? from : to;
1245 var xDiff = x - (ch == from ? fromX : toX);
1219 while (isExtendingChar.test(lineObj.text.charAt(ch))) ++ch;
1246 while (isExtendingChar.test(lineObj.text.charAt(ch))) ++ch;
1220 var pos = PosMaybeOutside(lineNo, ch, after ? fromOutside : toOutside);
1247 var pos = PosWithInfo(lineNo, ch, ch == from ? fromOutside : toOutside,
1221 pos.after = after;
1248 xDiff < 0 ? -1 : xDiff ? 1 : 0);
1222 return pos;
1249 return pos;
1223 }
1250 }
1224 var step = Math.ceil(dist / 2), middle = from + step;
1251 var step = Math.ceil(dist / 2), middle = from + step;
1225 if (bidi) {
1252 if (bidi) {
1226 middle = from;
1253 middle = from;
1227 for (var i = 0; i < step; ++i) middle = moveVisually(lineObj, middle, 1);
1254 for (var i = 0; i < step; ++i) middle = moveVisually(lineObj, middle, 1);
1228 }
1255 }
1229 var middleX = getX(middle);
1256 var middleX = getX(middle);
1230 if (middleX > x) {to = middle; toX = middleX; if (toOutside = wrongLine) toX += 1000; dist = step;}
1257 if (middleX > x) {to = middle; toX = middleX; if (toOutside = wrongLine) toX += 1000; dist = step;}
1231 else {from = middle; fromX = middleX; fromOutside = wrongLine; dist -= step;}
1258 else {from = middle; fromX = middleX; fromOutside = wrongLine; dist -= step;}
1232 }
1259 }
1233 }
1260 }
1234
1261
1235 var measureText;
1262 var measureText;
1236 function textHeight(display) {
1263 function textHeight(display) {
1237 if (display.cachedTextHeight != null) return display.cachedTextHeight;
1264 if (display.cachedTextHeight != null) return display.cachedTextHeight;
1238 if (measureText == null) {
1265 if (measureText == null) {
1239 measureText = elt("pre");
1266 measureText = elt("pre");
1240 // Measure a bunch of lines, for browsers that compute
1267 // Measure a bunch of lines, for browsers that compute
1241 // fractional heights.
1268 // fractional heights.
1242 for (var i = 0; i < 49; ++i) {
1269 for (var i = 0; i < 49; ++i) {
1243 measureText.appendChild(document.createTextNode("x"));
1270 measureText.appendChild(document.createTextNode("x"));
1244 measureText.appendChild(elt("br"));
1271 measureText.appendChild(elt("br"));
1245 }
1272 }
1246 measureText.appendChild(document.createTextNode("x"));
1273 measureText.appendChild(document.createTextNode("x"));
1247 }
1274 }
1248 removeChildrenAndAdd(display.measure, measureText);
1275 removeChildrenAndAdd(display.measure, measureText);
1249 var height = measureText.offsetHeight / 50;
1276 var height = measureText.offsetHeight / 50;
1250 if (height > 3) display.cachedTextHeight = height;
1277 if (height > 3) display.cachedTextHeight = height;
1251 removeChildren(display.measure);
1278 removeChildren(display.measure);
1252 return height || 1;
1279 return height || 1;
1253 }
1280 }
1254
1281
1255 function charWidth(display) {
1282 function charWidth(display) {
1256 if (display.cachedCharWidth != null) return display.cachedCharWidth;
1283 if (display.cachedCharWidth != null) return display.cachedCharWidth;
1257 var anchor = elt("span", "x");
1284 var anchor = elt("span", "x");
1258 var pre = elt("pre", [anchor]);
1285 var pre = elt("pre", [anchor]);
1259 removeChildrenAndAdd(display.measure, pre);
1286 removeChildrenAndAdd(display.measure, pre);
1260 var width = anchor.offsetWidth;
1287 var width = anchor.offsetWidth;
1261 if (width > 2) display.cachedCharWidth = width;
1288 if (width > 2) display.cachedCharWidth = width;
1262 return width || 10;
1289 return width || 10;
1263 }
1290 }
1264
1291
1265 // OPERATIONS
1292 // OPERATIONS
1266
1293
1267 // Operations are used to wrap changes in such a way that each
1294 // Operations are used to wrap changes in such a way that each
1268 // change won't have to update the cursor and display (which would
1295 // change won't have to update the cursor and display (which would
1269 // be awkward, slow, and error-prone), but instead updates are
1296 // be awkward, slow, and error-prone), but instead updates are
1270 // batched and then all combined and executed at once.
1297 // batched and then all combined and executed at once.
1271
1298
1272 var nextOpId = 0;
1299 var nextOpId = 0;
1273 function startOperation(cm) {
1300 function startOperation(cm) {
1274 cm.curOp = {
1301 cm.curOp = {
1275 // An array of ranges of lines that have to be updated. See
1302 // An array of ranges of lines that have to be updated. See
1276 // updateDisplay.
1303 // updateDisplay.
1277 changes: [],
1304 changes: [],
1278 updateInput: null,
1305 updateInput: null,
1279 userSelChange: null,
1306 userSelChange: null,
1280 textChanged: null,
1307 textChanged: null,
1281 selectionChanged: false,
1308 selectionChanged: false,
1282 cursorActivity: false,
1309 cursorActivity: false,
1283 updateMaxLine: false,
1310 updateMaxLine: false,
1284 updateScrollPos: false,
1311 updateScrollPos: false,
1285 id: ++nextOpId
1312 id: ++nextOpId
1286 };
1313 };
1287 if (!delayedCallbackDepth++) delayedCallbacks = [];
1314 if (!delayedCallbackDepth++) delayedCallbacks = [];
1288 }
1315 }
1289
1316
1290 function endOperation(cm) {
1317 function endOperation(cm) {
1291 var op = cm.curOp, doc = cm.doc, display = cm.display;
1318 var op = cm.curOp, doc = cm.doc, display = cm.display;
1292 cm.curOp = null;
1319 cm.curOp = null;
1293
1320
1294 if (op.updateMaxLine) computeMaxLength(cm);
1321 if (op.updateMaxLine) computeMaxLength(cm);
1295 if (display.maxLineChanged && !cm.options.lineWrapping && display.maxLine) {
1322 if (display.maxLineChanged && !cm.options.lineWrapping && display.maxLine) {
1296 var width = measureLineWidth(cm, display.maxLine);
1323 var width = measureLineWidth(cm, display.maxLine);
1297 display.sizer.style.minWidth = Math.max(0, width + 3 + scrollerCutOff) + "px";
1324 display.sizer.style.minWidth = Math.max(0, width + 3 + scrollerCutOff) + "px";
1298 display.maxLineChanged = false;
1325 display.maxLineChanged = false;
1299 var maxScrollLeft = Math.max(0, display.sizer.offsetLeft + display.sizer.offsetWidth - display.scroller.clientWidth);
1326 var maxScrollLeft = Math.max(0, display.sizer.offsetLeft + display.sizer.offsetWidth - display.scroller.clientWidth);
1300 if (maxScrollLeft < doc.scrollLeft && !op.updateScrollPos)
1327 if (maxScrollLeft < doc.scrollLeft && !op.updateScrollPos)
1301 setScrollLeft(cm, Math.min(display.scroller.scrollLeft, maxScrollLeft), true);
1328 setScrollLeft(cm, Math.min(display.scroller.scrollLeft, maxScrollLeft), true);
1302 }
1329 }
1303 var newScrollPos, updated;
1330 var newScrollPos, updated;
1304 if (op.updateScrollPos) {
1331 if (op.updateScrollPos) {
1305 newScrollPos = op.updateScrollPos;
1332 newScrollPos = op.updateScrollPos;
1306 } else if (op.selectionChanged && display.scroller.clientHeight) { // don't rescroll if not visible
1333 } else if (op.selectionChanged && display.scroller.clientHeight) { // don't rescroll if not visible
1307 var coords = cursorCoords(cm, doc.sel.head);
1334 var coords = cursorCoords(cm, doc.sel.head);
1308 newScrollPos = calculateScrollPos(cm, coords.left, coords.top, coords.left, coords.bottom);
1335 newScrollPos = calculateScrollPos(cm, coords.left, coords.top, coords.left, coords.bottom);
1309 }
1336 }
1310 if (op.changes.length || newScrollPos && newScrollPos.scrollTop != null) {
1337 if (op.changes.length || newScrollPos && newScrollPos.scrollTop != null) {
1311 updated = updateDisplay(cm, op.changes, newScrollPos && newScrollPos.scrollTop);
1338 updated = updateDisplay(cm, op.changes, newScrollPos && newScrollPos.scrollTop);
1312 if (cm.display.scroller.offsetHeight) cm.doc.scrollTop = cm.display.scroller.scrollTop;
1339 if (cm.display.scroller.offsetHeight) cm.doc.scrollTop = cm.display.scroller.scrollTop;
1313 }
1340 }
1314 if (!updated && op.selectionChanged) updateSelection(cm);
1341 if (!updated && op.selectionChanged) updateSelection(cm);
1315 if (op.updateScrollPos) {
1342 if (op.updateScrollPos) {
1316 display.scroller.scrollTop = display.scrollbarV.scrollTop = doc.scrollTop = newScrollPos.scrollTop;
1343 display.scroller.scrollTop = display.scrollbarV.scrollTop = doc.scrollTop = newScrollPos.scrollTop;
1317 display.scroller.scrollLeft = display.scrollbarH.scrollLeft = doc.scrollLeft = newScrollPos.scrollLeft;
1344 display.scroller.scrollLeft = display.scrollbarH.scrollLeft = doc.scrollLeft = newScrollPos.scrollLeft;
1318 alignHorizontally(cm);
1345 alignHorizontally(cm);
1319 if (op.scrollToPos)
1346 if (op.scrollToPos)
1320 scrollPosIntoView(cm, clipPos(cm.doc, op.scrollToPos), op.scrollToPosMargin);
1347 scrollPosIntoView(cm, clipPos(cm.doc, op.scrollToPos), op.scrollToPosMargin);
1321 } else if (newScrollPos) {
1348 } else if (newScrollPos) {
1322 scrollCursorIntoView(cm);
1349 scrollCursorIntoView(cm);
1323 }
1350 }
1324 if (op.selectionChanged) restartBlink(cm);
1351 if (op.selectionChanged) restartBlink(cm);
1325
1352
1326 if (cm.state.focused && op.updateInput)
1353 if (cm.state.focused && op.updateInput)
1327 resetInput(cm, op.userSelChange);
1354 resetInput(cm, op.userSelChange);
1328
1355
1329 var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers;
1356 var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers;
1330 if (hidden) for (var i = 0; i < hidden.length; ++i)
1357 if (hidden) for (var i = 0; i < hidden.length; ++i)
1331 if (!hidden[i].lines.length) signal(hidden[i], "hide");
1358 if (!hidden[i].lines.length) signal(hidden[i], "hide");
1332 if (unhidden) for (var i = 0; i < unhidden.length; ++i)
1359 if (unhidden) for (var i = 0; i < unhidden.length; ++i)
1333 if (unhidden[i].lines.length) signal(unhidden[i], "unhide");
1360 if (unhidden[i].lines.length) signal(unhidden[i], "unhide");
1334
1361
1335 var delayed;
1362 var delayed;
1336 if (!--delayedCallbackDepth) {
1363 if (!--delayedCallbackDepth) {
1337 delayed = delayedCallbacks;
1364 delayed = delayedCallbacks;
1338 delayedCallbacks = null;
1365 delayedCallbacks = null;
1339 }
1366 }
1340 if (op.textChanged)
1367 if (op.textChanged)
1341 signal(cm, "change", cm, op.textChanged);
1368 signal(cm, "change", cm, op.textChanged);
1342 if (op.cursorActivity) signal(cm, "cursorActivity", cm);
1369 if (op.cursorActivity) signal(cm, "cursorActivity", cm);
1343 if (delayed) for (var i = 0; i < delayed.length; ++i) delayed[i]();
1370 if (delayed) for (var i = 0; i < delayed.length; ++i) delayed[i]();
1344 }
1371 }
1345
1372
1346 // Wraps a function in an operation. Returns the wrapped function.
1373 // Wraps a function in an operation. Returns the wrapped function.
1347 function operation(cm1, f) {
1374 function operation(cm1, f) {
1348 return function() {
1375 return function() {
1349 var cm = cm1 || this, withOp = !cm.curOp;
1376 var cm = cm1 || this, withOp = !cm.curOp;
1350 if (withOp) startOperation(cm);
1377 if (withOp) startOperation(cm);
1351 try { var result = f.apply(cm, arguments); }
1378 try { var result = f.apply(cm, arguments); }
1352 finally { if (withOp) endOperation(cm); }
1379 finally { if (withOp) endOperation(cm); }
1353 return result;
1380 return result;
1354 };
1381 };
1355 }
1382 }
1356 function docOperation(f) {
1383 function docOperation(f) {
1357 return function() {
1384 return function() {
1358 var withOp = this.cm && !this.cm.curOp, result;
1385 var withOp = this.cm && !this.cm.curOp, result;
1359 if (withOp) startOperation(this.cm);
1386 if (withOp) startOperation(this.cm);
1360 try { result = f.apply(this, arguments); }
1387 try { result = f.apply(this, arguments); }
1361 finally { if (withOp) endOperation(this.cm); }
1388 finally { if (withOp) endOperation(this.cm); }
1362 return result;
1389 return result;
1363 };
1390 };
1364 }
1391 }
1365 function runInOp(cm, f) {
1392 function runInOp(cm, f) {
1366 var withOp = !cm.curOp, result;
1393 var withOp = !cm.curOp, result;
1367 if (withOp) startOperation(cm);
1394 if (withOp) startOperation(cm);
1368 try { result = f(); }
1395 try { result = f(); }
1369 finally { if (withOp) endOperation(cm); }
1396 finally { if (withOp) endOperation(cm); }
1370 return result;
1397 return result;
1371 }
1398 }
1372
1399
1373 function regChange(cm, from, to, lendiff) {
1400 function regChange(cm, from, to, lendiff) {
1374 if (from == null) from = cm.doc.first;
1401 if (from == null) from = cm.doc.first;
1375 if (to == null) to = cm.doc.first + cm.doc.size;
1402 if (to == null) to = cm.doc.first + cm.doc.size;
1376 cm.curOp.changes.push({from: from, to: to, diff: lendiff});
1403 cm.curOp.changes.push({from: from, to: to, diff: lendiff});
1377 }
1404 }
1378
1405
1379 // INPUT HANDLING
1406 // INPUT HANDLING
1380
1407
1381 function slowPoll(cm) {
1408 function slowPoll(cm) {
1382 if (cm.display.pollingFast) return;
1409 if (cm.display.pollingFast) return;
1383 cm.display.poll.set(cm.options.pollInterval, function() {
1410 cm.display.poll.set(cm.options.pollInterval, function() {
1384 readInput(cm);
1411 readInput(cm);
1385 if (cm.state.focused) slowPoll(cm);
1412 if (cm.state.focused) slowPoll(cm);
1386 });
1413 });
1387 }
1414 }
1388
1415
1389 function fastPoll(cm) {
1416 function fastPoll(cm) {
1390 var missed = false;
1417 var missed = false;
1391 cm.display.pollingFast = true;
1418 cm.display.pollingFast = true;
1392 function p() {
1419 function p() {
1393 var changed = readInput(cm);
1420 var changed = readInput(cm);
1394 if (!changed && !missed) {missed = true; cm.display.poll.set(60, p);}
1421 if (!changed && !missed) {missed = true; cm.display.poll.set(60, p);}
1395 else {cm.display.pollingFast = false; slowPoll(cm);}
1422 else {cm.display.pollingFast = false; slowPoll(cm);}
1396 }
1423 }
1397 cm.display.poll.set(20, p);
1424 cm.display.poll.set(20, p);
1398 }
1425 }
1399
1426
1400 // prevInput is a hack to work with IME. If we reset the textarea
1427 // prevInput is a hack to work with IME. If we reset the textarea
1401 // on every change, that breaks IME. So we look for changes
1428 // on every change, that breaks IME. So we look for changes
1402 // compared to the previous content instead. (Modern browsers have
1429 // compared to the previous content instead. (Modern browsers have
1403 // events that indicate IME taking place, but these are not widely
1430 // events that indicate IME taking place, but these are not widely
1404 // supported or compatible enough yet to rely on.)
1431 // supported or compatible enough yet to rely on.)
1405 function readInput(cm) {
1432 function readInput(cm) {
1406 var input = cm.display.input, prevInput = cm.display.prevInput, doc = cm.doc, sel = doc.sel;
1433 var input = cm.display.input, prevInput = cm.display.prevInput, doc = cm.doc, sel = doc.sel;
1407 if (!cm.state.focused || hasSelection(input) || isReadOnly(cm)) return false;
1434 if (!cm.state.focused || hasSelection(input) || isReadOnly(cm) || cm.state.disableInput) return false;
1408 var text = input.value;
1435 var text = input.value;
1409 if (text == prevInput && posEq(sel.from, sel.to)) return false;
1436 if (text == prevInput && posEq(sel.from, sel.to)) return false;
1410 if (ie && !ie_lt9 && cm.display.inputHasSelection === text) {
1437 if (ie && !ie_lt9 && cm.display.inputHasSelection === text) {
1411 resetInput(cm, true);
1438 resetInput(cm, true);
1412 return false;
1439 return false;
1413 }
1440 }
1414
1441
1415 var withOp = !cm.curOp;
1442 var withOp = !cm.curOp;
1416 if (withOp) startOperation(cm);
1443 if (withOp) startOperation(cm);
1417 sel.shift = false;
1444 sel.shift = false;
1418 var same = 0, l = Math.min(prevInput.length, text.length);
1445 var same = 0, l = Math.min(prevInput.length, text.length);
1419 while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) ++same;
1446 while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) ++same;
1420 var from = sel.from, to = sel.to;
1447 var from = sel.from, to = sel.to;
1421 if (same < prevInput.length)
1448 if (same < prevInput.length)
1422 from = Pos(from.line, from.ch - (prevInput.length - same));
1449 from = Pos(from.line, from.ch - (prevInput.length - same));
1423 else if (cm.state.overwrite && posEq(from, to) && !cm.state.pasteIncoming)
1450 else if (cm.state.overwrite && posEq(from, to) && !cm.state.pasteIncoming)
1424 to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + (text.length - same)));
1451 to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + (text.length - same)));
1452
1425 var updateInput = cm.curOp.updateInput;
1453 var updateInput = cm.curOp.updateInput;
1426 makeChange(cm.doc, {from: from, to: to, text: splitLines(text.slice(same)),
1454 var changeEvent = {from: from, to: to, text: splitLines(text.slice(same)),
1427 origin: cm.state.pasteIncoming ? "paste" : "+input"}, "end");
1455 origin: cm.state.pasteIncoming ? "paste" : "+input"};
1428
1456 makeChange(cm.doc, changeEvent, "end");
1429 cm.curOp.updateInput = updateInput;
1457 cm.curOp.updateInput = updateInput;
1458 signalLater(cm, "inputRead", cm, changeEvent);
1459
1430 if (text.length > 1000 || text.indexOf("\n") > -1) input.value = cm.display.prevInput = "";
1460 if (text.length > 1000 || text.indexOf("\n") > -1) input.value = cm.display.prevInput = "";
1431 else cm.display.prevInput = text;
1461 else cm.display.prevInput = text;
1432 if (withOp) endOperation(cm);
1462 if (withOp) endOperation(cm);
1433 cm.state.pasteIncoming = false;
1463 cm.state.pasteIncoming = false;
1434 return true;
1464 return true;
1435 }
1465 }
1436
1466
1437 function resetInput(cm, user) {
1467 function resetInput(cm, user) {
1438 var minimal, selected, doc = cm.doc;
1468 var minimal, selected, doc = cm.doc;
1439 if (!posEq(doc.sel.from, doc.sel.to)) {
1469 if (!posEq(doc.sel.from, doc.sel.to)) {
1440 cm.display.prevInput = "";
1470 cm.display.prevInput = "";
1441 minimal = hasCopyEvent &&
1471 minimal = hasCopyEvent &&
1442 (doc.sel.to.line - doc.sel.from.line > 100 || (selected = cm.getSelection()).length > 1000);
1472 (doc.sel.to.line - doc.sel.from.line > 100 || (selected = cm.getSelection()).length > 1000);
1443 var content = minimal ? "-" : selected || cm.getSelection();
1473 var content = minimal ? "-" : selected || cm.getSelection();
1444 cm.display.input.value = content;
1474 cm.display.input.value = content;
1445 if (cm.state.focused) selectInput(cm.display.input);
1475 if (cm.state.focused) selectInput(cm.display.input);
1446 if (ie && !ie_lt9) cm.display.inputHasSelection = content;
1476 if (ie && !ie_lt9) cm.display.inputHasSelection = content;
1447 } else if (user) {
1477 } else if (user) {
1448 cm.display.prevInput = cm.display.input.value = "";
1478 cm.display.prevInput = cm.display.input.value = "";
1449 if (ie && !ie_lt9) cm.display.inputHasSelection = null;
1479 if (ie && !ie_lt9) cm.display.inputHasSelection = null;
1450 }
1480 }
1451 cm.display.inaccurateSelection = minimal;
1481 cm.display.inaccurateSelection = minimal;
1452 }
1482 }
1453
1483
1454 function focusInput(cm) {
1484 function focusInput(cm) {
1455 if (cm.options.readOnly != "nocursor" && (!mobile || document.activeElement != cm.display.input))
1485 if (cm.options.readOnly != "nocursor" && (!mobile || document.activeElement != cm.display.input))
1456 cm.display.input.focus();
1486 cm.display.input.focus();
1457 }
1487 }
1458
1488
1459 function isReadOnly(cm) {
1489 function isReadOnly(cm) {
1460 return cm.options.readOnly || cm.doc.cantEdit;
1490 return cm.options.readOnly || cm.doc.cantEdit;
1461 }
1491 }
1462
1492
1463 // EVENT HANDLERS
1493 // EVENT HANDLERS
1464
1494
1465 function registerEventHandlers(cm) {
1495 function registerEventHandlers(cm) {
1466 var d = cm.display;
1496 var d = cm.display;
1467 on(d.scroller, "mousedown", operation(cm, onMouseDown));
1497 on(d.scroller, "mousedown", operation(cm, onMouseDown));
1468 if (ie)
1498 if (ie)
1469 on(d.scroller, "dblclick", operation(cm, function(e) {
1499 on(d.scroller, "dblclick", operation(cm, function(e) {
1500 if (signalDOMEvent(cm, e)) return;
1470 var pos = posFromMouse(cm, e);
1501 var pos = posFromMouse(cm, e);
1471 if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) return;
1502 if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) return;
1472 e_preventDefault(e);
1503 e_preventDefault(e);
1473 var word = findWordAt(getLine(cm.doc, pos.line).text, pos);
1504 var word = findWordAt(getLine(cm.doc, pos.line).text, pos);
1474 extendSelection(cm.doc, word.from, word.to);
1505 extendSelection(cm.doc, word.from, word.to);
1475 }));
1506 }));
1476 else
1507 else
1477 on(d.scroller, "dblclick", e_preventDefault);
1508 on(d.scroller, "dblclick", function(e) { signalDOMEvent(cm, e) || e_preventDefault(e); });
1478 on(d.lineSpace, "selectstart", function(e) {
1509 on(d.lineSpace, "selectstart", function(e) {
1479 if (!eventInWidget(d, e)) e_preventDefault(e);
1510 if (!eventInWidget(d, e)) e_preventDefault(e);
1480 });
1511 });
1481 // Gecko browsers fire contextmenu *after* opening the menu, at
1512 // Gecko browsers fire contextmenu *after* opening the menu, at
1482 // which point we can't mess with it anymore. Context menu is
1513 // which point we can't mess with it anymore. Context menu is
1483 // handled in onMouseDown for Gecko.
1514 // handled in onMouseDown for Gecko.
1484 if (!captureMiddleClick) on(d.scroller, "contextmenu", function(e) {onContextMenu(cm, e);});
1515 if (!captureMiddleClick) on(d.scroller, "contextmenu", function(e) {onContextMenu(cm, e);});
1485
1516
1486 on(d.scroller, "scroll", function() {
1517 on(d.scroller, "scroll", function() {
1487 if (d.scroller.clientHeight) {
1518 if (d.scroller.clientHeight) {
1488 setScrollTop(cm, d.scroller.scrollTop);
1519 setScrollTop(cm, d.scroller.scrollTop);
1489 setScrollLeft(cm, d.scroller.scrollLeft, true);
1520 setScrollLeft(cm, d.scroller.scrollLeft, true);
1490 signal(cm, "scroll", cm);
1521 signal(cm, "scroll", cm);
1491 }
1522 }
1492 });
1523 });
1493 on(d.scrollbarV, "scroll", function() {
1524 on(d.scrollbarV, "scroll", function() {
1494 if (d.scroller.clientHeight) setScrollTop(cm, d.scrollbarV.scrollTop);
1525 if (d.scroller.clientHeight) setScrollTop(cm, d.scrollbarV.scrollTop);
1495 });
1526 });
1496 on(d.scrollbarH, "scroll", function() {
1527 on(d.scrollbarH, "scroll", function() {
1497 if (d.scroller.clientHeight) setScrollLeft(cm, d.scrollbarH.scrollLeft);
1528 if (d.scroller.clientHeight) setScrollLeft(cm, d.scrollbarH.scrollLeft);
1498 });
1529 });
1499
1530
1500 on(d.scroller, "mousewheel", function(e){onScrollWheel(cm, e);});
1531 on(d.scroller, "mousewheel", function(e){onScrollWheel(cm, e);});
1501 on(d.scroller, "DOMMouseScroll", function(e){onScrollWheel(cm, e);});
1532 on(d.scroller, "DOMMouseScroll", function(e){onScrollWheel(cm, e);});
1502
1533
1503 function reFocus() { if (cm.state.focused) setTimeout(bind(focusInput, cm), 0); }
1534 function reFocus() { if (cm.state.focused) setTimeout(bind(focusInput, cm), 0); }
1504 on(d.scrollbarH, "mousedown", reFocus);
1535 on(d.scrollbarH, "mousedown", reFocus);
1505 on(d.scrollbarV, "mousedown", reFocus);
1536 on(d.scrollbarV, "mousedown", reFocus);
1506 // Prevent wrapper from ever scrolling
1537 // Prevent wrapper from ever scrolling
1507 on(d.wrapper, "scroll", function() { d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; });
1538 on(d.wrapper, "scroll", function() { d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; });
1508
1539
1540 var resizeTimer;
1509 function onResize() {
1541 function onResize() {
1510 // Might be a text scaling operation, clear size caches.
1542 if (resizeTimer == null) resizeTimer = setTimeout(function() {
1511 d.cachedCharWidth = d.cachedTextHeight = null;
1543 resizeTimer = null;
1512 clearCaches(cm);
1544 // Might be a text scaling operation, clear size caches.
1513 runInOp(cm, bind(regChange, cm));
1545 d.cachedCharWidth = d.cachedTextHeight = knownScrollbarWidth = null;
1546 clearCaches(cm);
1547 runInOp(cm, bind(regChange, cm));
1548 }, 100);
1514 }
1549 }
1515 on(window, "resize", onResize);
1550 on(window, "resize", onResize);
1516 // Above handler holds on to the editor and its data structures.
1551 // Above handler holds on to the editor and its data structures.
1517 // Here we poll to unregister it when the editor is no longer in
1552 // Here we poll to unregister it when the editor is no longer in
1518 // the document, so that it can be garbage-collected.
1553 // the document, so that it can be garbage-collected.
1519 function unregister() {
1554 function unregister() {
1520 for (var p = d.wrapper.parentNode; p && p != document.body; p = p.parentNode) {}
1555 for (var p = d.wrapper.parentNode; p && p != document.body; p = p.parentNode) {}
1521 if (p) setTimeout(unregister, 5000);
1556 if (p) setTimeout(unregister, 5000);
1522 else off(window, "resize", onResize);
1557 else off(window, "resize", onResize);
1523 }
1558 }
1524 setTimeout(unregister, 5000);
1559 setTimeout(unregister, 5000);
1525
1560
1526 on(d.input, "keyup", operation(cm, function(e) {
1561 on(d.input, "keyup", operation(cm, function(e) {
1527 if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
1562 if (signalDOMEvent(cm, e) || cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
1528 if (e.keyCode == 16) cm.doc.sel.shift = false;
1563 if (e.keyCode == 16) cm.doc.sel.shift = false;
1529 }));
1564 }));
1530 on(d.input, "input", bind(fastPoll, cm));
1565 on(d.input, "input", bind(fastPoll, cm));
1531 on(d.input, "keydown", operation(cm, onKeyDown));
1566 on(d.input, "keydown", operation(cm, onKeyDown));
1532 on(d.input, "keypress", operation(cm, onKeyPress));
1567 on(d.input, "keypress", operation(cm, onKeyPress));
1533 on(d.input, "focus", bind(onFocus, cm));
1568 on(d.input, "focus", bind(onFocus, cm));
1534 on(d.input, "blur", bind(onBlur, cm));
1569 on(d.input, "blur", bind(onBlur, cm));
1535
1570
1536 function drag_(e) {
1571 function drag_(e) {
1537 if (cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e))) return;
1572 if (signalDOMEvent(cm, e) || cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e))) return;
1538 e_stop(e);
1573 e_stop(e);
1539 }
1574 }
1540 if (cm.options.dragDrop) {
1575 if (cm.options.dragDrop) {
1541 on(d.scroller, "dragstart", function(e){onDragStart(cm, e);});
1576 on(d.scroller, "dragstart", function(e){onDragStart(cm, e);});
1542 on(d.scroller, "dragenter", drag_);
1577 on(d.scroller, "dragenter", drag_);
1543 on(d.scroller, "dragover", drag_);
1578 on(d.scroller, "dragover", drag_);
1544 on(d.scroller, "drop", operation(cm, onDrop));
1579 on(d.scroller, "drop", operation(cm, onDrop));
1545 }
1580 }
1546 on(d.scroller, "paste", function(e){
1581 on(d.scroller, "paste", function(e){
1547 if (eventInWidget(d, e)) return;
1582 if (eventInWidget(d, e)) return;
1548 focusInput(cm);
1583 focusInput(cm);
1549 fastPoll(cm);
1584 fastPoll(cm);
1550 });
1585 });
1551 on(d.input, "paste", function() {
1586 on(d.input, "paste", function() {
1552 cm.state.pasteIncoming = true;
1587 cm.state.pasteIncoming = true;
1553 fastPoll(cm);
1588 fastPoll(cm);
1554 });
1589 });
1555
1590
1556 function prepareCopy() {
1591 function prepareCopy() {
1557 if (d.inaccurateSelection) {
1592 if (d.inaccurateSelection) {
1558 d.prevInput = "";
1593 d.prevInput = "";
1559 d.inaccurateSelection = false;
1594 d.inaccurateSelection = false;
1560 d.input.value = cm.getSelection();
1595 d.input.value = cm.getSelection();
1561 selectInput(d.input);
1596 selectInput(d.input);
1562 }
1597 }
1563 }
1598 }
1564 on(d.input, "cut", prepareCopy);
1599 on(d.input, "cut", prepareCopy);
1565 on(d.input, "copy", prepareCopy);
1600 on(d.input, "copy", prepareCopy);
1566
1601
1567 // Needed to handle Tab key in KHTML
1602 // Needed to handle Tab key in KHTML
1568 if (khtml) on(d.sizer, "mouseup", function() {
1603 if (khtml) on(d.sizer, "mouseup", function() {
1569 if (document.activeElement == d.input) d.input.blur();
1604 if (document.activeElement == d.input) d.input.blur();
1570 focusInput(cm);
1605 focusInput(cm);
1571 });
1606 });
1572 }
1607 }
1573
1608
1574 function eventInWidget(display, e) {
1609 function eventInWidget(display, e) {
1575 for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {
1610 for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {
1576 if (!n) return true;
1611 if (!n || n.ignoreEvents || n.parentNode == display.sizer && n != display.mover) return true;
1577 if (/\bCodeMirror-(?:line)?widget\b/.test(n.className) ||
1578 n.parentNode == display.sizer && n != display.mover) return true;
1579 }
1612 }
1580 }
1613 }
1581
1614
1582 function posFromMouse(cm, e, liberal) {
1615 function posFromMouse(cm, e, liberal) {
1583 var display = cm.display;
1616 var display = cm.display;
1584 if (!liberal) {
1617 if (!liberal) {
1585 var target = e_target(e);
1618 var target = e_target(e);
1586 if (target == display.scrollbarH || target == display.scrollbarH.firstChild ||
1619 if (target == display.scrollbarH || target == display.scrollbarH.firstChild ||
1587 target == display.scrollbarV || target == display.scrollbarV.firstChild ||
1620 target == display.scrollbarV || target == display.scrollbarV.firstChild ||
1588 target == display.scrollbarFiller) return null;
1621 target == display.scrollbarFiller || target == display.gutterFiller) return null;
1589 }
1622 }
1590 var x, y, space = getRect(display.lineSpace);
1623 var x, y, space = getRect(display.lineSpace);
1591 // Fails unpredictably on IE[67] when mouse is dragged around quickly.
1624 // Fails unpredictably on IE[67] when mouse is dragged around quickly.
1592 try { x = e.clientX; y = e.clientY; } catch (e) { return null; }
1625 try { x = e.clientX; y = e.clientY; } catch (e) { return null; }
1593 return coordsChar(cm, x - space.left, y - space.top);
1626 return coordsChar(cm, x - space.left, y - space.top);
1594 }
1627 }
1595
1628
1596 var lastClick, lastDoubleClick;
1629 var lastClick, lastDoubleClick;
1597 function onMouseDown(e) {
1630 function onMouseDown(e) {
1631 if (signalDOMEvent(this, e)) return;
1598 var cm = this, display = cm.display, doc = cm.doc, sel = doc.sel;
1632 var cm = this, display = cm.display, doc = cm.doc, sel = doc.sel;
1599 sel.shift = e.shiftKey;
1633 sel.shift = e.shiftKey;
1600
1634
1601 if (eventInWidget(display, e)) {
1635 if (eventInWidget(display, e)) {
1602 if (!webkit) {
1636 if (!webkit) {
1603 display.scroller.draggable = false;
1637 display.scroller.draggable = false;
1604 setTimeout(function(){display.scroller.draggable = true;}, 100);
1638 setTimeout(function(){display.scroller.draggable = true;}, 100);
1605 }
1639 }
1606 return;
1640 return;
1607 }
1641 }
1608 if (clickInGutter(cm, e)) return;
1642 if (clickInGutter(cm, e)) return;
1609 var start = posFromMouse(cm, e);
1643 var start = posFromMouse(cm, e);
1610
1644
1611 switch (e_button(e)) {
1645 switch (e_button(e)) {
1612 case 3:
1646 case 3:
1613 if (captureMiddleClick) onContextMenu.call(cm, cm, e);
1647 if (captureMiddleClick) onContextMenu.call(cm, cm, e);
1614 return;
1648 return;
1615 case 2:
1649 case 2:
1616 if (start) extendSelection(cm.doc, start);
1650 if (start) extendSelection(cm.doc, start);
1617 setTimeout(bind(focusInput, cm), 20);
1651 setTimeout(bind(focusInput, cm), 20);
1618 e_preventDefault(e);
1652 e_preventDefault(e);
1619 return;
1653 return;
1620 }
1654 }
1621 // For button 1, if it was clicked inside the editor
1655 // For button 1, if it was clicked inside the editor
1622 // (posFromMouse returning non-null), we have to adjust the
1656 // (posFromMouse returning non-null), we have to adjust the
1623 // selection.
1657 // selection.
1624 if (!start) {if (e_target(e) == display.scroller) e_preventDefault(e); return;}
1658 if (!start) {if (e_target(e) == display.scroller) e_preventDefault(e); return;}
1625
1659
1626 if (!cm.state.focused) onFocus(cm);
1660 if (!cm.state.focused) onFocus(cm);
1627
1661
1628 var now = +new Date, type = "single";
1662 var now = +new Date, type = "single";
1629 if (lastDoubleClick && lastDoubleClick.time > now - 400 && posEq(lastDoubleClick.pos, start)) {
1663 if (lastDoubleClick && lastDoubleClick.time > now - 400 && posEq(lastDoubleClick.pos, start)) {
1630 type = "triple";
1664 type = "triple";
1631 e_preventDefault(e);
1665 e_preventDefault(e);
1632 setTimeout(bind(focusInput, cm), 20);
1666 setTimeout(bind(focusInput, cm), 20);
1633 selectLine(cm, start.line);
1667 selectLine(cm, start.line);
1634 } else if (lastClick && lastClick.time > now - 400 && posEq(lastClick.pos, start)) {
1668 } else if (lastClick && lastClick.time > now - 400 && posEq(lastClick.pos, start)) {
1635 type = "double";
1669 type = "double";
1636 lastDoubleClick = {time: now, pos: start};
1670 lastDoubleClick = {time: now, pos: start};
1637 e_preventDefault(e);
1671 e_preventDefault(e);
1638 var word = findWordAt(getLine(doc, start.line).text, start);
1672 var word = findWordAt(getLine(doc, start.line).text, start);
1639 extendSelection(cm.doc, word.from, word.to);
1673 extendSelection(cm.doc, word.from, word.to);
1640 } else { lastClick = {time: now, pos: start}; }
1674 } else { lastClick = {time: now, pos: start}; }
1641
1675
1642 var last = start;
1676 var last = start;
1643 if (cm.options.dragDrop && dragAndDrop && !isReadOnly(cm) && !posEq(sel.from, sel.to) &&
1677 if (cm.options.dragDrop && dragAndDrop && !isReadOnly(cm) && !posEq(sel.from, sel.to) &&
1644 !posLess(start, sel.from) && !posLess(sel.to, start) && type == "single") {
1678 !posLess(start, sel.from) && !posLess(sel.to, start) && type == "single") {
1645 var dragEnd = operation(cm, function(e2) {
1679 var dragEnd = operation(cm, function(e2) {
1646 if (webkit) display.scroller.draggable = false;
1680 if (webkit) display.scroller.draggable = false;
1647 cm.state.draggingText = false;
1681 cm.state.draggingText = false;
1648 off(document, "mouseup", dragEnd);
1682 off(document, "mouseup", dragEnd);
1649 off(display.scroller, "drop", dragEnd);
1683 off(display.scroller, "drop", dragEnd);
1650 if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) {
1684 if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) {
1651 e_preventDefault(e2);
1685 e_preventDefault(e2);
1652 extendSelection(cm.doc, start);
1686 extendSelection(cm.doc, start);
1653 focusInput(cm);
1687 focusInput(cm);
1654 }
1688 }
1655 });
1689 });
1656 // Let the drag handler handle this.
1690 // Let the drag handler handle this.
1657 if (webkit) display.scroller.draggable = true;
1691 if (webkit) display.scroller.draggable = true;
1658 cm.state.draggingText = dragEnd;
1692 cm.state.draggingText = dragEnd;
1659 // IE's approach to draggable
1693 // IE's approach to draggable
1660 if (display.scroller.dragDrop) display.scroller.dragDrop();
1694 if (display.scroller.dragDrop) display.scroller.dragDrop();
1661 on(document, "mouseup", dragEnd);
1695 on(document, "mouseup", dragEnd);
1662 on(display.scroller, "drop", dragEnd);
1696 on(display.scroller, "drop", dragEnd);
1663 return;
1697 return;
1664 }
1698 }
1665 e_preventDefault(e);
1699 e_preventDefault(e);
1666 if (type == "single") extendSelection(cm.doc, clipPos(doc, start));
1700 if (type == "single") extendSelection(cm.doc, clipPos(doc, start));
1667
1701
1668 var startstart = sel.from, startend = sel.to, lastPos = start;
1702 var startstart = sel.from, startend = sel.to, lastPos = start;
1669
1703
1670 function doSelect(cur) {
1704 function doSelect(cur) {
1671 if (posEq(lastPos, cur)) return;
1705 if (posEq(lastPos, cur)) return;
1672 lastPos = cur;
1706 lastPos = cur;
1673
1707
1674 if (type == "single") {
1708 if (type == "single") {
1675 extendSelection(cm.doc, clipPos(doc, start), cur);
1709 extendSelection(cm.doc, clipPos(doc, start), cur);
1676 return;
1710 return;
1677 }
1711 }
1678
1712
1679 startstart = clipPos(doc, startstart);
1713 startstart = clipPos(doc, startstart);
1680 startend = clipPos(doc, startend);
1714 startend = clipPos(doc, startend);
1681 if (type == "double") {
1715 if (type == "double") {
1682 var word = findWordAt(getLine(doc, cur.line).text, cur);
1716 var word = findWordAt(getLine(doc, cur.line).text, cur);
1683 if (posLess(cur, startstart)) extendSelection(cm.doc, word.from, startend);
1717 if (posLess(cur, startstart)) extendSelection(cm.doc, word.from, startend);
1684 else extendSelection(cm.doc, startstart, word.to);
1718 else extendSelection(cm.doc, startstart, word.to);
1685 } else if (type == "triple") {
1719 } else if (type == "triple") {
1686 if (posLess(cur, startstart)) extendSelection(cm.doc, startend, clipPos(doc, Pos(cur.line, 0)));
1720 if (posLess(cur, startstart)) extendSelection(cm.doc, startend, clipPos(doc, Pos(cur.line, 0)));
1687 else extendSelection(cm.doc, startstart, clipPos(doc, Pos(cur.line + 1, 0)));
1721 else extendSelection(cm.doc, startstart, clipPos(doc, Pos(cur.line + 1, 0)));
1688 }
1722 }
1689 }
1723 }
1690
1724
1691 var editorSize = getRect(display.wrapper);
1725 var editorSize = getRect(display.wrapper);
1692 // Used to ensure timeout re-tries don't fire when another extend
1726 // Used to ensure timeout re-tries don't fire when another extend
1693 // happened in the meantime (clearTimeout isn't reliable -- at
1727 // happened in the meantime (clearTimeout isn't reliable -- at
1694 // least on Chrome, the timeouts still happen even when cleared,
1728 // least on Chrome, the timeouts still happen even when cleared,
1695 // if the clear happens after their scheduled firing time).
1729 // if the clear happens after their scheduled firing time).
1696 var counter = 0;
1730 var counter = 0;
1697
1731
1698 function extend(e) {
1732 function extend(e) {
1699 var curCount = ++counter;
1733 var curCount = ++counter;
1700 var cur = posFromMouse(cm, e, true);
1734 var cur = posFromMouse(cm, e, true);
1701 if (!cur) return;
1735 if (!cur) return;
1702 if (!posEq(cur, last)) {
1736 if (!posEq(cur, last)) {
1703 if (!cm.state.focused) onFocus(cm);
1737 if (!cm.state.focused) onFocus(cm);
1704 last = cur;
1738 last = cur;
1705 doSelect(cur);
1739 doSelect(cur);
1706 var visible = visibleLines(display, doc);
1740 var visible = visibleLines(display, doc);
1707 if (cur.line >= visible.to || cur.line < visible.from)
1741 if (cur.line >= visible.to || cur.line < visible.from)
1708 setTimeout(operation(cm, function(){if (counter == curCount) extend(e);}), 150);
1742 setTimeout(operation(cm, function(){if (counter == curCount) extend(e);}), 150);
1709 } else {
1743 } else {
1710 var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0;
1744 var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0;
1711 if (outside) setTimeout(operation(cm, function() {
1745 if (outside) setTimeout(operation(cm, function() {
1712 if (counter != curCount) return;
1746 if (counter != curCount) return;
1713 display.scroller.scrollTop += outside;
1747 display.scroller.scrollTop += outside;
1714 extend(e);
1748 extend(e);
1715 }), 50);
1749 }), 50);
1716 }
1750 }
1717 }
1751 }
1718
1752
1719 function done(e) {
1753 function done(e) {
1720 counter = Infinity;
1754 counter = Infinity;
1721 var cur = posFromMouse(cm, e);
1722 if (cur) doSelect(cur);
1723 e_preventDefault(e);
1755 e_preventDefault(e);
1724 focusInput(cm);
1756 focusInput(cm);
1725 off(document, "mousemove", move);
1757 off(document, "mousemove", move);
1726 off(document, "mouseup", up);
1758 off(document, "mouseup", up);
1727 }
1759 }
1728
1760
1729 var move = operation(cm, function(e) {
1761 var move = operation(cm, function(e) {
1730 if (!ie && !e_button(e)) done(e);
1762 if (!ie && !e_button(e)) done(e);
1731 else extend(e);
1763 else extend(e);
1732 });
1764 });
1733 var up = operation(cm, done);
1765 var up = operation(cm, done);
1734 on(document, "mousemove", move);
1766 on(document, "mousemove", move);
1735 on(document, "mouseup", up);
1767 on(document, "mouseup", up);
1736 }
1768 }
1737
1769
1770 function clickInGutter(cm, e) {
1771 var display = cm.display;
1772 try { var mX = e.clientX, mY = e.clientY; }
1773 catch(e) { return false; }
1774
1775 if (mX >= Math.floor(getRect(display.gutters).right)) return false;
1776 e_preventDefault(e);
1777 if (!hasHandler(cm, "gutterClick")) return true;
1778
1779 var lineBox = getRect(display.lineDiv);
1780 if (mY > lineBox.bottom) return true;
1781 mY -= lineBox.top - display.viewOffset;
1782
1783 for (var i = 0; i < cm.options.gutters.length; ++i) {
1784 var g = display.gutters.childNodes[i];
1785 if (g && getRect(g).right >= mX) {
1786 var line = lineAtHeight(cm.doc, mY);
1787 var gutter = cm.options.gutters[i];
1788 signalLater(cm, "gutterClick", cm, line, gutter, e);
1789 break;
1790 }
1791 }
1792 return true;
1793 }
1794
1795 // Kludge to work around strange IE behavior where it'll sometimes
1796 // re-fire a series of drag-related events right after the drop (#1551)
1797 var lastDrop = 0;
1798
1738 function onDrop(e) {
1799 function onDrop(e) {
1739 var cm = this;
1800 var cm = this;
1740 if (eventInWidget(cm.display, e) || (cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e))))
1801 if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e) || (cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e))))
1741 return;
1802 return;
1742 e_preventDefault(e);
1803 e_preventDefault(e);
1804 if (ie) lastDrop = +new Date;
1743 var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files;
1805 var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files;
1744 if (!pos || isReadOnly(cm)) return;
1806 if (!pos || isReadOnly(cm)) return;
1745 if (files && files.length && window.FileReader && window.File) {
1807 if (files && files.length && window.FileReader && window.File) {
1746 var n = files.length, text = Array(n), read = 0;
1808 var n = files.length, text = Array(n), read = 0;
1747 var loadFile = function(file, i) {
1809 var loadFile = function(file, i) {
1748 var reader = new FileReader;
1810 var reader = new FileReader;
1749 reader.onload = function() {
1811 reader.onload = function() {
1750 text[i] = reader.result;
1812 text[i] = reader.result;
1751 if (++read == n) {
1813 if (++read == n) {
1752 pos = clipPos(cm.doc, pos);
1814 pos = clipPos(cm.doc, pos);
1753 makeChange(cm.doc, {from: pos, to: pos, text: splitLines(text.join("\n")), origin: "paste"}, "around");
1815 makeChange(cm.doc, {from: pos, to: pos, text: splitLines(text.join("\n")), origin: "paste"}, "around");
1754 }
1816 }
1755 };
1817 };
1756 reader.readAsText(file);
1818 reader.readAsText(file);
1757 };
1819 };
1758 for (var i = 0; i < n; ++i) loadFile(files[i], i);
1820 for (var i = 0; i < n; ++i) loadFile(files[i], i);
1759 } else {
1821 } else {
1760 // Don't do a replace if the drop happened inside of the selected text.
1822 // Don't do a replace if the drop happened inside of the selected text.
1761 if (cm.state.draggingText && !(posLess(pos, cm.doc.sel.from) || posLess(cm.doc.sel.to, pos))) {
1823 if (cm.state.draggingText && !(posLess(pos, cm.doc.sel.from) || posLess(cm.doc.sel.to, pos))) {
1762 cm.state.draggingText(e);
1824 cm.state.draggingText(e);
1763 // Ensure the editor is re-focused
1825 // Ensure the editor is re-focused
1764 setTimeout(bind(focusInput, cm), 20);
1826 setTimeout(bind(focusInput, cm), 20);
1765 return;
1827 return;
1766 }
1828 }
1767 try {
1829 try {
1768 var text = e.dataTransfer.getData("Text");
1830 var text = e.dataTransfer.getData("Text");
1769 if (text) {
1831 if (text) {
1770 var curFrom = cm.doc.sel.from, curTo = cm.doc.sel.to;
1832 var curFrom = cm.doc.sel.from, curTo = cm.doc.sel.to;
1771 setSelection(cm.doc, pos, pos);
1833 setSelection(cm.doc, pos, pos);
1772 if (cm.state.draggingText) replaceRange(cm.doc, "", curFrom, curTo, "paste");
1834 if (cm.state.draggingText) replaceRange(cm.doc, "", curFrom, curTo, "paste");
1773 cm.replaceSelection(text, null, "paste");
1835 cm.replaceSelection(text, null, "paste");
1774 focusInput(cm);
1836 focusInput(cm);
1775 onFocus(cm);
1837 onFocus(cm);
1776 }
1838 }
1777 }
1839 }
1778 catch(e){}
1840 catch(e){}
1779 }
1841 }
1780 }
1842 }
1781
1843
1782 function clickInGutter(cm, e) {
1783 var display = cm.display;
1784 try { var mX = e.clientX, mY = e.clientY; }
1785 catch(e) { return false; }
1786
1787 if (mX >= Math.floor(getRect(display.gutters).right)) return false;
1788 e_preventDefault(e);
1789 if (!hasHandler(cm, "gutterClick")) return true;
1790
1791 var lineBox = getRect(display.lineDiv);
1792 if (mY > lineBox.bottom) return true;
1793 mY -= lineBox.top - display.viewOffset;
1794
1795 for (var i = 0; i < cm.options.gutters.length; ++i) {
1796 var g = display.gutters.childNodes[i];
1797 if (g && getRect(g).right >= mX) {
1798 var line = lineAtHeight(cm.doc, mY);
1799 var gutter = cm.options.gutters[i];
1800 signalLater(cm, "gutterClick", cm, line, gutter, e);
1801 break;
1802 }
1803 }
1804 return true;
1805 }
1806
1807 function onDragStart(cm, e) {
1844 function onDragStart(cm, e) {
1808 if (ie && !cm.state.draggingText) { e_stop(e); return; }
1845 if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return; }
1809 if (eventInWidget(cm.display, e)) return;
1846 if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) return;
1810
1847
1811 var txt = cm.getSelection();
1848 var txt = cm.getSelection();
1812 e.dataTransfer.setData("Text", txt);
1849 e.dataTransfer.setData("Text", txt);
1813
1850
1814 // Use dummy image instead of default browsers image.
1851 // Use dummy image instead of default browsers image.
1815 // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
1852 // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
1816 if (e.dataTransfer.setDragImage) {
1853 if (e.dataTransfer.setDragImage && !safari) {
1817 var img = elt("img", null, null, "position: fixed; left: 0; top: 0;");
1854 var img = elt("img", null, null, "position: fixed; left: 0; top: 0;");
1818 if (opera) {
1855 if (opera) {
1819 img.width = img.height = 1;
1856 img.width = img.height = 1;
1820 cm.display.wrapper.appendChild(img);
1857 cm.display.wrapper.appendChild(img);
1821 // Force a relayout, or Opera won't use our image for some obscure reason
1858 // Force a relayout, or Opera won't use our image for some obscure reason
1822 img._top = img.offsetTop;
1859 img._top = img.offsetTop;
1823 }
1860 }
1824 if (safari) {
1825 if (cm.display.dragImg) {
1826 img = cm.display.dragImg;
1827 } else {
1828 cm.display.dragImg = img;
1829 img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
1830 cm.display.wrapper.appendChild(img);
1831 }
1832 }
1833 e.dataTransfer.setDragImage(img, 0, 0);
1861 e.dataTransfer.setDragImage(img, 0, 0);
1834 if (opera) img.parentNode.removeChild(img);
1862 if (opera) img.parentNode.removeChild(img);
1835 }
1863 }
1836 }
1864 }
1837
1865
1838 function setScrollTop(cm, val) {
1866 function setScrollTop(cm, val) {
1839 if (Math.abs(cm.doc.scrollTop - val) < 2) return;
1867 if (Math.abs(cm.doc.scrollTop - val) < 2) return;
1840 cm.doc.scrollTop = val;
1868 cm.doc.scrollTop = val;
1841 if (!gecko) updateDisplay(cm, [], val);
1869 if (!gecko) updateDisplay(cm, [], val);
1842 if (cm.display.scroller.scrollTop != val) cm.display.scroller.scrollTop = val;
1870 if (cm.display.scroller.scrollTop != val) cm.display.scroller.scrollTop = val;
1843 if (cm.display.scrollbarV.scrollTop != val) cm.display.scrollbarV.scrollTop = val;
1871 if (cm.display.scrollbarV.scrollTop != val) cm.display.scrollbarV.scrollTop = val;
1844 if (gecko) updateDisplay(cm, []);
1872 if (gecko) updateDisplay(cm, []);
1873 startWorker(cm, 100);
1845 }
1874 }
1846 function setScrollLeft(cm, val, isScroller) {
1875 function setScrollLeft(cm, val, isScroller) {
1847 if (isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) return;
1876 if (isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) return;
1848 val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth);
1877 val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth);
1849 cm.doc.scrollLeft = val;
1878 cm.doc.scrollLeft = val;
1850 alignHorizontally(cm);
1879 alignHorizontally(cm);
1851 if (cm.display.scroller.scrollLeft != val) cm.display.scroller.scrollLeft = val;
1880 if (cm.display.scroller.scrollLeft != val) cm.display.scroller.scrollLeft = val;
1852 if (cm.display.scrollbarH.scrollLeft != val) cm.display.scrollbarH.scrollLeft = val;
1881 if (cm.display.scrollbarH.scrollLeft != val) cm.display.scrollbarH.scrollLeft = val;
1853 }
1882 }
1854
1883
1855 // Since the delta values reported on mouse wheel events are
1884 // Since the delta values reported on mouse wheel events are
1856 // unstandardized between browsers and even browser versions, and
1885 // unstandardized between browsers and even browser versions, and
1857 // generally horribly unpredictable, this code starts by measuring
1886 // generally horribly unpredictable, this code starts by measuring
1858 // the scroll effect that the first few mouse wheel events have,
1887 // the scroll effect that the first few mouse wheel events have,
1859 // and, from that, detects the way it can convert deltas to pixel
1888 // and, from that, detects the way it can convert deltas to pixel
1860 // offsets afterwards.
1889 // offsets afterwards.
1861 //
1890 //
1862 // The reason we want to know the amount a wheel event will scroll
1891 // The reason we want to know the amount a wheel event will scroll
1863 // is that it gives us a chance to update the display before the
1892 // is that it gives us a chance to update the display before the
1864 // actual scrolling happens, reducing flickering.
1893 // actual scrolling happens, reducing flickering.
1865
1894
1866 var wheelSamples = 0, wheelPixelsPerUnit = null;
1895 var wheelSamples = 0, wheelPixelsPerUnit = null;
1867 // Fill in a browser-detected starting value on browsers where we
1896 // Fill in a browser-detected starting value on browsers where we
1868 // know one. These don't have to be accurate -- the result of them
1897 // know one. These don't have to be accurate -- the result of them
1869 // being wrong would just be a slight flicker on the first wheel
1898 // being wrong would just be a slight flicker on the first wheel
1870 // scroll (if it is large enough).
1899 // scroll (if it is large enough).
1871 if (ie) wheelPixelsPerUnit = -.53;
1900 if (ie) wheelPixelsPerUnit = -.53;
1872 else if (gecko) wheelPixelsPerUnit = 15;
1901 else if (gecko) wheelPixelsPerUnit = 15;
1873 else if (chrome) wheelPixelsPerUnit = -.7;
1902 else if (chrome) wheelPixelsPerUnit = -.7;
1874 else if (safari) wheelPixelsPerUnit = -1/3;
1903 else if (safari) wheelPixelsPerUnit = -1/3;
1875
1904
1876 function onScrollWheel(cm, e) {
1905 function onScrollWheel(cm, e) {
1877 var dx = e.wheelDeltaX, dy = e.wheelDeltaY;
1906 var dx = e.wheelDeltaX, dy = e.wheelDeltaY;
1878 if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) dx = e.detail;
1907 if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) dx = e.detail;
1879 if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) dy = e.detail;
1908 if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) dy = e.detail;
1880 else if (dy == null) dy = e.wheelDelta;
1909 else if (dy == null) dy = e.wheelDelta;
1881
1910
1882 var display = cm.display, scroll = display.scroller;
1911 var display = cm.display, scroll = display.scroller;
1883 // Quit if there's nothing to scroll here
1912 // Quit if there's nothing to scroll here
1884 if (!(dx && scroll.scrollWidth > scroll.clientWidth ||
1913 if (!(dx && scroll.scrollWidth > scroll.clientWidth ||
1885 dy && scroll.scrollHeight > scroll.clientHeight)) return;
1914 dy && scroll.scrollHeight > scroll.clientHeight)) return;
1886
1915
1887 // Webkit browsers on OS X abort momentum scrolls when the target
1916 // Webkit browsers on OS X abort momentum scrolls when the target
1888 // of the scroll event is removed from the scrollable element.
1917 // of the scroll event is removed from the scrollable element.
1889 // This hack (see related code in patchDisplay) makes sure the
1918 // This hack (see related code in patchDisplay) makes sure the
1890 // element is kept around.
1919 // element is kept around.
1891 if (dy && mac && webkit) {
1920 if (dy && mac && webkit) {
1892 for (var cur = e.target; cur != scroll; cur = cur.parentNode) {
1921 for (var cur = e.target; cur != scroll; cur = cur.parentNode) {
1893 if (cur.lineObj) {
1922 if (cur.lineObj) {
1894 cm.display.currentWheelTarget = cur;
1923 cm.display.currentWheelTarget = cur;
1895 break;
1924 break;
1896 }
1925 }
1897 }
1926 }
1898 }
1927 }
1899
1928
1900 // On some browsers, horizontal scrolling will cause redraws to
1929 // On some browsers, horizontal scrolling will cause redraws to
1901 // happen before the gutter has been realigned, causing it to
1930 // happen before the gutter has been realigned, causing it to
1902 // wriggle around in a most unseemly way. When we have an
1931 // wriggle around in a most unseemly way. When we have an
1903 // estimated pixels/delta value, we just handle horizontal
1932 // estimated pixels/delta value, we just handle horizontal
1904 // scrolling entirely here. It'll be slightly off from native, but
1933 // scrolling entirely here. It'll be slightly off from native, but
1905 // better than glitching out.
1934 // better than glitching out.
1906 if (dx && !gecko && !opera && wheelPixelsPerUnit != null) {
1935 if (dx && !gecko && !opera && wheelPixelsPerUnit != null) {
1907 if (dy)
1936 if (dy)
1908 setScrollTop(cm, Math.max(0, Math.min(scroll.scrollTop + dy * wheelPixelsPerUnit, scroll.scrollHeight - scroll.clientHeight)));
1937 setScrollTop(cm, Math.max(0, Math.min(scroll.scrollTop + dy * wheelPixelsPerUnit, scroll.scrollHeight - scroll.clientHeight)));
1909 setScrollLeft(cm, Math.max(0, Math.min(scroll.scrollLeft + dx * wheelPixelsPerUnit, scroll.scrollWidth - scroll.clientWidth)));
1938 setScrollLeft(cm, Math.max(0, Math.min(scroll.scrollLeft + dx * wheelPixelsPerUnit, scroll.scrollWidth - scroll.clientWidth)));
1910 e_preventDefault(e);
1939 e_preventDefault(e);
1911 display.wheelStartX = null; // Abort measurement, if in progress
1940 display.wheelStartX = null; // Abort measurement, if in progress
1912 return;
1941 return;
1913 }
1942 }
1914
1943
1915 if (dy && wheelPixelsPerUnit != null) {
1944 if (dy && wheelPixelsPerUnit != null) {
1916 var pixels = dy * wheelPixelsPerUnit;
1945 var pixels = dy * wheelPixelsPerUnit;
1917 var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight;
1946 var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight;
1918 if (pixels < 0) top = Math.max(0, top + pixels - 50);
1947 if (pixels < 0) top = Math.max(0, top + pixels - 50);
1919 else bot = Math.min(cm.doc.height, bot + pixels + 50);
1948 else bot = Math.min(cm.doc.height, bot + pixels + 50);
1920 updateDisplay(cm, [], {top: top, bottom: bot});
1949 updateDisplay(cm, [], {top: top, bottom: bot});
1921 }
1950 }
1922
1951
1923 if (wheelSamples < 20) {
1952 if (wheelSamples < 20) {
1924 if (display.wheelStartX == null) {
1953 if (display.wheelStartX == null) {
1925 display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop;
1954 display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop;
1926 display.wheelDX = dx; display.wheelDY = dy;
1955 display.wheelDX = dx; display.wheelDY = dy;
1927 setTimeout(function() {
1956 setTimeout(function() {
1928 if (display.wheelStartX == null) return;
1957 if (display.wheelStartX == null) return;
1929 var movedX = scroll.scrollLeft - display.wheelStartX;
1958 var movedX = scroll.scrollLeft - display.wheelStartX;
1930 var movedY = scroll.scrollTop - display.wheelStartY;
1959 var movedY = scroll.scrollTop - display.wheelStartY;
1931 var sample = (movedY && display.wheelDY && movedY / display.wheelDY) ||
1960 var sample = (movedY && display.wheelDY && movedY / display.wheelDY) ||
1932 (movedX && display.wheelDX && movedX / display.wheelDX);
1961 (movedX && display.wheelDX && movedX / display.wheelDX);
1933 display.wheelStartX = display.wheelStartY = null;
1962 display.wheelStartX = display.wheelStartY = null;
1934 if (!sample) return;
1963 if (!sample) return;
1935 wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1);
1964 wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1);
1936 ++wheelSamples;
1965 ++wheelSamples;
1937 }, 200);
1966 }, 200);
1938 } else {
1967 } else {
1939 display.wheelDX += dx; display.wheelDY += dy;
1968 display.wheelDX += dx; display.wheelDY += dy;
1940 }
1969 }
1941 }
1970 }
1942 }
1971 }
1943
1972
1944 function doHandleBinding(cm, bound, dropShift) {
1973 function doHandleBinding(cm, bound, dropShift) {
1945 if (typeof bound == "string") {
1974 if (typeof bound == "string") {
1946 bound = commands[bound];
1975 bound = commands[bound];
1947 if (!bound) return false;
1976 if (!bound) return false;
1948 }
1977 }
1949 // Ensure previous input has been read, so that the handler sees a
1978 // Ensure previous input has been read, so that the handler sees a
1950 // consistent view of the document
1979 // consistent view of the document
1951 if (cm.display.pollingFast && readInput(cm)) cm.display.pollingFast = false;
1980 if (cm.display.pollingFast && readInput(cm)) cm.display.pollingFast = false;
1952 var doc = cm.doc, prevShift = doc.sel.shift, done = false;
1981 var doc = cm.doc, prevShift = doc.sel.shift, done = false;
1953 try {
1982 try {
1954 if (isReadOnly(cm)) cm.state.suppressEdits = true;
1983 if (isReadOnly(cm)) cm.state.suppressEdits = true;
1955 if (dropShift) doc.sel.shift = false;
1984 if (dropShift) doc.sel.shift = false;
1956 done = bound(cm) != Pass;
1985 done = bound(cm) != Pass;
1957 } finally {
1986 } finally {
1958 doc.sel.shift = prevShift;
1987 doc.sel.shift = prevShift;
1959 cm.state.suppressEdits = false;
1988 cm.state.suppressEdits = false;
1960 }
1989 }
1961 return done;
1990 return done;
1962 }
1991 }
1963
1992
1964 function allKeyMaps(cm) {
1993 function allKeyMaps(cm) {
1965 var maps = cm.state.keyMaps.slice(0);
1994 var maps = cm.state.keyMaps.slice(0);
1966 if (cm.options.extraKeys) maps.push(cm.options.extraKeys);
1995 if (cm.options.extraKeys) maps.push(cm.options.extraKeys);
1967 maps.push(cm.options.keyMap);
1996 maps.push(cm.options.keyMap);
1968 return maps;
1997 return maps;
1969 }
1998 }
1970
1999
1971 var maybeTransition;
2000 var maybeTransition;
1972 function handleKeyBinding(cm, e) {
2001 function handleKeyBinding(cm, e) {
1973 // Handle auto keymap transitions
2002 // Handle auto keymap transitions
1974 var startMap = getKeyMap(cm.options.keyMap), next = startMap.auto;
2003 var startMap = getKeyMap(cm.options.keyMap), next = startMap.auto;
1975 clearTimeout(maybeTransition);
2004 clearTimeout(maybeTransition);
1976 if (next && !isModifierKey(e)) maybeTransition = setTimeout(function() {
2005 if (next && !isModifierKey(e)) maybeTransition = setTimeout(function() {
1977 if (getKeyMap(cm.options.keyMap) == startMap)
2006 if (getKeyMap(cm.options.keyMap) == startMap) {
1978 cm.options.keyMap = (next.call ? next.call(null, cm) : next);
2007 cm.options.keyMap = (next.call ? next.call(null, cm) : next);
2008 keyMapChanged(cm);
2009 }
1979 }, 50);
2010 }, 50);
1980
2011
1981 var name = keyName(e, true), handled = false;
2012 var name = keyName(e, true), handled = false;
1982 if (!name) return false;
2013 if (!name) return false;
1983 var keymaps = allKeyMaps(cm);
2014 var keymaps = allKeyMaps(cm);
1984
2015
1985 if (e.shiftKey) {
2016 if (e.shiftKey) {
1986 // First try to resolve full name (including 'Shift-'). Failing
2017 // First try to resolve full name (including 'Shift-'). Failing
1987 // that, see if there is a cursor-motion command (starting with
2018 // that, see if there is a cursor-motion command (starting with
1988 // 'go') bound to the keyname without 'Shift-'.
2019 // 'go') bound to the keyname without 'Shift-'.
1989 handled = lookupKey("Shift-" + name, keymaps, function(b) {return doHandleBinding(cm, b, true);})
2020 handled = lookupKey("Shift-" + name, keymaps, function(b) {return doHandleBinding(cm, b, true);})
1990 || lookupKey(name, keymaps, function(b) {
2021 || lookupKey(name, keymaps, function(b) {
1991 if (typeof b == "string" && /^go[A-Z]/.test(b)) return doHandleBinding(cm, b);
2022 if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion)
2023 return doHandleBinding(cm, b);
1992 });
2024 });
1993 } else {
2025 } else {
1994 handled = lookupKey(name, keymaps, function(b) { return doHandleBinding(cm, b); });
2026 handled = lookupKey(name, keymaps, function(b) { return doHandleBinding(cm, b); });
1995 }
2027 }
1996 if (handled == "stop") handled = false;
1997
2028
1998 if (handled) {
2029 if (handled) {
1999 e_preventDefault(e);
2030 e_preventDefault(e);
2000 restartBlink(cm);
2031 restartBlink(cm);
2001 if (ie_lt9) { e.oldKeyCode = e.keyCode; e.keyCode = 0; }
2032 if (ie_lt9) { e.oldKeyCode = e.keyCode; e.keyCode = 0; }
2033 signalLater(cm, "keyHandled", cm, name, e);
2002 }
2034 }
2003 return handled;
2035 return handled;
2004 }
2036 }
2005
2037
2006 function handleCharBinding(cm, e, ch) {
2038 function handleCharBinding(cm, e, ch) {
2007 var handled = lookupKey("'" + ch + "'", allKeyMaps(cm),
2039 var handled = lookupKey("'" + ch + "'", allKeyMaps(cm),
2008 function(b) { return doHandleBinding(cm, b, true); });
2040 function(b) { return doHandleBinding(cm, b, true); });
2009 if (handled) {
2041 if (handled) {
2010 e_preventDefault(e);
2042 e_preventDefault(e);
2011 restartBlink(cm);
2043 restartBlink(cm);
2044 signalLater(cm, "keyHandled", cm, "'" + ch + "'", e);
2012 }
2045 }
2013 return handled;
2046 return handled;
2014 }
2047 }
2015
2048
2016 var lastStoppedKey = null;
2049 var lastStoppedKey = null;
2017 function onKeyDown(e) {
2050 function onKeyDown(e) {
2018 var cm = this;
2051 var cm = this;
2019 if (!cm.state.focused) onFocus(cm);
2052 if (!cm.state.focused) onFocus(cm);
2020 if (ie && e.keyCode == 27) { e.returnValue = false; }
2053 if (ie && e.keyCode == 27) { e.returnValue = false; }
2021 if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
2054 if (signalDOMEvent(cm, e) || cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
2022 var code = e.keyCode;
2055 var code = e.keyCode;
2023 // IE does strange things with escape.
2056 // IE does strange things with escape.
2024 cm.doc.sel.shift = code == 16 || e.shiftKey;
2057 cm.doc.sel.shift = code == 16 || e.shiftKey;
2025 // First give onKeyEvent option a chance to handle this.
2058 // First give onKeyEvent option a chance to handle this.
2026 var handled = handleKeyBinding(cm, e);
2059 var handled = handleKeyBinding(cm, e);
2027 if (opera) {
2060 if (opera) {
2028 lastStoppedKey = handled ? code : null;
2061 lastStoppedKey = handled ? code : null;
2029 // Opera has no cut event... we try to at least catch the key combo
2062 // Opera has no cut event... we try to at least catch the key combo
2030 if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey))
2063 if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey))
2031 cm.replaceSelection("");
2064 cm.replaceSelection("");
2032 }
2065 }
2033 }
2066 }
2034
2067
2035 function onKeyPress(e) {
2068 function onKeyPress(e) {
2036 var cm = this;
2069 var cm = this;
2037 if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
2070 if (signalDOMEvent(cm, e) || cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
2038 var keyCode = e.keyCode, charCode = e.charCode;
2071 var keyCode = e.keyCode, charCode = e.charCode;
2039 if (opera && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;}
2072 if (opera && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;}
2040 if (((opera && (!e.which || e.which < 10)) || khtml) && handleKeyBinding(cm, e)) return;
2073 if (((opera && (!e.which || e.which < 10)) || khtml) && handleKeyBinding(cm, e)) return;
2041 var ch = String.fromCharCode(charCode == null ? keyCode : charCode);
2074 var ch = String.fromCharCode(charCode == null ? keyCode : charCode);
2042 if (this.options.electricChars && this.doc.mode.electricChars &&
2075 if (this.options.electricChars && this.doc.mode.electricChars &&
2043 this.options.smartIndent && !isReadOnly(this) &&
2076 this.options.smartIndent && !isReadOnly(this) &&
2044 this.doc.mode.electricChars.indexOf(ch) > -1)
2077 this.doc.mode.electricChars.indexOf(ch) > -1)
2045 setTimeout(operation(cm, function() {indentLine(cm, cm.doc.sel.to.line, "smart");}), 75);
2078 setTimeout(operation(cm, function() {indentLine(cm, cm.doc.sel.to.line, "smart");}), 75);
2046 if (handleCharBinding(cm, e, ch)) return;
2079 if (handleCharBinding(cm, e, ch)) return;
2047 if (ie && !ie_lt9) cm.display.inputHasSelection = null;
2080 if (ie && !ie_lt9) cm.display.inputHasSelection = null;
2048 fastPoll(cm);
2081 fastPoll(cm);
2049 }
2082 }
2050
2083
2051 function onFocus(cm) {
2084 function onFocus(cm) {
2052 if (cm.options.readOnly == "nocursor") return;
2085 if (cm.options.readOnly == "nocursor") return;
2053 if (!cm.state.focused) {
2086 if (!cm.state.focused) {
2054 signal(cm, "focus", cm);
2087 signal(cm, "focus", cm);
2055 cm.state.focused = true;
2088 cm.state.focused = true;
2056 if (cm.display.wrapper.className.search(/\bCodeMirror-focused\b/) == -1)
2089 if (cm.display.wrapper.className.search(/\bCodeMirror-focused\b/) == -1)
2057 cm.display.wrapper.className += " CodeMirror-focused";
2090 cm.display.wrapper.className += " CodeMirror-focused";
2058 resetInput(cm, true);
2091 resetInput(cm, true);
2059 }
2092 }
2060 slowPoll(cm);
2093 slowPoll(cm);
2061 restartBlink(cm);
2094 restartBlink(cm);
2062 }
2095 }
2063 function onBlur(cm) {
2096 function onBlur(cm) {
2064 if (cm.state.focused) {
2097 if (cm.state.focused) {
2065 signal(cm, "blur", cm);
2098 signal(cm, "blur", cm);
2066 cm.state.focused = false;
2099 cm.state.focused = false;
2067 cm.display.wrapper.className = cm.display.wrapper.className.replace(" CodeMirror-focused", "");
2100 cm.display.wrapper.className = cm.display.wrapper.className.replace(" CodeMirror-focused", "");
2068 }
2101 }
2069 clearInterval(cm.display.blinker);
2102 clearInterval(cm.display.blinker);
2070 setTimeout(function() {if (!cm.state.focused) cm.doc.sel.shift = false;}, 150);
2103 setTimeout(function() {if (!cm.state.focused) cm.doc.sel.shift = false;}, 150);
2071 }
2104 }
2072
2105
2073 var detectingSelectAll;
2106 var detectingSelectAll;
2074 function onContextMenu(cm, e) {
2107 function onContextMenu(cm, e) {
2075 var display = cm.display, sel = cm.doc.sel;
2108 var display = cm.display, sel = cm.doc.sel;
2076 if (eventInWidget(display, e)) return;
2109 if (eventInWidget(display, e)) return;
2077
2110
2078 var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop;
2111 var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop;
2079 if (!pos || opera) return; // Opera is difficult.
2112 if (!pos || opera) return; // Opera is difficult.
2080 if (posEq(sel.from, sel.to) || posLess(pos, sel.from) || !posLess(pos, sel.to))
2113 if (posEq(sel.from, sel.to) || posLess(pos, sel.from) || !posLess(pos, sel.to))
2081 operation(cm, setSelection)(cm.doc, pos, pos);
2114 operation(cm, setSelection)(cm.doc, pos, pos);
2082
2115
2083 var oldCSS = display.input.style.cssText;
2116 var oldCSS = display.input.style.cssText;
2084 display.inputDiv.style.position = "absolute";
2117 display.inputDiv.style.position = "absolute";
2085 display.input.style.cssText = "position: fixed; width: 30px; height: 30px; top: " + (e.clientY - 5) +
2118 display.input.style.cssText = "position: fixed; width: 30px; height: 30px; top: " + (e.clientY - 5) +
2086 "px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: white; outline: none;" +
2119 "px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: white; outline: none;" +
2087 "border-width: 0; outline: none; overflow: hidden; opacity: .05; -ms-opacity: .05; filter: alpha(opacity=5);";
2120 "border-width: 0; outline: none; overflow: hidden; opacity: .05; -ms-opacity: .05; filter: alpha(opacity=5);";
2088 focusInput(cm);
2121 focusInput(cm);
2089 resetInput(cm, true);
2122 resetInput(cm, true);
2090 // Adds "Select all" to context menu in FF
2123 // Adds "Select all" to context menu in FF
2091 if (posEq(sel.from, sel.to)) display.input.value = display.prevInput = " ";
2124 if (posEq(sel.from, sel.to)) display.input.value = display.prevInput = " ";
2092
2125
2126 function prepareSelectAllHack() {
2127 if (display.input.selectionStart != null) {
2128 var extval = display.input.value = " " + (posEq(sel.from, sel.to) ? "" : display.input.value);
2129 display.prevInput = " ";
2130 display.input.selectionStart = 1; display.input.selectionEnd = extval.length;
2131 }
2132 }
2093 function rehide() {
2133 function rehide() {
2094 display.inputDiv.style.position = "relative";
2134 display.inputDiv.style.position = "relative";
2095 display.input.style.cssText = oldCSS;
2135 display.input.style.cssText = oldCSS;
2096 if (ie_lt9) display.scrollbarV.scrollTop = display.scroller.scrollTop = scrollPos;
2136 if (ie_lt9) display.scrollbarV.scrollTop = display.scroller.scrollTop = scrollPos;
2097 slowPoll(cm);
2137 slowPoll(cm);
2098
2138
2099 // Try to detect the user choosing select-all
2139 // Try to detect the user choosing select-all
2100 if (display.input.selectionStart != null && (!ie || ie_lt9)) {
2140 if (display.input.selectionStart != null) {
2141 if (!ie || ie_lt9) prepareSelectAllHack();
2101 clearTimeout(detectingSelectAll);
2142 clearTimeout(detectingSelectAll);
2102 var extval = display.input.value = " " + (posEq(sel.from, sel.to) ? "" : display.input.value), i = 0;
2143 var i = 0, poll = function(){
2103 display.prevInput = " ";
2104 display.input.selectionStart = 1; display.input.selectionEnd = extval.length;
2105 var poll = function(){
2106 if (display.prevInput == " " && display.input.selectionStart == 0)
2144 if (display.prevInput == " " && display.input.selectionStart == 0)
2107 operation(cm, commands.selectAll)(cm);
2145 operation(cm, commands.selectAll)(cm);
2108 else if (i++ < 10) detectingSelectAll = setTimeout(poll, 500);
2146 else if (i++ < 10) detectingSelectAll = setTimeout(poll, 500);
2109 else resetInput(cm);
2147 else resetInput(cm);
2110 };
2148 };
2111 detectingSelectAll = setTimeout(poll, 200);
2149 detectingSelectAll = setTimeout(poll, 200);
2112 }
2150 }
2113 }
2151 }
2114
2152
2153 if (ie && !ie_lt9) prepareSelectAllHack();
2115 if (captureMiddleClick) {
2154 if (captureMiddleClick) {
2116 e_stop(e);
2155 e_stop(e);
2117 var mouseup = function() {
2156 var mouseup = function() {
2118 off(window, "mouseup", mouseup);
2157 off(window, "mouseup", mouseup);
2119 setTimeout(rehide, 20);
2158 setTimeout(rehide, 20);
2120 };
2159 };
2121 on(window, "mouseup", mouseup);
2160 on(window, "mouseup", mouseup);
2122 } else {
2161 } else {
2123 setTimeout(rehide, 50);
2162 setTimeout(rehide, 50);
2124 }
2163 }
2125 }
2164 }
2126
2165
2127 // UPDATING
2166 // UPDATING
2128
2167
2129 function changeEnd(change) {
2168 var changeEnd = CodeMirror.changeEnd = function(change) {
2130 if (!change.text) return change.to;
2169 if (!change.text) return change.to;
2131 return Pos(change.from.line + change.text.length - 1,
2170 return Pos(change.from.line + change.text.length - 1,
2132 lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0));
2171 lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0));
2133 }
2172 };
2134
2173
2135 // Make sure a position will be valid after the given change.
2174 // Make sure a position will be valid after the given change.
2136 function clipPostChange(doc, change, pos) {
2175 function clipPostChange(doc, change, pos) {
2137 if (!posLess(change.from, pos)) return clipPos(doc, pos);
2176 if (!posLess(change.from, pos)) return clipPos(doc, pos);
2138 var diff = (change.text.length - 1) - (change.to.line - change.from.line);
2177 var diff = (change.text.length - 1) - (change.to.line - change.from.line);
2139 if (pos.line > change.to.line + diff) {
2178 if (pos.line > change.to.line + diff) {
2140 var preLine = pos.line - diff, lastLine = doc.first + doc.size - 1;
2179 var preLine = pos.line - diff, lastLine = doc.first + doc.size - 1;
2141 if (preLine > lastLine) return Pos(lastLine, getLine(doc, lastLine).text.length);
2180 if (preLine > lastLine) return Pos(lastLine, getLine(doc, lastLine).text.length);
2142 return clipToLen(pos, getLine(doc, preLine).text.length);
2181 return clipToLen(pos, getLine(doc, preLine).text.length);
2143 }
2182 }
2144 if (pos.line == change.to.line + diff)
2183 if (pos.line == change.to.line + diff)
2145 return clipToLen(pos, lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0) +
2184 return clipToLen(pos, lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0) +
2146 getLine(doc, change.to.line).text.length - change.to.ch);
2185 getLine(doc, change.to.line).text.length - change.to.ch);
2147 var inside = pos.line - change.from.line;
2186 var inside = pos.line - change.from.line;
2148 return clipToLen(pos, change.text[inside].length + (inside ? 0 : change.from.ch));
2187 return clipToLen(pos, change.text[inside].length + (inside ? 0 : change.from.ch));
2149 }
2188 }
2150
2189
2151 // Hint can be null|"end"|"start"|"around"|{anchor,head}
2190 // Hint can be null|"end"|"start"|"around"|{anchor,head}
2152 function computeSelAfterChange(doc, change, hint) {
2191 function computeSelAfterChange(doc, change, hint) {
2153 if (hint && typeof hint == "object") // Assumed to be {anchor, head} object
2192 if (hint && typeof hint == "object") // Assumed to be {anchor, head} object
2154 return {anchor: clipPostChange(doc, change, hint.anchor),
2193 return {anchor: clipPostChange(doc, change, hint.anchor),
2155 head: clipPostChange(doc, change, hint.head)};
2194 head: clipPostChange(doc, change, hint.head)};
2156
2195
2157 if (hint == "start") return {anchor: change.from, head: change.from};
2196 if (hint == "start") return {anchor: change.from, head: change.from};
2158
2197
2159 var end = changeEnd(change);
2198 var end = changeEnd(change);
2160 if (hint == "around") return {anchor: change.from, head: end};
2199 if (hint == "around") return {anchor: change.from, head: end};
2161 if (hint == "end") return {anchor: end, head: end};
2200 if (hint == "end") return {anchor: end, head: end};
2162
2201
2163 // hint is null, leave the selection alone as much as possible
2202 // hint is null, leave the selection alone as much as possible
2164 var adjustPos = function(pos) {
2203 var adjustPos = function(pos) {
2165 if (posLess(pos, change.from)) return pos;
2204 if (posLess(pos, change.from)) return pos;
2166 if (!posLess(change.to, pos)) return end;
2205 if (!posLess(change.to, pos)) return end;
2167
2206
2168 var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch;
2207 var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch;
2169 if (pos.line == change.to.line) ch += end.ch - change.to.ch;
2208 if (pos.line == change.to.line) ch += end.ch - change.to.ch;
2170 return Pos(line, ch);
2209 return Pos(line, ch);
2171 };
2210 };
2172 return {anchor: adjustPos(doc.sel.anchor), head: adjustPos(doc.sel.head)};
2211 return {anchor: adjustPos(doc.sel.anchor), head: adjustPos(doc.sel.head)};
2173 }
2212 }
2174
2213
2175 function filterChange(doc, change) {
2214 function filterChange(doc, change, update) {
2176 var obj = {
2215 var obj = {
2177 canceled: false,
2216 canceled: false,
2178 from: change.from,
2217 from: change.from,
2179 to: change.to,
2218 to: change.to,
2180 text: change.text,
2219 text: change.text,
2181 origin: change.origin,
2220 origin: change.origin,
2182 update: function(from, to, text, origin) {
2183 if (from) this.from = clipPos(doc, from);
2184 if (to) this.to = clipPos(doc, to);
2185 if (text) this.text = text;
2186 if (origin !== undefined) this.origin = origin;
2187 },
2188 cancel: function() { this.canceled = true; }
2221 cancel: function() { this.canceled = true; }
2189 };
2222 };
2223 if (update) obj.update = function(from, to, text, origin) {
2224 if (from) this.from = clipPos(doc, from);
2225 if (to) this.to = clipPos(doc, to);
2226 if (text) this.text = text;
2227 if (origin !== undefined) this.origin = origin;
2228 };
2190 signal(doc, "beforeChange", doc, obj);
2229 signal(doc, "beforeChange", doc, obj);
2191 if (doc.cm) signal(doc.cm, "beforeChange", doc.cm, obj);
2230 if (doc.cm) signal(doc.cm, "beforeChange", doc.cm, obj);
2192
2231
2193 if (obj.canceled) return null;
2232 if (obj.canceled) return null;
2194 return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin};
2233 return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin};
2195 }
2234 }
2196
2235
2197 // Replace the range from from to to by the strings in replacement.
2236 // Replace the range from from to to by the strings in replacement.
2198 // change is a {from, to, text [, origin]} object
2237 // change is a {from, to, text [, origin]} object
2199 function makeChange(doc, change, selUpdate, ignoreReadOnly) {
2238 function makeChange(doc, change, selUpdate, ignoreReadOnly) {
2200 if (doc.cm) {
2239 if (doc.cm) {
2201 if (!doc.cm.curOp) return operation(doc.cm, makeChange)(doc, change, selUpdate, ignoreReadOnly);
2240 if (!doc.cm.curOp) return operation(doc.cm, makeChange)(doc, change, selUpdate, ignoreReadOnly);
2202 if (doc.cm.state.suppressEdits) return;
2241 if (doc.cm.state.suppressEdits) return;
2203 }
2242 }
2204
2243
2205 if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) {
2244 if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) {
2206 change = filterChange(doc, change);
2245 change = filterChange(doc, change, true);
2207 if (!change) return;
2246 if (!change) return;
2208 }
2247 }
2209
2248
2210 // Possibly split or suppress the update based on the presence
2249 // Possibly split or suppress the update based on the presence
2211 // of read-only spans in its range.
2250 // of read-only spans in its range.
2212 var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to);
2251 var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to);
2213 if (split) {
2252 if (split) {
2214 for (var i = split.length - 1; i >= 1; --i)
2253 for (var i = split.length - 1; i >= 1; --i)
2215 makeChangeNoReadonly(doc, {from: split[i].from, to: split[i].to, text: [""]});
2254 makeChangeNoReadonly(doc, {from: split[i].from, to: split[i].to, text: [""]});
2216 if (split.length)
2255 if (split.length)
2217 makeChangeNoReadonly(doc, {from: split[0].from, to: split[0].to, text: change.text}, selUpdate);
2256 makeChangeNoReadonly(doc, {from: split[0].from, to: split[0].to, text: change.text}, selUpdate);
2218 } else {
2257 } else {
2219 makeChangeNoReadonly(doc, change, selUpdate);
2258 makeChangeNoReadonly(doc, change, selUpdate);
2220 }
2259 }
2221 }
2260 }
2222
2261
2223 function makeChangeNoReadonly(doc, change, selUpdate) {
2262 function makeChangeNoReadonly(doc, change, selUpdate) {
2224 var selAfter = computeSelAfterChange(doc, change, selUpdate);
2263 var selAfter = computeSelAfterChange(doc, change, selUpdate);
2225 addToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN);
2264 addToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN);
2226
2265
2227 makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change));
2266 makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change));
2228 var rebased = [];
2267 var rebased = [];
2229
2268
2230 linkedDocs(doc, function(doc, sharedHist) {
2269 linkedDocs(doc, function(doc, sharedHist) {
2231 if (!sharedHist && indexOf(rebased, doc.history) == -1) {
2270 if (!sharedHist && indexOf(rebased, doc.history) == -1) {
2232 rebaseHist(doc.history, change);
2271 rebaseHist(doc.history, change);
2233 rebased.push(doc.history);
2272 rebased.push(doc.history);
2234 }
2273 }
2235 makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change));
2274 makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change));
2236 });
2275 });
2237 }
2276 }
2238
2277
2239 function makeChangeFromHistory(doc, type) {
2278 function makeChangeFromHistory(doc, type) {
2240 if (doc.cm && doc.cm.state.suppressEdits) return;
2279 if (doc.cm && doc.cm.state.suppressEdits) return;
2241
2280
2242 var hist = doc.history;
2281 var hist = doc.history;
2243 var event = (type == "undo" ? hist.done : hist.undone).pop();
2282 var event = (type == "undo" ? hist.done : hist.undone).pop();
2244 if (!event) return;
2283 if (!event) return;
2245 hist.dirtyCounter += type == "undo" ? -1 : 1;
2246
2284
2247 var anti = {changes: [], anchorBefore: event.anchorAfter, headBefore: event.headAfter,
2285 var anti = {changes: [], anchorBefore: event.anchorAfter, headBefore: event.headAfter,
2248 anchorAfter: event.anchorBefore, headAfter: event.headBefore};
2286 anchorAfter: event.anchorBefore, headAfter: event.headBefore,
2287 generation: hist.generation};
2249 (type == "undo" ? hist.undone : hist.done).push(anti);
2288 (type == "undo" ? hist.undone : hist.done).push(anti);
2289 hist.generation = event.generation || ++hist.maxGeneration;
2290
2291 var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange");
2250
2292
2251 for (var i = event.changes.length - 1; i >= 0; --i) {
2293 for (var i = event.changes.length - 1; i >= 0; --i) {
2252 var change = event.changes[i];
2294 var change = event.changes[i];
2253 change.origin = type;
2295 change.origin = type;
2296 if (filter && !filterChange(doc, change, false)) {
2297 (type == "undo" ? hist.done : hist.undone).length = 0;
2298 return;
2299 }
2300
2254 anti.changes.push(historyChangeFromChange(doc, change));
2301 anti.changes.push(historyChangeFromChange(doc, change));
2255
2302
2256 var after = i ? computeSelAfterChange(doc, change, null)
2303 var after = i ? computeSelAfterChange(doc, change, null)
2257 : {anchor: event.anchorBefore, head: event.headBefore};
2304 : {anchor: event.anchorBefore, head: event.headBefore};
2258 makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change));
2305 makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change));
2259 var rebased = [];
2306 var rebased = [];
2260
2307
2261 linkedDocs(doc, function(doc, sharedHist) {
2308 linkedDocs(doc, function(doc, sharedHist) {
2262 if (!sharedHist && indexOf(rebased, doc.history) == -1) {
2309 if (!sharedHist && indexOf(rebased, doc.history) == -1) {
2263 rebaseHist(doc.history, change);
2310 rebaseHist(doc.history, change);
2264 rebased.push(doc.history);
2311 rebased.push(doc.history);
2265 }
2312 }
2266 makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change));
2313 makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change));
2267 });
2314 });
2268 }
2315 }
2269 }
2316 }
2270
2317
2271 function shiftDoc(doc, distance) {
2318 function shiftDoc(doc, distance) {
2272 function shiftPos(pos) {return Pos(pos.line + distance, pos.ch);}
2319 function shiftPos(pos) {return Pos(pos.line + distance, pos.ch);}
2273 doc.first += distance;
2320 doc.first += distance;
2274 if (doc.cm) regChange(doc.cm, doc.first, doc.first, distance);
2321 if (doc.cm) regChange(doc.cm, doc.first, doc.first, distance);
2275 doc.sel.head = shiftPos(doc.sel.head); doc.sel.anchor = shiftPos(doc.sel.anchor);
2322 doc.sel.head = shiftPos(doc.sel.head); doc.sel.anchor = shiftPos(doc.sel.anchor);
2276 doc.sel.from = shiftPos(doc.sel.from); doc.sel.to = shiftPos(doc.sel.to);
2323 doc.sel.from = shiftPos(doc.sel.from); doc.sel.to = shiftPos(doc.sel.to);
2277 }
2324 }
2278
2325
2279 function makeChangeSingleDoc(doc, change, selAfter, spans) {
2326 function makeChangeSingleDoc(doc, change, selAfter, spans) {
2280 if (doc.cm && !doc.cm.curOp)
2327 if (doc.cm && !doc.cm.curOp)
2281 return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans);
2328 return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans);
2282
2329
2283 if (change.to.line < doc.first) {
2330 if (change.to.line < doc.first) {
2284 shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line));
2331 shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line));
2285 return;
2332 return;
2286 }
2333 }
2287 if (change.from.line > doc.lastLine()) return;
2334 if (change.from.line > doc.lastLine()) return;
2288
2335
2289 // Clip the change to the size of this doc
2336 // Clip the change to the size of this doc
2290 if (change.from.line < doc.first) {
2337 if (change.from.line < doc.first) {
2291 var shift = change.text.length - 1 - (doc.first - change.from.line);
2338 var shift = change.text.length - 1 - (doc.first - change.from.line);
2292 shiftDoc(doc, shift);
2339 shiftDoc(doc, shift);
2293 change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch),
2340 change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch),
2294 text: [lst(change.text)], origin: change.origin};
2341 text: [lst(change.text)], origin: change.origin};
2295 }
2342 }
2296 var last = doc.lastLine();
2343 var last = doc.lastLine();
2297 if (change.to.line > last) {
2344 if (change.to.line > last) {
2298 change = {from: change.from, to: Pos(last, getLine(doc, last).text.length),
2345 change = {from: change.from, to: Pos(last, getLine(doc, last).text.length),
2299 text: [change.text[0]], origin: change.origin};
2346 text: [change.text[0]], origin: change.origin};
2300 }
2347 }
2301
2348
2302 change.removed = getBetween(doc, change.from, change.to);
2349 change.removed = getBetween(doc, change.from, change.to);
2303
2350
2304 if (!selAfter) selAfter = computeSelAfterChange(doc, change, null);
2351 if (!selAfter) selAfter = computeSelAfterChange(doc, change, null);
2305 if (doc.cm) makeChangeSingleDocInEditor(doc.cm, change, spans, selAfter);
2352 if (doc.cm) makeChangeSingleDocInEditor(doc.cm, change, spans, selAfter);
2306 else updateDoc(doc, change, spans, selAfter);
2353 else updateDoc(doc, change, spans, selAfter);
2307 }
2354 }
2308
2355
2309 function makeChangeSingleDocInEditor(cm, change, spans, selAfter) {
2356 function makeChangeSingleDocInEditor(cm, change, spans, selAfter) {
2310 var doc = cm.doc, display = cm.display, from = change.from, to = change.to;
2357 var doc = cm.doc, display = cm.display, from = change.from, to = change.to;
2311
2358
2312 var recomputeMaxLength = false, checkWidthStart = from.line;
2359 var recomputeMaxLength = false, checkWidthStart = from.line;
2313 if (!cm.options.lineWrapping) {
2360 if (!cm.options.lineWrapping) {
2314 checkWidthStart = lineNo(visualLine(doc, getLine(doc, from.line)));
2361 checkWidthStart = lineNo(visualLine(doc, getLine(doc, from.line)));
2315 doc.iter(checkWidthStart, to.line + 1, function(line) {
2362 doc.iter(checkWidthStart, to.line + 1, function(line) {
2316 if (line == display.maxLine) {
2363 if (line == display.maxLine) {
2317 recomputeMaxLength = true;
2364 recomputeMaxLength = true;
2318 return true;
2365 return true;
2319 }
2366 }
2320 });
2367 });
2321 }
2368 }
2322
2369
2323 if (!posLess(doc.sel.head, change.from) && !posLess(change.to, doc.sel.head))
2370 if (!posLess(doc.sel.head, change.from) && !posLess(change.to, doc.sel.head))
2324 cm.curOp.cursorActivity = true;
2371 cm.curOp.cursorActivity = true;
2325
2372
2326 updateDoc(doc, change, spans, selAfter, estimateHeight(cm));
2373 updateDoc(doc, change, spans, selAfter, estimateHeight(cm));
2327
2374
2328 if (!cm.options.lineWrapping) {
2375 if (!cm.options.lineWrapping) {
2329 doc.iter(checkWidthStart, from.line + change.text.length, function(line) {
2376 doc.iter(checkWidthStart, from.line + change.text.length, function(line) {
2330 var len = lineLength(doc, line);
2377 var len = lineLength(doc, line);
2331 if (len > display.maxLineLength) {
2378 if (len > display.maxLineLength) {
2332 display.maxLine = line;
2379 display.maxLine = line;
2333 display.maxLineLength = len;
2380 display.maxLineLength = len;
2334 display.maxLineChanged = true;
2381 display.maxLineChanged = true;
2335 recomputeMaxLength = false;
2382 recomputeMaxLength = false;
2336 }
2383 }
2337 });
2384 });
2338 if (recomputeMaxLength) cm.curOp.updateMaxLine = true;
2385 if (recomputeMaxLength) cm.curOp.updateMaxLine = true;
2339 }
2386 }
2340
2387
2341 // Adjust frontier, schedule worker
2388 // Adjust frontier, schedule worker
2342 doc.frontier = Math.min(doc.frontier, from.line);
2389 doc.frontier = Math.min(doc.frontier, from.line);
2343 startWorker(cm, 400);
2390 startWorker(cm, 400);
2344
2391
2345 var lendiff = change.text.length - (to.line - from.line) - 1;
2392 var lendiff = change.text.length - (to.line - from.line) - 1;
2346 // Remember that these lines changed, for updating the display
2393 // Remember that these lines changed, for updating the display
2347 regChange(cm, from.line, to.line + 1, lendiff);
2394 regChange(cm, from.line, to.line + 1, lendiff);
2348
2395
2349 if (hasHandler(cm, "change")) {
2396 if (hasHandler(cm, "change")) {
2350 var changeObj = {from: from, to: to,
2397 var changeObj = {from: from, to: to,
2351 text: change.text,
2398 text: change.text,
2352 removed: change.removed,
2399 removed: change.removed,
2353 origin: change.origin};
2400 origin: change.origin};
2354 if (cm.curOp.textChanged) {
2401 if (cm.curOp.textChanged) {
2355 for (var cur = cm.curOp.textChanged; cur.next; cur = cur.next) {}
2402 for (var cur = cm.curOp.textChanged; cur.next; cur = cur.next) {}
2356 cur.next = changeObj;
2403 cur.next = changeObj;
2357 } else cm.curOp.textChanged = changeObj;
2404 } else cm.curOp.textChanged = changeObj;
2358 }
2405 }
2359 }
2406 }
2360
2407
2361 function replaceRange(doc, code, from, to, origin) {
2408 function replaceRange(doc, code, from, to, origin) {
2362 if (!to) to = from;
2409 if (!to) to = from;
2363 if (posLess(to, from)) { var tmp = to; to = from; from = tmp; }
2410 if (posLess(to, from)) { var tmp = to; to = from; from = tmp; }
2364 if (typeof code == "string") code = splitLines(code);
2411 if (typeof code == "string") code = splitLines(code);
2365 makeChange(doc, {from: from, to: to, text: code, origin: origin}, null);
2412 makeChange(doc, {from: from, to: to, text: code, origin: origin}, null);
2366 }
2413 }
2367
2414
2368 // POSITION OBJECT
2415 // POSITION OBJECT
2369
2416
2370 function Pos(line, ch) {
2417 function Pos(line, ch) {
2371 if (!(this instanceof Pos)) return new Pos(line, ch);
2418 if (!(this instanceof Pos)) return new Pos(line, ch);
2372 this.line = line; this.ch = ch;
2419 this.line = line; this.ch = ch;
2373 }
2420 }
2374 CodeMirror.Pos = Pos;
2421 CodeMirror.Pos = Pos;
2375
2422
2376 function posEq(a, b) {return a.line == b.line && a.ch == b.ch;}
2423 function posEq(a, b) {return a.line == b.line && a.ch == b.ch;}
2377 function posLess(a, b) {return a.line < b.line || (a.line == b.line && a.ch < b.ch);}
2424 function posLess(a, b) {return a.line < b.line || (a.line == b.line && a.ch < b.ch);}
2378 function copyPos(x) {return Pos(x.line, x.ch);}
2425 function copyPos(x) {return Pos(x.line, x.ch);}
2379
2426
2380 // SELECTION
2427 // SELECTION
2381
2428
2382 function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1));}
2429 function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1));}
2383 function clipPos(doc, pos) {
2430 function clipPos(doc, pos) {
2384 if (pos.line < doc.first) return Pos(doc.first, 0);
2431 if (pos.line < doc.first) return Pos(doc.first, 0);
2385 var last = doc.first + doc.size - 1;
2432 var last = doc.first + doc.size - 1;
2386 if (pos.line > last) return Pos(last, getLine(doc, last).text.length);
2433 if (pos.line > last) return Pos(last, getLine(doc, last).text.length);
2387 return clipToLen(pos, getLine(doc, pos.line).text.length);
2434 return clipToLen(pos, getLine(doc, pos.line).text.length);
2388 }
2435 }
2389 function clipToLen(pos, linelen) {
2436 function clipToLen(pos, linelen) {
2390 var ch = pos.ch;
2437 var ch = pos.ch;
2391 if (ch == null || ch > linelen) return Pos(pos.line, linelen);
2438 if (ch == null || ch > linelen) return Pos(pos.line, linelen);
2392 else if (ch < 0) return Pos(pos.line, 0);
2439 else if (ch < 0) return Pos(pos.line, 0);
2393 else return pos;
2440 else return pos;
2394 }
2441 }
2395 function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size;}
2442 function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size;}
2396
2443
2397 // If shift is held, this will move the selection anchor. Otherwise,
2444 // If shift is held, this will move the selection anchor. Otherwise,
2398 // it'll set the whole selection.
2445 // it'll set the whole selection.
2399 function extendSelection(doc, pos, other, bias) {
2446 function extendSelection(doc, pos, other, bias) {
2400 if (doc.sel.shift || doc.sel.extend) {
2447 if (doc.sel.shift || doc.sel.extend) {
2401 var anchor = doc.sel.anchor;
2448 var anchor = doc.sel.anchor;
2402 if (other) {
2449 if (other) {
2403 var posBefore = posLess(pos, anchor);
2450 var posBefore = posLess(pos, anchor);
2404 if (posBefore != posLess(other, anchor)) {
2451 if (posBefore != posLess(other, anchor)) {
2405 anchor = pos;
2452 anchor = pos;
2406 pos = other;
2453 pos = other;
2407 } else if (posBefore != posLess(pos, other)) {
2454 } else if (posBefore != posLess(pos, other)) {
2408 pos = other;
2455 pos = other;
2409 }
2456 }
2410 }
2457 }
2411 setSelection(doc, anchor, pos, bias);
2458 setSelection(doc, anchor, pos, bias);
2412 } else {
2459 } else {
2413 setSelection(doc, pos, other || pos, bias);
2460 setSelection(doc, pos, other || pos, bias);
2414 }
2461 }
2415 if (doc.cm) doc.cm.curOp.userSelChange = true;
2462 if (doc.cm) doc.cm.curOp.userSelChange = true;
2416 }
2463 }
2417
2464
2418 function filterSelectionChange(doc, anchor, head) {
2465 function filterSelectionChange(doc, anchor, head) {
2419 var obj = {anchor: anchor, head: head};
2466 var obj = {anchor: anchor, head: head};
2420 signal(doc, "beforeSelectionChange", doc, obj);
2467 signal(doc, "beforeSelectionChange", doc, obj);
2421 if (doc.cm) signal(doc.cm, "beforeSelectionChange", doc.cm, obj);
2468 if (doc.cm) signal(doc.cm, "beforeSelectionChange", doc.cm, obj);
2422 obj.anchor = clipPos(doc, obj.anchor); obj.head = clipPos(doc, obj.head);
2469 obj.anchor = clipPos(doc, obj.anchor); obj.head = clipPos(doc, obj.head);
2423 return obj;
2470 return obj;
2424 }
2471 }
2425
2472
2426 // Update the selection. Last two args are only used by
2473 // Update the selection. Last two args are only used by
2427 // updateDoc, since they have to be expressed in the line
2474 // updateDoc, since they have to be expressed in the line
2428 // numbers before the update.
2475 // numbers before the update.
2429 function setSelection(doc, anchor, head, bias, checkAtomic) {
2476 function setSelection(doc, anchor, head, bias, checkAtomic) {
2430 if (!checkAtomic && hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange")) {
2477 if (!checkAtomic && hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange")) {
2431 var filtered = filterSelectionChange(doc, anchor, head);
2478 var filtered = filterSelectionChange(doc, anchor, head);
2432 head = filtered.head;
2479 head = filtered.head;
2433 anchor = filtered.anchor;
2480 anchor = filtered.anchor;
2434 }
2481 }
2435
2482
2436 var sel = doc.sel;
2483 var sel = doc.sel;
2437 sel.goalColumn = null;
2484 sel.goalColumn = null;
2438 // Skip over atomic spans.
2485 // Skip over atomic spans.
2439 if (checkAtomic || !posEq(anchor, sel.anchor))
2486 if (checkAtomic || !posEq(anchor, sel.anchor))
2440 anchor = skipAtomic(doc, anchor, bias, checkAtomic != "push");
2487 anchor = skipAtomic(doc, anchor, bias, checkAtomic != "push");
2441 if (checkAtomic || !posEq(head, sel.head))
2488 if (checkAtomic || !posEq(head, sel.head))
2442 head = skipAtomic(doc, head, bias, checkAtomic != "push");
2489 head = skipAtomic(doc, head, bias, checkAtomic != "push");
2443
2490
2444 if (posEq(sel.anchor, anchor) && posEq(sel.head, head)) return;
2491 if (posEq(sel.anchor, anchor) && posEq(sel.head, head)) return;
2445
2492
2446 sel.anchor = anchor; sel.head = head;
2493 sel.anchor = anchor; sel.head = head;
2447 var inv = posLess(head, anchor);
2494 var inv = posLess(head, anchor);
2448 sel.from = inv ? head : anchor;
2495 sel.from = inv ? head : anchor;
2449 sel.to = inv ? anchor : head;
2496 sel.to = inv ? anchor : head;
2450
2497
2451 if (doc.cm)
2498 if (doc.cm)
2452 doc.cm.curOp.updateInput = doc.cm.curOp.selectionChanged =
2499 doc.cm.curOp.updateInput = doc.cm.curOp.selectionChanged =
2453 doc.cm.curOp.cursorActivity = true;
2500 doc.cm.curOp.cursorActivity = true;
2454
2501
2455 signalLater(doc, "cursorActivity", doc);
2502 signalLater(doc, "cursorActivity", doc);
2456 }
2503 }
2457
2504
2458 function reCheckSelection(cm) {
2505 function reCheckSelection(cm) {
2459 setSelection(cm.doc, cm.doc.sel.from, cm.doc.sel.to, null, "push");
2506 setSelection(cm.doc, cm.doc.sel.from, cm.doc.sel.to, null, "push");
2460 }
2507 }
2461
2508
2462 function skipAtomic(doc, pos, bias, mayClear) {
2509 function skipAtomic(doc, pos, bias, mayClear) {
2463 var flipped = false, curPos = pos;
2510 var flipped = false, curPos = pos;
2464 var dir = bias || 1;
2511 var dir = bias || 1;
2465 doc.cantEdit = false;
2512 doc.cantEdit = false;
2466 search: for (;;) {
2513 search: for (;;) {
2467 var line = getLine(doc, curPos.line);
2514 var line = getLine(doc, curPos.line);
2468 if (line.markedSpans) {
2515 if (line.markedSpans) {
2469 for (var i = 0; i < line.markedSpans.length; ++i) {
2516 for (var i = 0; i < line.markedSpans.length; ++i) {
2470 var sp = line.markedSpans[i], m = sp.marker;
2517 var sp = line.markedSpans[i], m = sp.marker;
2471 if ((sp.from == null || (m.inclusiveLeft ? sp.from <= curPos.ch : sp.from < curPos.ch)) &&
2518 if ((sp.from == null || (m.inclusiveLeft ? sp.from <= curPos.ch : sp.from < curPos.ch)) &&
2472 (sp.to == null || (m.inclusiveRight ? sp.to >= curPos.ch : sp.to > curPos.ch))) {
2519 (sp.to == null || (m.inclusiveRight ? sp.to >= curPos.ch : sp.to > curPos.ch))) {
2473 if (mayClear) {
2520 if (mayClear) {
2474 signal(m, "beforeCursorEnter");
2521 signal(m, "beforeCursorEnter");
2475 if (m.explicitlyCleared) {
2522 if (m.explicitlyCleared) {
2476 if (!line.markedSpans) break;
2523 if (!line.markedSpans) break;
2477 else {--i; continue;}
2524 else {--i; continue;}
2478 }
2525 }
2479 }
2526 }
2480 if (!m.atomic) continue;
2527 if (!m.atomic) continue;
2481 var newPos = m.find()[dir < 0 ? "from" : "to"];
2528 var newPos = m.find()[dir < 0 ? "from" : "to"];
2482 if (posEq(newPos, curPos)) {
2529 if (posEq(newPos, curPos)) {
2483 newPos.ch += dir;
2530 newPos.ch += dir;
2484 if (newPos.ch < 0) {
2531 if (newPos.ch < 0) {
2485 if (newPos.line > doc.first) newPos = clipPos(doc, Pos(newPos.line - 1));
2532 if (newPos.line > doc.first) newPos = clipPos(doc, Pos(newPos.line - 1));
2486 else newPos = null;
2533 else newPos = null;
2487 } else if (newPos.ch > line.text.length) {
2534 } else if (newPos.ch > line.text.length) {
2488 if (newPos.line < doc.first + doc.size - 1) newPos = Pos(newPos.line + 1, 0);
2535 if (newPos.line < doc.first + doc.size - 1) newPos = Pos(newPos.line + 1, 0);
2489 else newPos = null;
2536 else newPos = null;
2490 }
2537 }
2491 if (!newPos) {
2538 if (!newPos) {
2492 if (flipped) {
2539 if (flipped) {
2493 // Driven in a corner -- no valid cursor position found at all
2540 // Driven in a corner -- no valid cursor position found at all
2494 // -- try again *with* clearing, if we didn't already
2541 // -- try again *with* clearing, if we didn't already
2495 if (!mayClear) return skipAtomic(doc, pos, bias, true);
2542 if (!mayClear) return skipAtomic(doc, pos, bias, true);
2496 // Otherwise, turn off editing until further notice, and return the start of the doc
2543 // Otherwise, turn off editing until further notice, and return the start of the doc
2497 doc.cantEdit = true;
2544 doc.cantEdit = true;
2498 return Pos(doc.first, 0);
2545 return Pos(doc.first, 0);
2499 }
2546 }
2500 flipped = true; newPos = pos; dir = -dir;
2547 flipped = true; newPos = pos; dir = -dir;
2501 }
2548 }
2502 }
2549 }
2503 curPos = newPos;
2550 curPos = newPos;
2504 continue search;
2551 continue search;
2505 }
2552 }
2506 }
2553 }
2507 }
2554 }
2508 return curPos;
2555 return curPos;
2509 }
2556 }
2510 }
2557 }
2511
2558
2512 // SCROLLING
2559 // SCROLLING
2513
2560
2514 function scrollCursorIntoView(cm) {
2561 function scrollCursorIntoView(cm) {
2515 var coords = scrollPosIntoView(cm, cm.doc.sel.head);
2562 var coords = scrollPosIntoView(cm, cm.doc.sel.head, cm.options.cursorScrollMargin);
2516 if (!cm.state.focused) return;
2563 if (!cm.state.focused) return;
2517 var display = cm.display, box = getRect(display.sizer), doScroll = null, pTop = paddingTop(cm.display);
2564 var display = cm.display, box = getRect(display.sizer), doScroll = null;
2518 if (coords.top + pTop + box.top < 0) doScroll = true;
2565 if (coords.top + box.top < 0) doScroll = true;
2519 else if (coords.bottom + pTop + box.top > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false;
2566 else if (coords.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false;
2520 if (doScroll != null && !phantom) {
2567 if (doScroll != null && !phantom) {
2521 var hidden = display.cursor.style.display == "none";
2568 var hidden = display.cursor.style.display == "none";
2522 if (hidden) {
2569 if (hidden) {
2523 display.cursor.style.display = "";
2570 display.cursor.style.display = "";
2524 display.cursor.style.left = coords.left + "px";
2571 display.cursor.style.left = coords.left + "px";
2525 display.cursor.style.top = (coords.top - display.viewOffset) + "px";
2572 display.cursor.style.top = (coords.top - display.viewOffset) + "px";
2526 }
2573 }
2527 display.cursor.scrollIntoView(doScroll);
2574 display.cursor.scrollIntoView(doScroll);
2528 if (hidden) display.cursor.style.display = "none";
2575 if (hidden) display.cursor.style.display = "none";
2529 }
2576 }
2530 }
2577 }
2531
2578
2532 function scrollPosIntoView(cm, pos, margin) {
2579 function scrollPosIntoView(cm, pos, margin) {
2533 if (margin == null) margin = 0;
2580 if (margin == null) margin = 0;
2534 for (;;) {
2581 for (;;) {
2535 var changed = false, coords = cursorCoords(cm, pos);
2582 var changed = false, coords = cursorCoords(cm, pos);
2536 var scrollPos = calculateScrollPos(cm, coords.left, coords.top - margin, coords.left, coords.bottom + margin);
2583 var scrollPos = calculateScrollPos(cm, coords.left, coords.top - margin, coords.left, coords.bottom + margin);
2537 var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft;
2584 var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft;
2538 if (scrollPos.scrollTop != null) {
2585 if (scrollPos.scrollTop != null) {
2539 setScrollTop(cm, scrollPos.scrollTop);
2586 setScrollTop(cm, scrollPos.scrollTop);
2540 if (Math.abs(cm.doc.scrollTop - startTop) > 1) changed = true;
2587 if (Math.abs(cm.doc.scrollTop - startTop) > 1) changed = true;
2541 }
2588 }
2542 if (scrollPos.scrollLeft != null) {
2589 if (scrollPos.scrollLeft != null) {
2543 setScrollLeft(cm, scrollPos.scrollLeft);
2590 setScrollLeft(cm, scrollPos.scrollLeft);
2544 if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) changed = true;
2591 if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) changed = true;
2545 }
2592 }
2546 if (!changed) return coords;
2593 if (!changed) return coords;
2547 }
2594 }
2548 }
2595 }
2549
2596
2550 function scrollIntoView(cm, x1, y1, x2, y2) {
2597 function scrollIntoView(cm, x1, y1, x2, y2) {
2551 var scrollPos = calculateScrollPos(cm, x1, y1, x2, y2);
2598 var scrollPos = calculateScrollPos(cm, x1, y1, x2, y2);
2552 if (scrollPos.scrollTop != null) setScrollTop(cm, scrollPos.scrollTop);
2599 if (scrollPos.scrollTop != null) setScrollTop(cm, scrollPos.scrollTop);
2553 if (scrollPos.scrollLeft != null) setScrollLeft(cm, scrollPos.scrollLeft);
2600 if (scrollPos.scrollLeft != null) setScrollLeft(cm, scrollPos.scrollLeft);
2554 }
2601 }
2555
2602
2556 function calculateScrollPos(cm, x1, y1, x2, y2) {
2603 function calculateScrollPos(cm, x1, y1, x2, y2) {
2557 var display = cm.display, pt = paddingTop(display);
2604 var display = cm.display, snapMargin = textHeight(cm.display);
2558 y1 += pt; y2 += pt;
2559 if (y1 < 0) y1 = 0;
2605 if (y1 < 0) y1 = 0;
2560 var screen = display.scroller.clientHeight - scrollerCutOff, screentop = display.scroller.scrollTop, result = {};
2606 var screen = display.scroller.clientHeight - scrollerCutOff, screentop = display.scroller.scrollTop, result = {};
2561 var docBottom = cm.doc.height + paddingVert(display);
2607 var docBottom = cm.doc.height + paddingVert(display);
2562 var atTop = y1 < pt + 10, atBottom = y2 + pt > docBottom - 10;
2608 var atTop = y1 < snapMargin, atBottom = y2 > docBottom - snapMargin;
2563 if (y1 < screentop) {
2609 if (y1 < screentop) {
2564 result.scrollTop = atTop ? 0 : y1;
2610 result.scrollTop = atTop ? 0 : y1;
2565 } else if (y2 > screentop + screen) {
2611 } else if (y2 > screentop + screen) {
2566 var newTop = Math.min(y1, (atBottom ? docBottom : y2) - screen);
2612 var newTop = Math.min(y1, (atBottom ? docBottom : y2) - screen);
2567 if (newTop != screentop) result.scrollTop = newTop;
2613 if (newTop != screentop) result.scrollTop = newTop;
2568 }
2614 }
2569
2615
2570 var screenw = display.scroller.clientWidth - scrollerCutOff, screenleft = display.scroller.scrollLeft;
2616 var screenw = display.scroller.clientWidth - scrollerCutOff, screenleft = display.scroller.scrollLeft;
2571 x1 += display.gutters.offsetWidth; x2 += display.gutters.offsetWidth;
2617 x1 += display.gutters.offsetWidth; x2 += display.gutters.offsetWidth;
2572 var gutterw = display.gutters.offsetWidth;
2618 var gutterw = display.gutters.offsetWidth;
2573 var atLeft = x1 < gutterw + 10;
2619 var atLeft = x1 < gutterw + 10;
2574 if (x1 < screenleft + gutterw || atLeft) {
2620 if (x1 < screenleft + gutterw || atLeft) {
2575 if (atLeft) x1 = 0;
2621 if (atLeft) x1 = 0;
2576 result.scrollLeft = Math.max(0, x1 - 10 - gutterw);
2622 result.scrollLeft = Math.max(0, x1 - 10 - gutterw);
2577 } else if (x2 > screenw + screenleft - 3) {
2623 } else if (x2 > screenw + screenleft - 3) {
2578 result.scrollLeft = x2 + 10 - screenw;
2624 result.scrollLeft = x2 + 10 - screenw;
2579 }
2625 }
2580 return result;
2626 return result;
2581 }
2627 }
2582
2628
2583 function updateScrollPos(cm, left, top) {
2629 function updateScrollPos(cm, left, top) {
2584 cm.curOp.updateScrollPos = {scrollLeft: left == null ? cm.doc.scrollLeft : left,
2630 cm.curOp.updateScrollPos = {scrollLeft: left == null ? cm.doc.scrollLeft : left,
2585 scrollTop: top == null ? cm.doc.scrollTop : top};
2631 scrollTop: top == null ? cm.doc.scrollTop : top};
2586 }
2632 }
2587
2633
2588 function addToScrollPos(cm, left, top) {
2634 function addToScrollPos(cm, left, top) {
2589 var pos = cm.curOp.updateScrollPos || (cm.curOp.updateScrollPos = {scrollLeft: cm.doc.scrollLeft, scrollTop: cm.doc.scrollTop});
2635 var pos = cm.curOp.updateScrollPos || (cm.curOp.updateScrollPos = {scrollLeft: cm.doc.scrollLeft, scrollTop: cm.doc.scrollTop});
2590 var scroll = cm.display.scroller;
2636 var scroll = cm.display.scroller;
2591 pos.scrollTop = Math.max(0, Math.min(scroll.scrollHeight - scroll.clientHeight, pos.scrollTop + top));
2637 pos.scrollTop = Math.max(0, Math.min(scroll.scrollHeight - scroll.clientHeight, pos.scrollTop + top));
2592 pos.scrollLeft = Math.max(0, Math.min(scroll.scrollWidth - scroll.clientWidth, pos.scrollLeft + left));
2638 pos.scrollLeft = Math.max(0, Math.min(scroll.scrollWidth - scroll.clientWidth, pos.scrollLeft + left));
2593 }
2639 }
2594
2640
2595 // API UTILITIES
2641 // API UTILITIES
2596
2642
2597 function indentLine(cm, n, how, aggressive) {
2643 function indentLine(cm, n, how, aggressive) {
2598 var doc = cm.doc;
2644 var doc = cm.doc;
2599 if (!how) how = "add";
2645 if (how == null) how = "add";
2600 if (how == "smart") {
2646 if (how == "smart") {
2601 if (!cm.doc.mode.indent) how = "prev";
2647 if (!cm.doc.mode.indent) how = "prev";
2602 else var state = getStateBefore(cm, n);
2648 else var state = getStateBefore(cm, n);
2603 }
2649 }
2604
2650
2605 var tabSize = cm.options.tabSize;
2651 var tabSize = cm.options.tabSize;
2606 var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize);
2652 var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize);
2607 var curSpaceString = line.text.match(/^\s*/)[0], indentation;
2653 var curSpaceString = line.text.match(/^\s*/)[0], indentation;
2608 if (how == "smart") {
2654 if (how == "smart") {
2609 indentation = cm.doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text);
2655 indentation = cm.doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text);
2610 if (indentation == Pass) {
2656 if (indentation == Pass) {
2611 if (!aggressive) return;
2657 if (!aggressive) return;
2612 how = "prev";
2658 how = "prev";
2613 }
2659 }
2614 }
2660 }
2615 if (how == "prev") {
2661 if (how == "prev") {
2616 if (n > doc.first) indentation = countColumn(getLine(doc, n-1).text, null, tabSize);
2662 if (n > doc.first) indentation = countColumn(getLine(doc, n-1).text, null, tabSize);
2617 else indentation = 0;
2663 else indentation = 0;
2618 } else if (how == "add") {
2664 } else if (how == "add") {
2619 indentation = curSpace + cm.options.indentUnit;
2665 indentation = curSpace + cm.options.indentUnit;
2620 } else if (how == "subtract") {
2666 } else if (how == "subtract") {
2621 indentation = curSpace - cm.options.indentUnit;
2667 indentation = curSpace - cm.options.indentUnit;
2668 } else if (typeof how == "number") {
2669 indentation = curSpace + how;
2622 }
2670 }
2623 indentation = Math.max(0, indentation);
2671 indentation = Math.max(0, indentation);
2624
2672
2625 var indentString = "", pos = 0;
2673 var indentString = "", pos = 0;
2626 if (cm.options.indentWithTabs)
2674 if (cm.options.indentWithTabs)
2627 for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";}
2675 for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";}
2628 if (pos < indentation) indentString += spaceStr(indentation - pos);
2676 if (pos < indentation) indentString += spaceStr(indentation - pos);
2629
2677
2630 if (indentString != curSpaceString)
2678 if (indentString != curSpaceString)
2631 replaceRange(cm.doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input");
2679 replaceRange(cm.doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input");
2632 line.stateAfter = null;
2680 line.stateAfter = null;
2633 }
2681 }
2634
2682
2635 function changeLine(cm, handle, op) {
2683 function changeLine(cm, handle, op) {
2636 var no = handle, line = handle, doc = cm.doc;
2684 var no = handle, line = handle, doc = cm.doc;
2637 if (typeof handle == "number") line = getLine(doc, clipLine(doc, handle));
2685 if (typeof handle == "number") line = getLine(doc, clipLine(doc, handle));
2638 else no = lineNo(handle);
2686 else no = lineNo(handle);
2639 if (no == null) return null;
2687 if (no == null) return null;
2640 if (op(line, no)) regChange(cm, no, no + 1);
2688 if (op(line, no)) regChange(cm, no, no + 1);
2641 else return null;
2689 else return null;
2642 return line;
2690 return line;
2643 }
2691 }
2644
2692
2645 function findPosH(doc, pos, dir, unit, visually) {
2693 function findPosH(doc, pos, dir, unit, visually) {
2646 var line = pos.line, ch = pos.ch;
2694 var line = pos.line, ch = pos.ch, origDir = dir;
2647 var lineObj = getLine(doc, line);
2695 var lineObj = getLine(doc, line);
2648 var possible = true;
2696 var possible = true;
2649 function findNextLine() {
2697 function findNextLine() {
2650 var l = line + dir;
2698 var l = line + dir;
2651 if (l < doc.first || l >= doc.first + doc.size) return (possible = false);
2699 if (l < doc.first || l >= doc.first + doc.size) return (possible = false);
2652 line = l;
2700 line = l;
2653 return lineObj = getLine(doc, l);
2701 return lineObj = getLine(doc, l);
2654 }
2702 }
2655 function moveOnce(boundToLine) {
2703 function moveOnce(boundToLine) {
2656 var next = (visually ? moveVisually : moveLogically)(lineObj, ch, dir, true);
2704 var next = (visually ? moveVisually : moveLogically)(lineObj, ch, dir, true);
2657 if (next == null) {
2705 if (next == null) {
2658 if (!boundToLine && findNextLine()) {
2706 if (!boundToLine && findNextLine()) {
2659 if (visually) ch = (dir < 0 ? lineRight : lineLeft)(lineObj);
2707 if (visually) ch = (dir < 0 ? lineRight : lineLeft)(lineObj);
2660 else ch = dir < 0 ? lineObj.text.length : 0;
2708 else ch = dir < 0 ? lineObj.text.length : 0;
2661 } else return (possible = false);
2709 } else return (possible = false);
2662 } else ch = next;
2710 } else ch = next;
2663 return true;
2711 return true;
2664 }
2712 }
2665
2713
2666 if (unit == "char") moveOnce();
2714 if (unit == "char") moveOnce();
2667 else if (unit == "column") moveOnce(true);
2715 else if (unit == "column") moveOnce(true);
2668 else if (unit == "word" || unit == "group") {
2716 else if (unit == "word" || unit == "group") {
2669 var sawType = null, group = unit == "group";
2717 var sawType = null, group = unit == "group";
2670 for (var first = true;; first = false) {
2718 for (var first = true;; first = false) {
2671 if (dir < 0 && !moveOnce(!first)) break;
2719 if (dir < 0 && !moveOnce(!first)) break;
2672 var cur = lineObj.text.charAt(ch) || "\n";
2720 var cur = lineObj.text.charAt(ch) || "\n";
2673 var type = isWordChar(cur) ? "w"
2721 var type = isWordChar(cur) ? "w"
2674 : !group ? null
2722 : !group ? null
2675 : /\s/.test(cur) ? null
2723 : /\s/.test(cur) ? null
2676 : "p";
2724 : "p";
2677 if (sawType && sawType != type) {
2725 if (sawType && sawType != type) {
2678 if (dir < 0) {dir = 1; moveOnce();}
2726 if (dir < 0) {dir = 1; moveOnce();}
2679 break;
2727 break;
2680 }
2728 }
2681 if (type) sawType = type;
2729 if (type) sawType = type;
2682 if (dir > 0 && !moveOnce(!first)) break;
2730 if (dir > 0 && !moveOnce(!first)) break;
2683 }
2731 }
2684 }
2732 }
2685 var result = skipAtomic(doc, Pos(line, ch), dir, true);
2733 var result = skipAtomic(doc, Pos(line, ch), origDir, true);
2686 if (!possible) result.hitSide = true;
2734 if (!possible) result.hitSide = true;
2687 return result;
2735 return result;
2688 }
2736 }
2689
2737
2690 function findPosV(cm, pos, dir, unit) {
2738 function findPosV(cm, pos, dir, unit) {
2691 var doc = cm.doc, x = pos.left, y;
2739 var doc = cm.doc, x = pos.left, y;
2692 if (unit == "page") {
2740 if (unit == "page") {
2693 var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight);
2741 var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight);
2694 y = pos.top + dir * (pageSize - (dir < 0 ? 1.5 : .5) * textHeight(cm.display));
2742 y = pos.top + dir * (pageSize - (dir < 0 ? 1.5 : .5) * textHeight(cm.display));
2695 } else if (unit == "line") {
2743 } else if (unit == "line") {
2696 y = dir > 0 ? pos.bottom + 3 : pos.top - 3;
2744 y = dir > 0 ? pos.bottom + 3 : pos.top - 3;
2697 }
2745 }
2698 for (;;) {
2746 for (;;) {
2699 var target = coordsChar(cm, x, y);
2747 var target = coordsChar(cm, x, y);
2700 if (!target.outside) break;
2748 if (!target.outside) break;
2701 if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break; }
2749 if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break; }
2702 y += dir * 5;
2750 y += dir * 5;
2703 }
2751 }
2704 return target;
2752 return target;
2705 }
2753 }
2706
2754
2707 function findWordAt(line, pos) {
2755 function findWordAt(line, pos) {
2708 var start = pos.ch, end = pos.ch;
2756 var start = pos.ch, end = pos.ch;
2709 if (line) {
2757 if (line) {
2710 if (pos.after === false || end == line.length) --start; else ++end;
2758 if (pos.xRel < 0 || end == line.length) --start; else ++end;
2711 var startChar = line.charAt(start);
2759 var startChar = line.charAt(start);
2712 var check = isWordChar(startChar) ? isWordChar
2760 var check = isWordChar(startChar) ? isWordChar
2713 : /\s/.test(startChar) ? function(ch) {return /\s/.test(ch);}
2761 : /\s/.test(startChar) ? function(ch) {return /\s/.test(ch);}
2714 : function(ch) {return !/\s/.test(ch) && !isWordChar(ch);};
2762 : function(ch) {return !/\s/.test(ch) && !isWordChar(ch);};
2715 while (start > 0 && check(line.charAt(start - 1))) --start;
2763 while (start > 0 && check(line.charAt(start - 1))) --start;
2716 while (end < line.length && check(line.charAt(end))) ++end;
2764 while (end < line.length && check(line.charAt(end))) ++end;
2717 }
2765 }
2718 return {from: Pos(pos.line, start), to: Pos(pos.line, end)};
2766 return {from: Pos(pos.line, start), to: Pos(pos.line, end)};
2719 }
2767 }
2720
2768
2721 function selectLine(cm, line) {
2769 function selectLine(cm, line) {
2722 extendSelection(cm.doc, Pos(line, 0), clipPos(cm.doc, Pos(line + 1, 0)));
2770 extendSelection(cm.doc, Pos(line, 0), clipPos(cm.doc, Pos(line + 1, 0)));
2723 }
2771 }
2724
2772
2725 // PROTOTYPE
2773 // PROTOTYPE
2726
2774
2727 // The publicly visible API. Note that operation(null, f) means
2775 // The publicly visible API. Note that operation(null, f) means
2728 // 'wrap f in an operation, performed on its `this` parameter'
2776 // 'wrap f in an operation, performed on its `this` parameter'
2729
2777
2730 CodeMirror.prototype = {
2778 CodeMirror.prototype = {
2779 constructor: CodeMirror,
2731 focus: function(){window.focus(); focusInput(this); onFocus(this); fastPoll(this);},
2780 focus: function(){window.focus(); focusInput(this); onFocus(this); fastPoll(this);},
2732
2781
2733 setOption: function(option, value) {
2782 setOption: function(option, value) {
2734 var options = this.options, old = options[option];
2783 var options = this.options, old = options[option];
2735 if (options[option] == value && option != "mode") return;
2784 if (options[option] == value && option != "mode") return;
2736 options[option] = value;
2785 options[option] = value;
2737 if (optionHandlers.hasOwnProperty(option))
2786 if (optionHandlers.hasOwnProperty(option))
2738 operation(this, optionHandlers[option])(this, value, old);
2787 operation(this, optionHandlers[option])(this, value, old);
2739 },
2788 },
2740
2789
2741 getOption: function(option) {return this.options[option];},
2790 getOption: function(option) {return this.options[option];},
2742 getDoc: function() {return this.doc;},
2791 getDoc: function() {return this.doc;},
2743
2792
2744 addKeyMap: function(map, bottom) {
2793 addKeyMap: function(map, bottom) {
2745 this.state.keyMaps[bottom ? "push" : "unshift"](map);
2794 this.state.keyMaps[bottom ? "push" : "unshift"](map);
2746 },
2795 },
2747 removeKeyMap: function(map) {
2796 removeKeyMap: function(map) {
2748 var maps = this.state.keyMaps;
2797 var maps = this.state.keyMaps;
2749 for (var i = 0; i < maps.length; ++i)
2798 for (var i = 0; i < maps.length; ++i)
2750 if ((typeof map == "string" ? maps[i].name : maps[i]) == map) {
2799 if ((typeof map == "string" ? maps[i].name : maps[i]) == map) {
2751 maps.splice(i, 1);
2800 maps.splice(i, 1);
2752 return true;
2801 return true;
2753 }
2802 }
2754 },
2803 },
2755
2804
2756 addOverlay: operation(null, function(spec, options) {
2805 addOverlay: operation(null, function(spec, options) {
2757 var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec);
2806 var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec);
2758 if (mode.startState) throw new Error("Overlays may not be stateful.");
2807 if (mode.startState) throw new Error("Overlays may not be stateful.");
2759 this.state.overlays.push({mode: mode, modeSpec: spec, opaque: options && options.opaque});
2808 this.state.overlays.push({mode: mode, modeSpec: spec, opaque: options && options.opaque});
2760 this.state.modeGen++;
2809 this.state.modeGen++;
2761 regChange(this);
2810 regChange(this);
2762 }),
2811 }),
2763 removeOverlay: operation(null, function(spec) {
2812 removeOverlay: operation(null, function(spec) {
2764 var overlays = this.state.overlays;
2813 var overlays = this.state.overlays;
2765 for (var i = 0; i < overlays.length; ++i) {
2814 for (var i = 0; i < overlays.length; ++i) {
2766 if (overlays[i].modeSpec == spec) {
2815 var cur = overlays[i].modeSpec;
2816 if (cur == spec || typeof spec == "string" && cur.name == spec) {
2767 overlays.splice(i, 1);
2817 overlays.splice(i, 1);
2768 this.state.modeGen++;
2818 this.state.modeGen++;
2769 regChange(this);
2819 regChange(this);
2770 return;
2820 return;
2771 }
2821 }
2772 }
2822 }
2773 }),
2823 }),
2774
2824
2775 indentLine: operation(null, function(n, dir, aggressive) {
2825 indentLine: operation(null, function(n, dir, aggressive) {
2776 if (typeof dir != "string") {
2826 if (typeof dir != "string" && typeof dir != "number") {
2777 if (dir == null) dir = this.options.smartIndent ? "smart" : "prev";
2827 if (dir == null) dir = this.options.smartIndent ? "smart" : "prev";
2778 else dir = dir ? "add" : "subtract";
2828 else dir = dir ? "add" : "subtract";
2779 }
2829 }
2780 if (isLine(this.doc, n)) indentLine(this, n, dir, aggressive);
2830 if (isLine(this.doc, n)) indentLine(this, n, dir, aggressive);
2781 }),
2831 }),
2782 indentSelection: operation(null, function(how) {
2832 indentSelection: operation(null, function(how) {
2783 var sel = this.doc.sel;
2833 var sel = this.doc.sel;
2784 if (posEq(sel.from, sel.to)) return indentLine(this, sel.from.line, how);
2834 if (posEq(sel.from, sel.to)) return indentLine(this, sel.from.line, how);
2785 var e = sel.to.line - (sel.to.ch ? 0 : 1);
2835 var e = sel.to.line - (sel.to.ch ? 0 : 1);
2786 for (var i = sel.from.line; i <= e; ++i) indentLine(this, i, how);
2836 for (var i = sel.from.line; i <= e; ++i) indentLine(this, i, how);
2787 }),
2837 }),
2788
2838
2789 // Fetch the parser token for a given character. Useful for hacks
2839 // Fetch the parser token for a given character. Useful for hacks
2790 // that want to inspect the mode state (say, for completion).
2840 // that want to inspect the mode state (say, for completion).
2791 getTokenAt: function(pos) {
2841 getTokenAt: function(pos, precise) {
2792 var doc = this.doc;
2842 var doc = this.doc;
2793 pos = clipPos(doc, pos);
2843 pos = clipPos(doc, pos);
2794 var state = getStateBefore(this, pos.line), mode = this.doc.mode;
2844 var state = getStateBefore(this, pos.line, precise), mode = this.doc.mode;
2795 var line = getLine(doc, pos.line);
2845 var line = getLine(doc, pos.line);
2796 var stream = new StringStream(line.text, this.options.tabSize);
2846 var stream = new StringStream(line.text, this.options.tabSize);
2797 while (stream.pos < pos.ch && !stream.eol()) {
2847 while (stream.pos < pos.ch && !stream.eol()) {
2798 stream.start = stream.pos;
2848 stream.start = stream.pos;
2799 var style = mode.token(stream, state);
2849 var style = mode.token(stream, state);
2800 }
2850 }
2801 return {start: stream.start,
2851 return {start: stream.start,
2802 end: stream.pos,
2852 end: stream.pos,
2803 string: stream.current(),
2853 string: stream.current(),
2804 className: style || null, // Deprecated, use 'type' instead
2854 className: style || null, // Deprecated, use 'type' instead
2805 type: style || null,
2855 type: style || null,
2806 state: state};
2856 state: state};
2807 },
2857 },
2808
2858
2809 getStateAfter: function(line) {
2859 getTokenTypeAt: function(pos) {
2860 pos = clipPos(this.doc, pos);
2861 var styles = getLineStyles(this, getLine(this.doc, pos.line));
2862 var before = 0, after = (styles.length - 1) / 2, ch = pos.ch;
2863 for (;;) {
2864 var mid = (before + after) >> 1;
2865 if ((mid ? styles[mid * 2 - 1] : 0) >= ch) after = mid;
2866 else if (styles[mid * 2 + 1] < ch) before = mid + 1;
2867 else return styles[mid * 2 + 2];
2868 }
2869 },
2870
2871 getStateAfter: function(line, precise) {
2810 var doc = this.doc;
2872 var doc = this.doc;
2811 line = clipLine(doc, line == null ? doc.first + doc.size - 1: line);
2873 line = clipLine(doc, line == null ? doc.first + doc.size - 1: line);
2812 return getStateBefore(this, line + 1);
2874 return getStateBefore(this, line + 1, precise);
2813 },
2875 },
2814
2876
2815 cursorCoords: function(start, mode) {
2877 cursorCoords: function(start, mode) {
2816 var pos, sel = this.doc.sel;
2878 var pos, sel = this.doc.sel;
2817 if (start == null) pos = sel.head;
2879 if (start == null) pos = sel.head;
2818 else if (typeof start == "object") pos = clipPos(this.doc, start);
2880 else if (typeof start == "object") pos = clipPos(this.doc, start);
2819 else pos = start ? sel.from : sel.to;
2881 else pos = start ? sel.from : sel.to;
2820 return cursorCoords(this, pos, mode || "page");
2882 return cursorCoords(this, pos, mode || "page");
2821 },
2883 },
2822
2884
2823 charCoords: function(pos, mode) {
2885 charCoords: function(pos, mode) {
2824 return charCoords(this, clipPos(this.doc, pos), mode || "page");
2886 return charCoords(this, clipPos(this.doc, pos), mode || "page");
2825 },
2887 },
2826
2888
2827 coordsChar: function(coords, mode) {
2889 coordsChar: function(coords, mode) {
2828 coords = fromCoordSystem(this, coords, mode || "page");
2890 coords = fromCoordSystem(this, coords, mode || "page");
2829 return coordsChar(this, coords.left, coords.top);
2891 return coordsChar(this, coords.left, coords.top);
2830 },
2892 },
2831
2893
2894 lineAtHeight: function(height, mode) {
2895 height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top;
2896 return lineAtHeight(this.doc, height + this.display.viewOffset);
2897 },
2898 heightAtLine: function(line, mode) {
2899 var end = false, last = this.doc.first + this.doc.size - 1;
2900 if (line < this.doc.first) line = this.doc.first;
2901 else if (line > last) { line = last; end = true; }
2902 var lineObj = getLine(this.doc, line);
2903 return intoCoordSystem(this, getLine(this.doc, line), {top: 0, left: 0}, mode || "page").top +
2904 (end ? lineObj.height : 0);
2905 },
2906
2832 defaultTextHeight: function() { return textHeight(this.display); },
2907 defaultTextHeight: function() { return textHeight(this.display); },
2833 defaultCharWidth: function() { return charWidth(this.display); },
2908 defaultCharWidth: function() { return charWidth(this.display); },
2834
2909
2835 setGutterMarker: operation(null, function(line, gutterID, value) {
2910 setGutterMarker: operation(null, function(line, gutterID, value) {
2836 return changeLine(this, line, function(line) {
2911 return changeLine(this, line, function(line) {
2837 var markers = line.gutterMarkers || (line.gutterMarkers = {});
2912 var markers = line.gutterMarkers || (line.gutterMarkers = {});
2838 markers[gutterID] = value;
2913 markers[gutterID] = value;
2839 if (!value && isEmpty(markers)) line.gutterMarkers = null;
2914 if (!value && isEmpty(markers)) line.gutterMarkers = null;
2840 return true;
2915 return true;
2841 });
2916 });
2842 }),
2917 }),
2843
2918
2844 clearGutter: operation(null, function(gutterID) {
2919 clearGutter: operation(null, function(gutterID) {
2845 var cm = this, doc = cm.doc, i = doc.first;
2920 var cm = this, doc = cm.doc, i = doc.first;
2846 doc.iter(function(line) {
2921 doc.iter(function(line) {
2847 if (line.gutterMarkers && line.gutterMarkers[gutterID]) {
2922 if (line.gutterMarkers && line.gutterMarkers[gutterID]) {
2848 line.gutterMarkers[gutterID] = null;
2923 line.gutterMarkers[gutterID] = null;
2849 regChange(cm, i, i + 1);
2924 regChange(cm, i, i + 1);
2850 if (isEmpty(line.gutterMarkers)) line.gutterMarkers = null;
2925 if (isEmpty(line.gutterMarkers)) line.gutterMarkers = null;
2851 }
2926 }
2852 ++i;
2927 ++i;
2853 });
2928 });
2854 }),
2929 }),
2855
2930
2856 addLineClass: operation(null, function(handle, where, cls) {
2931 addLineClass: operation(null, function(handle, where, cls) {
2857 return changeLine(this, handle, function(line) {
2932 return changeLine(this, handle, function(line) {
2858 var prop = where == "text" ? "textClass" : where == "background" ? "bgClass" : "wrapClass";
2933 var prop = where == "text" ? "textClass" : where == "background" ? "bgClass" : "wrapClass";
2859 if (!line[prop]) line[prop] = cls;
2934 if (!line[prop]) line[prop] = cls;
2860 else if (new RegExp("\\b" + cls + "\\b").test(line[prop])) return false;
2935 else if (new RegExp("(?:^|\\s)" + cls + "(?:$|\\s)").test(line[prop])) return false;
2861 else line[prop] += " " + cls;
2936 else line[prop] += " " + cls;
2862 return true;
2937 return true;
2863 });
2938 });
2864 }),
2939 }),
2865
2940
2866 removeLineClass: operation(null, function(handle, where, cls) {
2941 removeLineClass: operation(null, function(handle, where, cls) {
2867 return changeLine(this, handle, function(line) {
2942 return changeLine(this, handle, function(line) {
2868 var prop = where == "text" ? "textClass" : where == "background" ? "bgClass" : "wrapClass";
2943 var prop = where == "text" ? "textClass" : where == "background" ? "bgClass" : "wrapClass";
2869 var cur = line[prop];
2944 var cur = line[prop];
2870 if (!cur) return false;
2945 if (!cur) return false;
2871 else if (cls == null) line[prop] = null;
2946 else if (cls == null) line[prop] = null;
2872 else {
2947 else {
2873 var upd = cur.replace(new RegExp("^" + cls + "\\b\\s*|\\s*\\b" + cls + "\\b"), "");
2948 var found = cur.match(new RegExp("(?:^|\\s+)" + cls + "(?:$|\\s+)"));
2874 if (upd == cur) return false;
2949 if (!found) return false;
2875 line[prop] = upd || null;
2950 var end = found.index + found[0].length;
2951 line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null;
2876 }
2952 }
2877 return true;
2953 return true;
2878 });
2954 });
2879 }),
2955 }),
2880
2956
2881 addLineWidget: operation(null, function(handle, node, options) {
2957 addLineWidget: operation(null, function(handle, node, options) {
2882 return addLineWidget(this, handle, node, options);
2958 return addLineWidget(this, handle, node, options);
2883 }),
2959 }),
2884
2960
2885 removeLineWidget: function(widget) { widget.clear(); },
2961 removeLineWidget: function(widget) { widget.clear(); },
2886
2962
2887 lineInfo: function(line) {
2963 lineInfo: function(line) {
2888 if (typeof line == "number") {
2964 if (typeof line == "number") {
2889 if (!isLine(this.doc, line)) return null;
2965 if (!isLine(this.doc, line)) return null;
2890 var n = line;
2966 var n = line;
2891 line = getLine(this.doc, line);
2967 line = getLine(this.doc, line);
2892 if (!line) return null;
2968 if (!line) return null;
2893 } else {
2969 } else {
2894 var n = lineNo(line);
2970 var n = lineNo(line);
2895 if (n == null) return null;
2971 if (n == null) return null;
2896 }
2972 }
2897 return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers,
2973 return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers,
2898 textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass,
2974 textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass,
2899 widgets: line.widgets};
2975 widgets: line.widgets};
2900 },
2976 },
2901
2977
2902 getViewport: function() { return {from: this.display.showingFrom, to: this.display.showingTo};},
2978 getViewport: function() { return {from: this.display.showingFrom, to: this.display.showingTo};},
2903
2979
2904 addWidget: function(pos, node, scroll, vert, horiz) {
2980 addWidget: function(pos, node, scroll, vert, horiz) {
2905 var display = this.display;
2981 var display = this.display;
2906 pos = cursorCoords(this, clipPos(this.doc, pos));
2982 pos = cursorCoords(this, clipPos(this.doc, pos));
2907 var top = pos.bottom, left = pos.left;
2983 var top = pos.bottom, left = pos.left;
2908 node.style.position = "absolute";
2984 node.style.position = "absolute";
2909 display.sizer.appendChild(node);
2985 display.sizer.appendChild(node);
2910 if (vert == "over") {
2986 if (vert == "over") {
2911 top = pos.top;
2987 top = pos.top;
2912 } else if (vert == "above" || vert == "near") {
2988 } else if (vert == "above" || vert == "near") {
2913 var vspace = Math.max(display.wrapper.clientHeight, this.doc.height),
2989 var vspace = Math.max(display.wrapper.clientHeight, this.doc.height),
2914 hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth);
2990 hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth);
2915 // Default to positioning above (if specified and possible); otherwise default to positioning below
2991 // Default to positioning above (if specified and possible); otherwise default to positioning below
2916 if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight)
2992 if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight)
2917 top = pos.top - node.offsetHeight;
2993 top = pos.top - node.offsetHeight;
2918 else if (pos.bottom + node.offsetHeight <= vspace)
2994 else if (pos.bottom + node.offsetHeight <= vspace)
2919 top = pos.bottom;
2995 top = pos.bottom;
2920 if (left + node.offsetWidth > hspace)
2996 if (left + node.offsetWidth > hspace)
2921 left = hspace - node.offsetWidth;
2997 left = hspace - node.offsetWidth;
2922 }
2998 }
2923 node.style.top = (top + paddingTop(display)) + "px";
2999 node.style.top = top + "px";
2924 node.style.left = node.style.right = "";
3000 node.style.left = node.style.right = "";
2925 if (horiz == "right") {
3001 if (horiz == "right") {
2926 left = display.sizer.clientWidth - node.offsetWidth;
3002 left = display.sizer.clientWidth - node.offsetWidth;
2927 node.style.right = "0px";
3003 node.style.right = "0px";
2928 } else {
3004 } else {
2929 if (horiz == "left") left = 0;
3005 if (horiz == "left") left = 0;
2930 else if (horiz == "middle") left = (display.sizer.clientWidth - node.offsetWidth) / 2;
3006 else if (horiz == "middle") left = (display.sizer.clientWidth - node.offsetWidth) / 2;
2931 node.style.left = left + "px";
3007 node.style.left = left + "px";
2932 }
3008 }
2933 if (scroll)
3009 if (scroll)
2934 scrollIntoView(this, left, top, left + node.offsetWidth, top + node.offsetHeight);
3010 scrollIntoView(this, left, top, left + node.offsetWidth, top + node.offsetHeight);
2935 },
3011 },
2936
3012
2937 triggerOnKeyDown: operation(null, onKeyDown),
3013 triggerOnKeyDown: operation(null, onKeyDown),
2938
3014
2939 execCommand: function(cmd) {return commands[cmd](this);},
3015 execCommand: function(cmd) {return commands[cmd](this);},
2940
3016
2941 findPosH: function(from, amount, unit, visually) {
3017 findPosH: function(from, amount, unit, visually) {
2942 var dir = 1;
3018 var dir = 1;
2943 if (amount < 0) { dir = -1; amount = -amount; }
3019 if (amount < 0) { dir = -1; amount = -amount; }
2944 for (var i = 0, cur = clipPos(this.doc, from); i < amount; ++i) {
3020 for (var i = 0, cur = clipPos(this.doc, from); i < amount; ++i) {
2945 cur = findPosH(this.doc, cur, dir, unit, visually);
3021 cur = findPosH(this.doc, cur, dir, unit, visually);
2946 if (cur.hitSide) break;
3022 if (cur.hitSide) break;
2947 }
3023 }
2948 return cur;
3024 return cur;
2949 },
3025 },
2950
3026
2951 moveH: operation(null, function(dir, unit) {
3027 moveH: operation(null, function(dir, unit) {
2952 var sel = this.doc.sel, pos;
3028 var sel = this.doc.sel, pos;
2953 if (sel.shift || sel.extend || posEq(sel.from, sel.to))
3029 if (sel.shift || sel.extend || posEq(sel.from, sel.to))
2954 pos = findPosH(this.doc, sel.head, dir, unit, this.options.rtlMoveVisually);
3030 pos = findPosH(this.doc, sel.head, dir, unit, this.options.rtlMoveVisually);
2955 else
3031 else
2956 pos = dir < 0 ? sel.from : sel.to;
3032 pos = dir < 0 ? sel.from : sel.to;
2957 extendSelection(this.doc, pos, pos, dir);
3033 extendSelection(this.doc, pos, pos, dir);
2958 }),
3034 }),
2959
3035
2960 deleteH: operation(null, function(dir, unit) {
3036 deleteH: operation(null, function(dir, unit) {
2961 var sel = this.doc.sel;
3037 var sel = this.doc.sel;
2962 if (!posEq(sel.from, sel.to)) replaceRange(this.doc, "", sel.from, sel.to, "+delete");
3038 if (!posEq(sel.from, sel.to)) replaceRange(this.doc, "", sel.from, sel.to, "+delete");
2963 else replaceRange(this.doc, "", sel.from, findPosH(this.doc, sel.head, dir, unit, false), "+delete");
3039 else replaceRange(this.doc, "", sel.from, findPosH(this.doc, sel.head, dir, unit, false), "+delete");
2964 this.curOp.userSelChange = true;
3040 this.curOp.userSelChange = true;
2965 }),
3041 }),
2966
3042
2967 findPosV: function(from, amount, unit, goalColumn) {
3043 findPosV: function(from, amount, unit, goalColumn) {
2968 var dir = 1, x = goalColumn;
3044 var dir = 1, x = goalColumn;
2969 if (amount < 0) { dir = -1; amount = -amount; }
3045 if (amount < 0) { dir = -1; amount = -amount; }
2970 for (var i = 0, cur = clipPos(this.doc, from); i < amount; ++i) {
3046 for (var i = 0, cur = clipPos(this.doc, from); i < amount; ++i) {
2971 var coords = cursorCoords(this, cur, "div");
3047 var coords = cursorCoords(this, cur, "div");
2972 if (x == null) x = coords.left;
3048 if (x == null) x = coords.left;
2973 else coords.left = x;
3049 else coords.left = x;
2974 cur = findPosV(this, coords, dir, unit);
3050 cur = findPosV(this, coords, dir, unit);
2975 if (cur.hitSide) break;
3051 if (cur.hitSide) break;
2976 }
3052 }
2977 return cur;
3053 return cur;
2978 },
3054 },
2979
3055
2980 moveV: operation(null, function(dir, unit) {
3056 moveV: operation(null, function(dir, unit) {
2981 var sel = this.doc.sel;
3057 var sel = this.doc.sel;
2982 var pos = cursorCoords(this, sel.head, "div");
3058 var pos = cursorCoords(this, sel.head, "div");
2983 if (sel.goalColumn != null) pos.left = sel.goalColumn;
3059 if (sel.goalColumn != null) pos.left = sel.goalColumn;
2984 var target = findPosV(this, pos, dir, unit);
3060 var target = findPosV(this, pos, dir, unit);
2985
3061
2986 if (unit == "page") addToScrollPos(this, 0, charCoords(this, target, "div").top - pos.top);
3062 if (unit == "page") addToScrollPos(this, 0, charCoords(this, target, "div").top - pos.top);
2987 extendSelection(this.doc, target, target, dir);
3063 extendSelection(this.doc, target, target, dir);
2988 sel.goalColumn = pos.left;
3064 sel.goalColumn = pos.left;
2989 }),
3065 }),
2990
3066
2991 toggleOverwrite: function() {
3067 toggleOverwrite: function(value) {
3068 if (value != null && value == this.state.overwrite) return;
2992 if (this.state.overwrite = !this.state.overwrite)
3069 if (this.state.overwrite = !this.state.overwrite)
2993 this.display.cursor.className += " CodeMirror-overwrite";
3070 this.display.cursor.className += " CodeMirror-overwrite";
2994 else
3071 else
2995 this.display.cursor.className = this.display.cursor.className.replace(" CodeMirror-overwrite", "");
3072 this.display.cursor.className = this.display.cursor.className.replace(" CodeMirror-overwrite", "");
2996 },
3073 },
2997 hasFocus: function() { return this.state.focused; },
3074 hasFocus: function() { return this.state.focused; },
2998
3075
2999 scrollTo: operation(null, function(x, y) {
3076 scrollTo: operation(null, function(x, y) {
3000 updateScrollPos(this, x, y);
3077 updateScrollPos(this, x, y);
3001 }),
3078 }),
3002 getScrollInfo: function() {
3079 getScrollInfo: function() {
3003 var scroller = this.display.scroller, co = scrollerCutOff;
3080 var scroller = this.display.scroller, co = scrollerCutOff;
3004 return {left: scroller.scrollLeft, top: scroller.scrollTop,
3081 return {left: scroller.scrollLeft, top: scroller.scrollTop,
3005 height: scroller.scrollHeight - co, width: scroller.scrollWidth - co,
3082 height: scroller.scrollHeight - co, width: scroller.scrollWidth - co,
3006 clientHeight: scroller.clientHeight - co, clientWidth: scroller.clientWidth - co};
3083 clientHeight: scroller.clientHeight - co, clientWidth: scroller.clientWidth - co};
3007 },
3084 },
3008
3085
3009 scrollIntoView: operation(null, function(pos, margin) {
3086 scrollIntoView: operation(null, function(pos, margin) {
3010 if (typeof pos == "number") pos = Pos(pos, 0);
3087 if (typeof pos == "number") pos = Pos(pos, 0);
3011 if (!margin) margin = 0;
3088 if (!margin) margin = 0;
3012 var coords = pos;
3089 var coords = pos;
3013
3090
3014 if (!pos || pos.line != null) {
3091 if (!pos || pos.line != null) {
3015 this.curOp.scrollToPos = pos ? clipPos(this.doc, pos) : this.doc.sel.head;
3092 this.curOp.scrollToPos = pos ? clipPos(this.doc, pos) : this.doc.sel.head;
3016 this.curOp.scrollToPosMargin = margin;
3093 this.curOp.scrollToPosMargin = margin;
3017 coords = cursorCoords(this, this.curOp.scrollToPos);
3094 coords = cursorCoords(this, this.curOp.scrollToPos);
3018 }
3095 }
3019 var sPos = calculateScrollPos(this, coords.left, coords.top - margin, coords.right, coords.bottom + margin);
3096 var sPos = calculateScrollPos(this, coords.left, coords.top - margin, coords.right, coords.bottom + margin);
3020 updateScrollPos(this, sPos.scrollLeft, sPos.scrollTop);
3097 updateScrollPos(this, sPos.scrollLeft, sPos.scrollTop);
3021 }),
3098 }),
3022
3099
3023 setSize: function(width, height) {
3100 setSize: function(width, height) {
3024 function interpret(val) {
3101 function interpret(val) {
3025 return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val;
3102 return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val;
3026 }
3103 }
3027 if (width != null) this.display.wrapper.style.width = interpret(width);
3104 if (width != null) this.display.wrapper.style.width = interpret(width);
3028 if (height != null) this.display.wrapper.style.height = interpret(height);
3105 if (height != null) this.display.wrapper.style.height = interpret(height);
3029 this.refresh();
3106 this.refresh();
3030 },
3107 },
3031
3108
3032 on: function(type, f) {on(this, type, f);},
3109 on: function(type, f) {on(this, type, f);},
3033 off: function(type, f) {off(this, type, f);},
3110 off: function(type, f) {off(this, type, f);},
3034
3111
3035 operation: function(f){return runInOp(this, f);},
3112 operation: function(f){return runInOp(this, f);},
3036
3113
3037 refresh: operation(null, function() {
3114 refresh: operation(null, function() {
3038 clearCaches(this);
3115 clearCaches(this);
3039 updateScrollPos(this, this.doc.scrollLeft, this.doc.scrollTop);
3116 updateScrollPos(this, this.doc.scrollLeft, this.doc.scrollTop);
3040 regChange(this);
3117 regChange(this);
3041 }),
3118 }),
3042
3119
3043 swapDoc: operation(null, function(doc) {
3120 swapDoc: operation(null, function(doc) {
3044 var old = this.doc;
3121 var old = this.doc;
3045 old.cm = null;
3122 old.cm = null;
3046 attachDoc(this, doc);
3123 attachDoc(this, doc);
3047 clearCaches(this);
3124 clearCaches(this);
3048 resetInput(this, true);
3125 resetInput(this, true);
3049 updateScrollPos(this, doc.scrollLeft, doc.scrollTop);
3126 updateScrollPos(this, doc.scrollLeft, doc.scrollTop);
3050 return old;
3127 return old;
3051 }),
3128 }),
3052
3129
3053 getInputField: function(){return this.display.input;},
3130 getInputField: function(){return this.display.input;},
3054 getWrapperElement: function(){return this.display.wrapper;},
3131 getWrapperElement: function(){return this.display.wrapper;},
3055 getScrollerElement: function(){return this.display.scroller;},
3132 getScrollerElement: function(){return this.display.scroller;},
3056 getGutterElement: function(){return this.display.gutters;}
3133 getGutterElement: function(){return this.display.gutters;}
3057 };
3134 };
3058
3135
3059 // OPTION DEFAULTS
3136 // OPTION DEFAULTS
3060
3137
3061 var optionHandlers = CodeMirror.optionHandlers = {};
3138 var optionHandlers = CodeMirror.optionHandlers = {};
3062
3139
3063 // The default configuration options.
3140 // The default configuration options.
3064 var defaults = CodeMirror.defaults = {};
3141 var defaults = CodeMirror.defaults = {};
3065
3142
3066 function option(name, deflt, handle, notOnInit) {
3143 function option(name, deflt, handle, notOnInit) {
3067 CodeMirror.defaults[name] = deflt;
3144 CodeMirror.defaults[name] = deflt;
3068 if (handle) optionHandlers[name] =
3145 if (handle) optionHandlers[name] =
3069 notOnInit ? function(cm, val, old) {if (old != Init) handle(cm, val, old);} : handle;
3146 notOnInit ? function(cm, val, old) {if (old != Init) handle(cm, val, old);} : handle;
3070 }
3147 }
3071
3148
3072 var Init = CodeMirror.Init = {toString: function(){return "CodeMirror.Init";}};
3149 var Init = CodeMirror.Init = {toString: function(){return "CodeMirror.Init";}};
3073
3150
3074 // These two are, on init, called from the constructor because they
3151 // These two are, on init, called from the constructor because they
3075 // have to be initialized before the editor can start at all.
3152 // have to be initialized before the editor can start at all.
3076 option("value", "", function(cm, val) {
3153 option("value", "", function(cm, val) {
3077 cm.setValue(val);
3154 cm.setValue(val);
3078 }, true);
3155 }, true);
3079 option("mode", null, function(cm, val) {
3156 option("mode", null, function(cm, val) {
3080 cm.doc.modeOption = val;
3157 cm.doc.modeOption = val;
3081 loadMode(cm);
3158 loadMode(cm);
3082 }, true);
3159 }, true);
3083
3160
3084 option("indentUnit", 2, loadMode, true);
3161 option("indentUnit", 2, loadMode, true);
3085 option("indentWithTabs", false);
3162 option("indentWithTabs", false);
3086 option("smartIndent", true);
3163 option("smartIndent", true);
3087 option("tabSize", 4, function(cm) {
3164 option("tabSize", 4, function(cm) {
3088 loadMode(cm);
3165 loadMode(cm);
3089 clearCaches(cm);
3166 clearCaches(cm);
3090 regChange(cm);
3167 regChange(cm);
3091 }, true);
3168 }, true);
3092 option("electricChars", true);
3169 option("electricChars", true);
3093 option("rtlMoveVisually", !windows);
3170 option("rtlMoveVisually", !windows);
3094
3171
3095 option("theme", "default", function(cm) {
3172 option("theme", "default", function(cm) {
3096 themeChanged(cm);
3173 themeChanged(cm);
3097 guttersChanged(cm);
3174 guttersChanged(cm);
3098 }, true);
3175 }, true);
3099 option("keyMap", "default", keyMapChanged);
3176 option("keyMap", "default", keyMapChanged);
3100 option("extraKeys", null);
3177 option("extraKeys", null);
3101
3178
3102 option("onKeyEvent", null);
3179 option("onKeyEvent", null);
3103 option("onDragEvent", null);
3180 option("onDragEvent", null);
3104
3181
3105 option("lineWrapping", false, wrappingChanged, true);
3182 option("lineWrapping", false, wrappingChanged, true);
3106 option("gutters", [], function(cm) {
3183 option("gutters", [], function(cm) {
3107 setGuttersForLineNumbers(cm.options);
3184 setGuttersForLineNumbers(cm.options);
3108 guttersChanged(cm);
3185 guttersChanged(cm);
3109 }, true);
3186 }, true);
3110 option("fixedGutter", true, function(cm, val) {
3187 option("fixedGutter", true, function(cm, val) {
3111 cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0";
3188 cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0";
3112 cm.refresh();
3189 cm.refresh();
3113 }, true);
3190 }, true);
3191 option("coverGutterNextToScrollbar", false, updateScrollbars, true);
3114 option("lineNumbers", false, function(cm) {
3192 option("lineNumbers", false, function(cm) {
3115 setGuttersForLineNumbers(cm.options);
3193 setGuttersForLineNumbers(cm.options);
3116 guttersChanged(cm);
3194 guttersChanged(cm);
3117 }, true);
3195 }, true);
3118 option("firstLineNumber", 1, guttersChanged, true);
3196 option("firstLineNumber", 1, guttersChanged, true);
3119 option("lineNumberFormatter", function(integer) {return integer;}, guttersChanged, true);
3197 option("lineNumberFormatter", function(integer) {return integer;}, guttersChanged, true);
3120 option("showCursorWhenSelecting", false, updateSelection, true);
3198 option("showCursorWhenSelecting", false, updateSelection, true);
3121
3199
3122 option("readOnly", false, function(cm, val) {
3200 option("readOnly", false, function(cm, val) {
3123 if (val == "nocursor") {onBlur(cm); cm.display.input.blur();}
3201 if (val == "nocursor") {onBlur(cm); cm.display.input.blur();}
3124 else if (!val) resetInput(cm, true);
3202 else if (!val) resetInput(cm, true);
3125 });
3203 });
3126 option("dragDrop", true);
3204 option("dragDrop", true);
3127
3205
3128 option("cursorBlinkRate", 530);
3206 option("cursorBlinkRate", 530);
3207 option("cursorScrollMargin", 0);
3129 option("cursorHeight", 1);
3208 option("cursorHeight", 1);
3130 option("workTime", 100);
3209 option("workTime", 100);
3131 option("workDelay", 100);
3210 option("workDelay", 100);
3132 option("flattenSpans", true);
3211 option("flattenSpans", true);
3133 option("pollInterval", 100);
3212 option("pollInterval", 100);
3134 option("undoDepth", 40, function(cm, val){cm.doc.history.undoDepth = val;});
3213 option("undoDepth", 40, function(cm, val){cm.doc.history.undoDepth = val;});
3135 option("historyEventDelay", 500);
3214 option("historyEventDelay", 500);
3136 option("viewportMargin", 10, function(cm){cm.refresh();}, true);
3215 option("viewportMargin", 10, function(cm){cm.refresh();}, true);
3137 option("maxHighlightLength", 10000, function(cm){loadMode(cm); cm.refresh();}, true);
3216 option("maxHighlightLength", 10000, function(cm){loadMode(cm); cm.refresh();}, true);
3138 option("moveInputWithCursor", true, function(cm, val) {
3217 option("moveInputWithCursor", true, function(cm, val) {
3139 if (!val) cm.display.inputDiv.style.top = cm.display.inputDiv.style.left = 0;
3218 if (!val) cm.display.inputDiv.style.top = cm.display.inputDiv.style.left = 0;
3140 });
3219 });
3141
3220
3142 option("tabindex", null, function(cm, val) {
3221 option("tabindex", null, function(cm, val) {
3143 cm.display.input.tabIndex = val || "";
3222 cm.display.input.tabIndex = val || "";
3144 });
3223 });
3145 option("autofocus", null);
3224 option("autofocus", null);
3146
3225
3147 // MODE DEFINITION AND QUERYING
3226 // MODE DEFINITION AND QUERYING
3148
3227
3149 // Known modes, by name and by MIME
3228 // Known modes, by name and by MIME
3150 var modes = CodeMirror.modes = {}, mimeModes = CodeMirror.mimeModes = {};
3229 var modes = CodeMirror.modes = {}, mimeModes = CodeMirror.mimeModes = {};
3151
3230
3152 CodeMirror.defineMode = function(name, mode) {
3231 CodeMirror.defineMode = function(name, mode) {
3153 if (!CodeMirror.defaults.mode && name != "null") CodeMirror.defaults.mode = name;
3232 if (!CodeMirror.defaults.mode && name != "null") CodeMirror.defaults.mode = name;
3154 if (arguments.length > 2) {
3233 if (arguments.length > 2) {
3155 mode.dependencies = [];
3234 mode.dependencies = [];
3156 for (var i = 2; i < arguments.length; ++i) mode.dependencies.push(arguments[i]);
3235 for (var i = 2; i < arguments.length; ++i) mode.dependencies.push(arguments[i]);
3157 }
3236 }
3158 modes[name] = mode;
3237 modes[name] = mode;
3159 };
3238 };
3160
3239
3161 CodeMirror.defineMIME = function(mime, spec) {
3240 CodeMirror.defineMIME = function(mime, spec) {
3162 mimeModes[mime] = spec;
3241 mimeModes[mime] = spec;
3163 };
3242 };
3164
3243
3165 CodeMirror.resolveMode = function(spec) {
3244 CodeMirror.resolveMode = function(spec) {
3166 if (typeof spec == "string" && mimeModes.hasOwnProperty(spec))
3245 if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) {
3167 spec = mimeModes[spec];
3246 spec = mimeModes[spec];
3168 else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec))
3247 } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) {
3248 var found = mimeModes[spec.name];
3249 spec = createObj(found, spec);
3250 spec.name = found.name;
3251 } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) {
3169 return CodeMirror.resolveMode("application/xml");
3252 return CodeMirror.resolveMode("application/xml");
3253 }
3170 if (typeof spec == "string") return {name: spec};
3254 if (typeof spec == "string") return {name: spec};
3171 else return spec || {name: "null"};
3255 else return spec || {name: "null"};
3172 };
3256 };
3173
3257
3174 CodeMirror.getMode = function(options, spec) {
3258 CodeMirror.getMode = function(options, spec) {
3175 spec = CodeMirror.resolveMode(spec);
3259 spec = CodeMirror.resolveMode(spec);
3176 var mfactory = modes[spec.name];
3260 var mfactory = modes[spec.name];
3177 if (!mfactory) return CodeMirror.getMode(options, "text/plain");
3261 if (!mfactory) return CodeMirror.getMode(options, "text/plain");
3178 var modeObj = mfactory(options, spec);
3262 var modeObj = mfactory(options, spec);
3179 if (modeExtensions.hasOwnProperty(spec.name)) {
3263 if (modeExtensions.hasOwnProperty(spec.name)) {
3180 var exts = modeExtensions[spec.name];
3264 var exts = modeExtensions[spec.name];
3181 for (var prop in exts) {
3265 for (var prop in exts) {
3182 if (!exts.hasOwnProperty(prop)) continue;
3266 if (!exts.hasOwnProperty(prop)) continue;
3183 if (modeObj.hasOwnProperty(prop)) modeObj["_" + prop] = modeObj[prop];
3267 if (modeObj.hasOwnProperty(prop)) modeObj["_" + prop] = modeObj[prop];
3184 modeObj[prop] = exts[prop];
3268 modeObj[prop] = exts[prop];
3185 }
3269 }
3186 }
3270 }
3187 modeObj.name = spec.name;
3271 modeObj.name = spec.name;
3188 return modeObj;
3272 return modeObj;
3189 };
3273 };
3190
3274
3191 CodeMirror.defineMode("null", function() {
3275 CodeMirror.defineMode("null", function() {
3192 return {token: function(stream) {stream.skipToEnd();}};
3276 return {token: function(stream) {stream.skipToEnd();}};
3193 });
3277 });
3194 CodeMirror.defineMIME("text/plain", "null");
3278 CodeMirror.defineMIME("text/plain", "null");
3195
3279
3196 var modeExtensions = CodeMirror.modeExtensions = {};
3280 var modeExtensions = CodeMirror.modeExtensions = {};
3197 CodeMirror.extendMode = function(mode, properties) {
3281 CodeMirror.extendMode = function(mode, properties) {
3198 var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});
3282 var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});
3199 copyObj(properties, exts);
3283 copyObj(properties, exts);
3200 };
3284 };
3201
3285
3202 // EXTENSIONS
3286 // EXTENSIONS
3203
3287
3204 CodeMirror.defineExtension = function(name, func) {
3288 CodeMirror.defineExtension = function(name, func) {
3205 CodeMirror.prototype[name] = func;
3289 CodeMirror.prototype[name] = func;
3206 };
3290 };
3207 CodeMirror.defineDocExtension = function(name, func) {
3291 CodeMirror.defineDocExtension = function(name, func) {
3208 Doc.prototype[name] = func;
3292 Doc.prototype[name] = func;
3209 };
3293 };
3210 CodeMirror.defineOption = option;
3294 CodeMirror.defineOption = option;
3211
3295
3212 var initHooks = [];
3296 var initHooks = [];
3213 CodeMirror.defineInitHook = function(f) {initHooks.push(f);};
3297 CodeMirror.defineInitHook = function(f) {initHooks.push(f);};
3214
3298
3215 // MODE STATE HANDLING
3299 // MODE STATE HANDLING
3216
3300
3217 // Utility functions for working with state. Exported because modes
3301 // Utility functions for working with state. Exported because modes
3218 // sometimes need to do this.
3302 // sometimes need to do this.
3219 function copyState(mode, state) {
3303 function copyState(mode, state) {
3220 if (state === true) return state;
3304 if (state === true) return state;
3221 if (mode.copyState) return mode.copyState(state);
3305 if (mode.copyState) return mode.copyState(state);
3222 var nstate = {};
3306 var nstate = {};
3223 for (var n in state) {
3307 for (var n in state) {
3224 var val = state[n];
3308 var val = state[n];
3225 if (val instanceof Array) val = val.concat([]);
3309 if (val instanceof Array) val = val.concat([]);
3226 nstate[n] = val;
3310 nstate[n] = val;
3227 }
3311 }
3228 return nstate;
3312 return nstate;
3229 }
3313 }
3230 CodeMirror.copyState = copyState;
3314 CodeMirror.copyState = copyState;
3231
3315
3232 function startState(mode, a1, a2) {
3316 function startState(mode, a1, a2) {
3233 return mode.startState ? mode.startState(a1, a2) : true;
3317 return mode.startState ? mode.startState(a1, a2) : true;
3234 }
3318 }
3235 CodeMirror.startState = startState;
3319 CodeMirror.startState = startState;
3236
3320
3237 CodeMirror.innerMode = function(mode, state) {
3321 CodeMirror.innerMode = function(mode, state) {
3238 while (mode.innerMode) {
3322 while (mode.innerMode) {
3239 var info = mode.innerMode(state);
3323 var info = mode.innerMode(state);
3240 state = info.state;
3324 state = info.state;
3241 mode = info.mode;
3325 mode = info.mode;
3242 }
3326 }
3243 return info || {mode: mode, state: state};
3327 return info || {mode: mode, state: state};
3244 };
3328 };
3245
3329
3246 // STANDARD COMMANDS
3330 // STANDARD COMMANDS
3247
3331
3248 var commands = CodeMirror.commands = {
3332 var commands = CodeMirror.commands = {
3249 selectAll: function(cm) {cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()));},
3333 selectAll: function(cm) {cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()));},
3250 killLine: function(cm) {
3334 killLine: function(cm) {
3251 var from = cm.getCursor(true), to = cm.getCursor(false), sel = !posEq(from, to);
3335 var from = cm.getCursor(true), to = cm.getCursor(false), sel = !posEq(from, to);
3252 if (!sel && cm.getLine(from.line).length == from.ch)
3336 if (!sel && cm.getLine(from.line).length == from.ch)
3253 cm.replaceRange("", from, Pos(from.line + 1, 0), "+delete");
3337 cm.replaceRange("", from, Pos(from.line + 1, 0), "+delete");
3254 else cm.replaceRange("", from, sel ? to : Pos(from.line), "+delete");
3338 else cm.replaceRange("", from, sel ? to : Pos(from.line), "+delete");
3255 },
3339 },
3256 deleteLine: function(cm) {
3340 deleteLine: function(cm) {
3257 var l = cm.getCursor().line;
3341 var l = cm.getCursor().line;
3258 cm.replaceRange("", Pos(l, 0), Pos(l), "+delete");
3342 cm.replaceRange("", Pos(l, 0), Pos(l), "+delete");
3259 },
3343 },
3344 delLineLeft: function(cm) {
3345 var cur = cm.getCursor();
3346 cm.replaceRange("", Pos(cur.line, 0), cur, "+delete");
3347 },
3260 undo: function(cm) {cm.undo();},
3348 undo: function(cm) {cm.undo();},
3261 redo: function(cm) {cm.redo();},
3349 redo: function(cm) {cm.redo();},
3262 goDocStart: function(cm) {cm.extendSelection(Pos(cm.firstLine(), 0));},
3350 goDocStart: function(cm) {cm.extendSelection(Pos(cm.firstLine(), 0));},
3263 goDocEnd: function(cm) {cm.extendSelection(Pos(cm.lastLine()));},
3351 goDocEnd: function(cm) {cm.extendSelection(Pos(cm.lastLine()));},
3264 goLineStart: function(cm) {
3352 goLineStart: function(cm) {
3265 cm.extendSelection(lineStart(cm, cm.getCursor().line));
3353 cm.extendSelection(lineStart(cm, cm.getCursor().line));
3266 },
3354 },
3267 goLineStartSmart: function(cm) {
3355 goLineStartSmart: function(cm) {
3268 var cur = cm.getCursor(), start = lineStart(cm, cur.line);
3356 var cur = cm.getCursor(), start = lineStart(cm, cur.line);
3269 var line = cm.getLineHandle(start.line);
3357 var line = cm.getLineHandle(start.line);
3270 var order = getOrder(line);
3358 var order = getOrder(line);
3271 if (!order || order[0].level == 0) {
3359 if (!order || order[0].level == 0) {
3272 var firstNonWS = Math.max(0, line.text.search(/\S/));
3360 var firstNonWS = Math.max(0, line.text.search(/\S/));
3273 var inWS = cur.line == start.line && cur.ch <= firstNonWS && cur.ch;
3361 var inWS = cur.line == start.line && cur.ch <= firstNonWS && cur.ch;
3274 cm.extendSelection(Pos(start.line, inWS ? 0 : firstNonWS));
3362 cm.extendSelection(Pos(start.line, inWS ? 0 : firstNonWS));
3275 } else cm.extendSelection(start);
3363 } else cm.extendSelection(start);
3276 },
3364 },
3277 goLineEnd: function(cm) {
3365 goLineEnd: function(cm) {
3278 cm.extendSelection(lineEnd(cm, cm.getCursor().line));
3366 cm.extendSelection(lineEnd(cm, cm.getCursor().line));
3279 },
3367 },
3280 goLineRight: function(cm) {
3368 goLineRight: function(cm) {
3281 var top = cm.charCoords(cm.getCursor(), "div").top + 5;
3369 var top = cm.charCoords(cm.getCursor(), "div").top + 5;
3282 cm.extendSelection(cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div"));
3370 cm.extendSelection(cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div"));
3283 },
3371 },
3284 goLineLeft: function(cm) {
3372 goLineLeft: function(cm) {
3285 var top = cm.charCoords(cm.getCursor(), "div").top + 5;
3373 var top = cm.charCoords(cm.getCursor(), "div").top + 5;
3286 cm.extendSelection(cm.coordsChar({left: 0, top: top}, "div"));
3374 cm.extendSelection(cm.coordsChar({left: 0, top: top}, "div"));
3287 },
3375 },
3288 goLineUp: function(cm) {cm.moveV(-1, "line");},
3376 goLineUp: function(cm) {cm.moveV(-1, "line");},
3289 goLineDown: function(cm) {cm.moveV(1, "line");},
3377 goLineDown: function(cm) {cm.moveV(1, "line");},
3290 goPageUp: function(cm) {cm.moveV(-1, "page");},
3378 goPageUp: function(cm) {cm.moveV(-1, "page");},
3291 goPageDown: function(cm) {cm.moveV(1, "page");},
3379 goPageDown: function(cm) {cm.moveV(1, "page");},
3292 goCharLeft: function(cm) {cm.moveH(-1, "char");},
3380 goCharLeft: function(cm) {cm.moveH(-1, "char");},
3293 goCharRight: function(cm) {cm.moveH(1, "char");},
3381 goCharRight: function(cm) {cm.moveH(1, "char");},
3294 goColumnLeft: function(cm) {cm.moveH(-1, "column");},
3382 goColumnLeft: function(cm) {cm.moveH(-1, "column");},
3295 goColumnRight: function(cm) {cm.moveH(1, "column");},
3383 goColumnRight: function(cm) {cm.moveH(1, "column");},
3296 goWordLeft: function(cm) {cm.moveH(-1, "word");},
3384 goWordLeft: function(cm) {cm.moveH(-1, "word");},
3297 goGroupRight: function(cm) {cm.moveH(1, "group");},
3385 goGroupRight: function(cm) {cm.moveH(1, "group");},
3298 goGroupLeft: function(cm) {cm.moveH(-1, "group");},
3386 goGroupLeft: function(cm) {cm.moveH(-1, "group");},
3299 goWordRight: function(cm) {cm.moveH(1, "word");},
3387 goWordRight: function(cm) {cm.moveH(1, "word");},
3300 delCharBefore: function(cm) {cm.deleteH(-1, "char");},
3388 delCharBefore: function(cm) {cm.deleteH(-1, "char");},
3301 delCharAfter: function(cm) {cm.deleteH(1, "char");},
3389 delCharAfter: function(cm) {cm.deleteH(1, "char");},
3302 delWordBefore: function(cm) {cm.deleteH(-1, "word");},
3390 delWordBefore: function(cm) {cm.deleteH(-1, "word");},
3303 delWordAfter: function(cm) {cm.deleteH(1, "word");},
3391 delWordAfter: function(cm) {cm.deleteH(1, "word");},
3304 delGroupBefore: function(cm) {cm.deleteH(-1, "group");},
3392 delGroupBefore: function(cm) {cm.deleteH(-1, "group");},
3305 delGroupAfter: function(cm) {cm.deleteH(1, "group");},
3393 delGroupAfter: function(cm) {cm.deleteH(1, "group");},
3306 indentAuto: function(cm) {cm.indentSelection("smart");},
3394 indentAuto: function(cm) {cm.indentSelection("smart");},
3307 indentMore: function(cm) {cm.indentSelection("add");},
3395 indentMore: function(cm) {cm.indentSelection("add");},
3308 indentLess: function(cm) {cm.indentSelection("subtract");},
3396 indentLess: function(cm) {cm.indentSelection("subtract");},
3309 insertTab: function(cm) {cm.replaceSelection("\t", "end", "+input");},
3397 insertTab: function(cm) {cm.replaceSelection("\t", "end", "+input");},
3310 defaultTab: function(cm) {
3398 defaultTab: function(cm) {
3311 if (cm.somethingSelected()) cm.indentSelection("add");
3399 if (cm.somethingSelected()) cm.indentSelection("add");
3312 else cm.replaceSelection("\t", "end", "+input");
3400 else cm.replaceSelection("\t", "end", "+input");
3313 },
3401 },
3314 transposeChars: function(cm) {
3402 transposeChars: function(cm) {
3315 var cur = cm.getCursor(), line = cm.getLine(cur.line);
3403 var cur = cm.getCursor(), line = cm.getLine(cur.line);
3316 if (cur.ch > 0 && cur.ch < line.length - 1)
3404 if (cur.ch > 0 && cur.ch < line.length - 1)
3317 cm.replaceRange(line.charAt(cur.ch) + line.charAt(cur.ch - 1),
3405 cm.replaceRange(line.charAt(cur.ch) + line.charAt(cur.ch - 1),
3318 Pos(cur.line, cur.ch - 1), Pos(cur.line, cur.ch + 1));
3406 Pos(cur.line, cur.ch - 1), Pos(cur.line, cur.ch + 1));
3319 },
3407 },
3320 newlineAndIndent: function(cm) {
3408 newlineAndIndent: function(cm) {
3321 operation(cm, function() {
3409 operation(cm, function() {
3322 cm.replaceSelection("\n", "end", "+input");
3410 cm.replaceSelection("\n", "end", "+input");
3323 cm.indentLine(cm.getCursor().line, null, true);
3411 cm.indentLine(cm.getCursor().line, null, true);
3324 })();
3412 })();
3325 },
3413 },
3326 toggleOverwrite: function(cm) {cm.toggleOverwrite();}
3414 toggleOverwrite: function(cm) {cm.toggleOverwrite();}
3327 };
3415 };
3328
3416
3329 // STANDARD KEYMAPS
3417 // STANDARD KEYMAPS
3330
3418
3331 var keyMap = CodeMirror.keyMap = {};
3419 var keyMap = CodeMirror.keyMap = {};
3332 keyMap.basic = {
3420 keyMap.basic = {
3333 "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
3421 "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
3334 "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
3422 "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
3335 "Delete": "delCharAfter", "Backspace": "delCharBefore", "Tab": "defaultTab", "Shift-Tab": "indentAuto",
3423 "Delete": "delCharAfter", "Backspace": "delCharBefore", "Tab": "defaultTab", "Shift-Tab": "indentAuto",
3336 "Enter": "newlineAndIndent", "Insert": "toggleOverwrite"
3424 "Enter": "newlineAndIndent", "Insert": "toggleOverwrite"
3337 };
3425 };
3338 // Note that the save and find-related commands aren't defined by
3426 // Note that the save and find-related commands aren't defined by
3339 // default. Unknown commands are simply ignored.
3427 // default. Unknown commands are simply ignored.
3340 keyMap.pcDefault = {
3428 keyMap.pcDefault = {
3341 "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
3429 "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
3342 "Ctrl-Home": "goDocStart", "Alt-Up": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Down": "goDocEnd",
3430 "Ctrl-Home": "goDocStart", "Alt-Up": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Down": "goDocEnd",
3343 "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
3431 "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
3344 "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find",
3432 "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find",
3345 "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
3433 "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
3346 "Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
3434 "Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
3347 fallthrough: "basic"
3435 fallthrough: "basic"
3348 };
3436 };
3349 keyMap.macDefault = {
3437 keyMap.macDefault = {
3350 "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
3438 "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
3351 "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft",
3439 "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft",
3352 "Alt-Right": "goGroupRight", "Cmd-Left": "goLineStart", "Cmd-Right": "goLineEnd", "Alt-Backspace": "delGroupBefore",
3440 "Alt-Right": "goGroupRight", "Cmd-Left": "goLineStart", "Cmd-Right": "goLineEnd", "Alt-Backspace": "delGroupBefore",
3353 "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find",
3441 "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find",
3354 "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
3442 "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
3355 "Cmd-[": "indentLess", "Cmd-]": "indentMore",
3443 "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delLineLeft",
3356 fallthrough: ["basic", "emacsy"]
3444 fallthrough: ["basic", "emacsy"]
3357 };
3445 };
3358 keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault;
3446 keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault;
3359 keyMap.emacsy = {
3447 keyMap.emacsy = {
3360 "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
3448 "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
3361 "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
3449 "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
3362 "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore",
3450 "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore",
3363 "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars"
3451 "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars"
3364 };
3452 };
3365
3453
3366 // KEYMAP DISPATCH
3454 // KEYMAP DISPATCH
3367
3455
3368 function getKeyMap(val) {
3456 function getKeyMap(val) {
3369 if (typeof val == "string") return keyMap[val];
3457 if (typeof val == "string") return keyMap[val];
3370 else return val;
3458 else return val;
3371 }
3459 }
3372
3460
3373 function lookupKey(name, maps, handle) {
3461 function lookupKey(name, maps, handle) {
3374 function lookup(map) {
3462 function lookup(map) {
3375 map = getKeyMap(map);
3463 map = getKeyMap(map);
3376 var found = map[name];
3464 var found = map[name];
3377 if (found === false) return "stop";
3465 if (found === false) return "stop";
3378 if (found != null && handle(found)) return true;
3466 if (found != null && handle(found)) return true;
3379 if (map.nofallthrough) return "stop";
3467 if (map.nofallthrough) return "stop";
3380
3468
3381 var fallthrough = map.fallthrough;
3469 var fallthrough = map.fallthrough;
3382 if (fallthrough == null) return false;
3470 if (fallthrough == null) return false;
3383 if (Object.prototype.toString.call(fallthrough) != "[object Array]")
3471 if (Object.prototype.toString.call(fallthrough) != "[object Array]")
3384 return lookup(fallthrough);
3472 return lookup(fallthrough);
3385 for (var i = 0, e = fallthrough.length; i < e; ++i) {
3473 for (var i = 0, e = fallthrough.length; i < e; ++i) {
3386 var done = lookup(fallthrough[i]);
3474 var done = lookup(fallthrough[i]);
3387 if (done) return done;
3475 if (done) return done;
3388 }
3476 }
3389 return false;
3477 return false;
3390 }
3478 }
3391
3479
3392 for (var i = 0; i < maps.length; ++i) {
3480 for (var i = 0; i < maps.length; ++i) {
3393 var done = lookup(maps[i]);
3481 var done = lookup(maps[i]);
3394 if (done) return done;
3482 if (done) return done != "stop";
3395 }
3483 }
3396 }
3484 }
3397 function isModifierKey(event) {
3485 function isModifierKey(event) {
3398 var name = keyNames[event.keyCode];
3486 var name = keyNames[event.keyCode];
3399 return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod";
3487 return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod";
3400 }
3488 }
3401 function keyName(event, noShift) {
3489 function keyName(event, noShift) {
3402 if (opera && event.keyCode == 34 && event["char"]) return false;
3490 if (opera && event.keyCode == 34 && event["char"]) return false;
3403 var name = keyNames[event.keyCode];
3491 var name = keyNames[event.keyCode];
3404 if (name == null || event.altGraphKey) return false;
3492 if (name == null || event.altGraphKey) return false;
3405 if (event.altKey) name = "Alt-" + name;
3493 if (event.altKey) name = "Alt-" + name;
3406 if (flipCtrlCmd ? event.metaKey : event.ctrlKey) name = "Ctrl-" + name;
3494 if (flipCtrlCmd ? event.metaKey : event.ctrlKey) name = "Ctrl-" + name;
3407 if (flipCtrlCmd ? event.ctrlKey : event.metaKey) name = "Cmd-" + name;
3495 if (flipCtrlCmd ? event.ctrlKey : event.metaKey) name = "Cmd-" + name;
3408 if (!noShift && event.shiftKey) name = "Shift-" + name;
3496 if (!noShift && event.shiftKey) name = "Shift-" + name;
3409 return name;
3497 return name;
3410 }
3498 }
3411 CodeMirror.lookupKey = lookupKey;
3499 CodeMirror.lookupKey = lookupKey;
3412 CodeMirror.isModifierKey = isModifierKey;
3500 CodeMirror.isModifierKey = isModifierKey;
3413 CodeMirror.keyName = keyName;
3501 CodeMirror.keyName = keyName;
3414
3502
3415 // FROMTEXTAREA
3503 // FROMTEXTAREA
3416
3504
3417 CodeMirror.fromTextArea = function(textarea, options) {
3505 CodeMirror.fromTextArea = function(textarea, options) {
3418 if (!options) options = {};
3506 if (!options) options = {};
3419 options.value = textarea.value;
3507 options.value = textarea.value;
3420 if (!options.tabindex && textarea.tabindex)
3508 if (!options.tabindex && textarea.tabindex)
3421 options.tabindex = textarea.tabindex;
3509 options.tabindex = textarea.tabindex;
3422 if (!options.placeholder && textarea.placeholder)
3510 if (!options.placeholder && textarea.placeholder)
3423 options.placeholder = textarea.placeholder;
3511 options.placeholder = textarea.placeholder;
3424 // Set autofocus to true if this textarea is focused, or if it has
3512 // Set autofocus to true if this textarea is focused, or if it has
3425 // autofocus and no other element is focused.
3513 // autofocus and no other element is focused.
3426 if (options.autofocus == null) {
3514 if (options.autofocus == null) {
3427 var hasFocus = document.body;
3515 var hasFocus = document.body;
3428 // doc.activeElement occasionally throws on IE
3516 // doc.activeElement occasionally throws on IE
3429 try { hasFocus = document.activeElement; } catch(e) {}
3517 try { hasFocus = document.activeElement; } catch(e) {}
3430 options.autofocus = hasFocus == textarea ||
3518 options.autofocus = hasFocus == textarea ||
3431 textarea.getAttribute("autofocus") != null && hasFocus == document.body;
3519 textarea.getAttribute("autofocus") != null && hasFocus == document.body;
3432 }
3520 }
3433
3521
3434 function save() {textarea.value = cm.getValue();}
3522 function save() {textarea.value = cm.getValue();}
3435 if (textarea.form) {
3523 if (textarea.form) {
3436 on(textarea.form, "submit", save);
3524 on(textarea.form, "submit", save);
3437 // Deplorable hack to make the submit method do the right thing.
3525 // Deplorable hack to make the submit method do the right thing.
3438 if (!options.leaveSubmitMethodAlone) {
3526 if (!options.leaveSubmitMethodAlone) {
3439 var form = textarea.form, realSubmit = form.submit;
3527 var form = textarea.form, realSubmit = form.submit;
3440 try {
3528 try {
3441 var wrappedSubmit = form.submit = function() {
3529 var wrappedSubmit = form.submit = function() {
3442 save();
3530 save();
3443 form.submit = realSubmit;
3531 form.submit = realSubmit;
3444 form.submit();
3532 form.submit();
3445 form.submit = wrappedSubmit;
3533 form.submit = wrappedSubmit;
3446 };
3534 };
3447 } catch(e) {}
3535 } catch(e) {}
3448 }
3536 }
3449 }
3537 }
3450
3538
3451 textarea.style.display = "none";
3539 textarea.style.display = "none";
3452 var cm = CodeMirror(function(node) {
3540 var cm = CodeMirror(function(node) {
3453 textarea.parentNode.insertBefore(node, textarea.nextSibling);
3541 textarea.parentNode.insertBefore(node, textarea.nextSibling);
3454 }, options);
3542 }, options);
3455 cm.save = save;
3543 cm.save = save;
3456 cm.getTextArea = function() { return textarea; };
3544 cm.getTextArea = function() { return textarea; };
3457 cm.toTextArea = function() {
3545 cm.toTextArea = function() {
3458 save();
3546 save();
3459 textarea.parentNode.removeChild(cm.getWrapperElement());
3547 textarea.parentNode.removeChild(cm.getWrapperElement());
3460 textarea.style.display = "";
3548 textarea.style.display = "";
3461 if (textarea.form) {
3549 if (textarea.form) {
3462 off(textarea.form, "submit", save);
3550 off(textarea.form, "submit", save);
3463 if (typeof textarea.form.submit == "function")
3551 if (typeof textarea.form.submit == "function")
3464 textarea.form.submit = realSubmit;
3552 textarea.form.submit = realSubmit;
3465 }
3553 }
3466 };
3554 };
3467 return cm;
3555 return cm;
3468 };
3556 };
3469
3557
3470 // STRING STREAM
3558 // STRING STREAM
3471
3559
3472 // Fed to the mode parsers, provides helper functions to make
3560 // Fed to the mode parsers, provides helper functions to make
3473 // parsers more succinct.
3561 // parsers more succinct.
3474
3562
3475 // The character stream used by a mode's parser.
3563 // The character stream used by a mode's parser.
3476 function StringStream(string, tabSize) {
3564 function StringStream(string, tabSize) {
3477 this.pos = this.start = 0;
3565 this.pos = this.start = 0;
3478 this.string = string;
3566 this.string = string;
3479 this.tabSize = tabSize || 8;
3567 this.tabSize = tabSize || 8;
3480 this.lastColumnPos = this.lastColumnValue = 0;
3568 this.lastColumnPos = this.lastColumnValue = 0;
3481 }
3569 }
3482
3570
3483 StringStream.prototype = {
3571 StringStream.prototype = {
3484 eol: function() {return this.pos >= this.string.length;},
3572 eol: function() {return this.pos >= this.string.length;},
3485 sol: function() {return this.pos == 0;},
3573 sol: function() {return this.pos == 0;},
3486 peek: function() {return this.string.charAt(this.pos) || undefined;},
3574 peek: function() {return this.string.charAt(this.pos) || undefined;},
3487 next: function() {
3575 next: function() {
3488 if (this.pos < this.string.length)
3576 if (this.pos < this.string.length)
3489 return this.string.charAt(this.pos++);
3577 return this.string.charAt(this.pos++);
3490 },
3578 },
3491 eat: function(match) {
3579 eat: function(match) {
3492 var ch = this.string.charAt(this.pos);
3580 var ch = this.string.charAt(this.pos);
3493 if (typeof match == "string") var ok = ch == match;
3581 if (typeof match == "string") var ok = ch == match;
3494 else var ok = ch && (match.test ? match.test(ch) : match(ch));
3582 else var ok = ch && (match.test ? match.test(ch) : match(ch));
3495 if (ok) {++this.pos; return ch;}
3583 if (ok) {++this.pos; return ch;}
3496 },
3584 },
3497 eatWhile: function(match) {
3585 eatWhile: function(match) {
3498 var start = this.pos;
3586 var start = this.pos;
3499 while (this.eat(match)){}
3587 while (this.eat(match)){}
3500 return this.pos > start;
3588 return this.pos > start;
3501 },
3589 },
3502 eatSpace: function() {
3590 eatSpace: function() {
3503 var start = this.pos;
3591 var start = this.pos;
3504 while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) ++this.pos;
3592 while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) ++this.pos;
3505 return this.pos > start;
3593 return this.pos > start;
3506 },
3594 },
3507 skipToEnd: function() {this.pos = this.string.length;},
3595 skipToEnd: function() {this.pos = this.string.length;},
3508 skipTo: function(ch) {
3596 skipTo: function(ch) {
3509 var found = this.string.indexOf(ch, this.pos);
3597 var found = this.string.indexOf(ch, this.pos);
3510 if (found > -1) {this.pos = found; return true;}
3598 if (found > -1) {this.pos = found; return true;}
3511 },
3599 },
3512 backUp: function(n) {this.pos -= n;},
3600 backUp: function(n) {this.pos -= n;},
3513 column: function() {
3601 column: function() {
3514 if (this.lastColumnPos < this.start) {
3602 if (this.lastColumnPos < this.start) {
3515 this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue);
3603 this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue);
3516 this.lastColumnPos = this.start;
3604 this.lastColumnPos = this.start;
3517 }
3605 }
3518 return this.lastColumnValue;
3606 return this.lastColumnValue;
3519 },
3607 },
3520 indentation: function() {return countColumn(this.string, null, this.tabSize);},
3608 indentation: function() {return countColumn(this.string, null, this.tabSize);},
3521 match: function(pattern, consume, caseInsensitive) {
3609 match: function(pattern, consume, caseInsensitive) {
3522 if (typeof pattern == "string") {
3610 if (typeof pattern == "string") {
3523 var cased = function(str) {return caseInsensitive ? str.toLowerCase() : str;};
3611 var cased = function(str) {return caseInsensitive ? str.toLowerCase() : str;};
3524 var substr = this.string.substr(this.pos, pattern.length);
3612 var substr = this.string.substr(this.pos, pattern.length);
3525 if (cased(substr) == cased(pattern)) {
3613 if (cased(substr) == cased(pattern)) {
3526 if (consume !== false) this.pos += pattern.length;
3614 if (consume !== false) this.pos += pattern.length;
3527 return true;
3615 return true;
3528 }
3616 }
3529 } else {
3617 } else {
3530 var match = this.string.slice(this.pos).match(pattern);
3618 var match = this.string.slice(this.pos).match(pattern);
3531 if (match && match.index > 0) return null;
3619 if (match && match.index > 0) return null;
3532 if (match && consume !== false) this.pos += match[0].length;
3620 if (match && consume !== false) this.pos += match[0].length;
3533 return match;
3621 return match;
3534 }
3622 }
3535 },
3623 },
3536 current: function(){return this.string.slice(this.start, this.pos);}
3624 current: function(){return this.string.slice(this.start, this.pos);}
3537 };
3625 };
3538 CodeMirror.StringStream = StringStream;
3626 CodeMirror.StringStream = StringStream;
3539
3627
3540 // TEXTMARKERS
3628 // TEXTMARKERS
3541
3629
3542 function TextMarker(doc, type) {
3630 function TextMarker(doc, type) {
3543 this.lines = [];
3631 this.lines = [];
3544 this.type = type;
3632 this.type = type;
3545 this.doc = doc;
3633 this.doc = doc;
3546 }
3634 }
3547 CodeMirror.TextMarker = TextMarker;
3635 CodeMirror.TextMarker = TextMarker;
3548
3636
3549 TextMarker.prototype.clear = function() {
3637 TextMarker.prototype.clear = function() {
3550 if (this.explicitlyCleared) return;
3638 if (this.explicitlyCleared) return;
3551 var cm = this.doc.cm, withOp = cm && !cm.curOp;
3639 var cm = this.doc.cm, withOp = cm && !cm.curOp;
3552 if (withOp) startOperation(cm);
3640 if (withOp) startOperation(cm);
3553 var min = null, max = null;
3641 var min = null, max = null;
3554 for (var i = 0; i < this.lines.length; ++i) {
3642 for (var i = 0; i < this.lines.length; ++i) {
3555 var line = this.lines[i];
3643 var line = this.lines[i];
3556 var span = getMarkedSpanFor(line.markedSpans, this);
3644 var span = getMarkedSpanFor(line.markedSpans, this);
3557 if (span.to != null) max = lineNo(line);
3645 if (span.to != null) max = lineNo(line);
3558 line.markedSpans = removeMarkedSpan(line.markedSpans, span);
3646 line.markedSpans = removeMarkedSpan(line.markedSpans, span);
3559 if (span.from != null)
3647 if (span.from != null)
3560 min = lineNo(line);
3648 min = lineNo(line);
3561 else if (this.collapsed && !lineIsHidden(this.doc, line) && cm)
3649 else if (this.collapsed && !lineIsHidden(this.doc, line) && cm)
3562 updateLineHeight(line, textHeight(cm.display));
3650 updateLineHeight(line, textHeight(cm.display));
3563 }
3651 }
3564 if (cm && this.collapsed && !cm.options.lineWrapping) for (var i = 0; i < this.lines.length; ++i) {
3652 if (cm && this.collapsed && !cm.options.lineWrapping) for (var i = 0; i < this.lines.length; ++i) {
3565 var visual = visualLine(cm.doc, this.lines[i]), len = lineLength(cm.doc, visual);
3653 var visual = visualLine(cm.doc, this.lines[i]), len = lineLength(cm.doc, visual);
3566 if (len > cm.display.maxLineLength) {
3654 if (len > cm.display.maxLineLength) {
3567 cm.display.maxLine = visual;
3655 cm.display.maxLine = visual;
3568 cm.display.maxLineLength = len;
3656 cm.display.maxLineLength = len;
3569 cm.display.maxLineChanged = true;
3657 cm.display.maxLineChanged = true;
3570 }
3658 }
3571 }
3659 }
3572
3660
3573 if (min != null && cm) regChange(cm, min, max + 1);
3661 if (min != null && cm) regChange(cm, min, max + 1);
3574 this.lines.length = 0;
3662 this.lines.length = 0;
3575 this.explicitlyCleared = true;
3663 this.explicitlyCleared = true;
3576 if (this.collapsed && this.doc.cantEdit) {
3664 if (this.atomic && this.doc.cantEdit) {
3577 this.doc.cantEdit = false;
3665 this.doc.cantEdit = false;
3578 if (cm) reCheckSelection(cm);
3666 if (cm) reCheckSelection(cm);
3579 }
3667 }
3580 if (withOp) endOperation(cm);
3668 if (withOp) endOperation(cm);
3581 signalLater(this, "clear");
3669 signalLater(this, "clear");
3582 };
3670 };
3583
3671
3584 TextMarker.prototype.find = function() {
3672 TextMarker.prototype.find = function() {
3585 var from, to;
3673 var from, to;
3586 for (var i = 0; i < this.lines.length; ++i) {
3674 for (var i = 0; i < this.lines.length; ++i) {
3587 var line = this.lines[i];
3675 var line = this.lines[i];
3588 var span = getMarkedSpanFor(line.markedSpans, this);
3676 var span = getMarkedSpanFor(line.markedSpans, this);
3589 if (span.from != null || span.to != null) {
3677 if (span.from != null || span.to != null) {
3590 var found = lineNo(line);
3678 var found = lineNo(line);
3591 if (span.from != null) from = Pos(found, span.from);
3679 if (span.from != null) from = Pos(found, span.from);
3592 if (span.to != null) to = Pos(found, span.to);
3680 if (span.to != null) to = Pos(found, span.to);
3593 }
3681 }
3594 }
3682 }
3595 if (this.type == "bookmark") return from;
3683 if (this.type == "bookmark") return from;
3596 return from && {from: from, to: to};
3684 return from && {from: from, to: to};
3597 };
3685 };
3598
3686
3599 TextMarker.prototype.getOptions = function(copyWidget) {
3687 TextMarker.prototype.changed = function() {
3600 var repl = this.replacedWith;
3688 var pos = this.find(), cm = this.doc.cm;
3601 return {className: this.className,
3689 if (!pos || !cm) return;
3602 inclusiveLeft: this.inclusiveLeft, inclusiveRight: this.inclusiveRight,
3690 var line = getLine(this.doc, pos.from.line);
3603 atomic: this.atomic,
3691 clearCachedMeasurement(cm, line);
3604 collapsed: this.collapsed,
3692 if (pos.from.line >= cm.display.showingFrom && pos.from.line < cm.display.showingTo) {
3605 replacedWith: copyWidget ? repl && repl.cloneNode(true) : repl,
3693 for (var node = cm.display.lineDiv.firstChild; node; node = node.nextSibling) if (node.lineObj == line) {
3606 readOnly: this.readOnly,
3694 if (node.offsetHeight != line.height) updateLineHeight(line, node.offsetHeight);
3607 startStyle: this.startStyle, endStyle: this.endStyle};
3695 break;
3696 }
3697 runInOp(cm, function() { cm.curOp.selectionChanged = true; });
3698 }
3608 };
3699 };
3609
3700
3610 TextMarker.prototype.attachLine = function(line) {
3701 TextMarker.prototype.attachLine = function(line) {
3611 if (!this.lines.length && this.doc.cm) {
3702 if (!this.lines.length && this.doc.cm) {
3612 var op = this.doc.cm.curOp;
3703 var op = this.doc.cm.curOp;
3613 if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1)
3704 if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1)
3614 (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this);
3705 (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this);
3615 }
3706 }
3616 this.lines.push(line);
3707 this.lines.push(line);
3617 };
3708 };
3618 TextMarker.prototype.detachLine = function(line) {
3709 TextMarker.prototype.detachLine = function(line) {
3619 this.lines.splice(indexOf(this.lines, line), 1);
3710 this.lines.splice(indexOf(this.lines, line), 1);
3620 if (!this.lines.length && this.doc.cm) {
3711 if (!this.lines.length && this.doc.cm) {
3621 var op = this.doc.cm.curOp;
3712 var op = this.doc.cm.curOp;
3622 (op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this);
3713 (op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this);
3623 }
3714 }
3624 };
3715 };
3625
3716
3626 function markText(doc, from, to, options, type) {
3717 function markText(doc, from, to, options, type) {
3627 if (options && options.shared) return markTextShared(doc, from, to, options, type);
3718 if (options && options.shared) return markTextShared(doc, from, to, options, type);
3628 if (doc.cm && !doc.cm.curOp) return operation(doc.cm, markText)(doc, from, to, options, type);
3719 if (doc.cm && !doc.cm.curOp) return operation(doc.cm, markText)(doc, from, to, options, type);
3629
3720
3630 var marker = new TextMarker(doc, type);
3721 var marker = new TextMarker(doc, type);
3631 if (type == "range" && !posLess(from, to)) return marker;
3722 if (type == "range" && !posLess(from, to)) return marker;
3632 if (options) copyObj(options, marker);
3723 if (options) copyObj(options, marker);
3633 if (marker.replacedWith) {
3724 if (marker.replacedWith) {
3634 marker.collapsed = true;
3725 marker.collapsed = true;
3635 marker.replacedWith = elt("span", [marker.replacedWith], "CodeMirror-widget");
3726 marker.replacedWith = elt("span", [marker.replacedWith], "CodeMirror-widget");
3727 if (!options.handleMouseEvents) marker.replacedWith.ignoreEvents = true;
3636 }
3728 }
3637 if (marker.collapsed) sawCollapsedSpans = true;
3729 if (marker.collapsed) sawCollapsedSpans = true;
3638
3730
3639 if (marker.addToHistory)
3731 if (marker.addToHistory)
3640 addToHistory(doc, {from: from, to: to, origin: "markText"},
3732 addToHistory(doc, {from: from, to: to, origin: "markText"},
3641 {head: doc.sel.head, anchor: doc.sel.anchor}, NaN);
3733 {head: doc.sel.head, anchor: doc.sel.anchor}, NaN);
3642
3734
3643 var curLine = from.line, size = 0, collapsedAtStart, collapsedAtEnd, cm = doc.cm, updateMaxLine;
3735 var curLine = from.line, size = 0, collapsedAtStart, collapsedAtEnd, cm = doc.cm, updateMaxLine;
3644 doc.iter(curLine, to.line + 1, function(line) {
3736 doc.iter(curLine, to.line + 1, function(line) {
3645 if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(doc, line) == cm.display.maxLine)
3737 if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(doc, line) == cm.display.maxLine)
3646 updateMaxLine = true;
3738 updateMaxLine = true;
3647 var span = {from: null, to: null, marker: marker};
3739 var span = {from: null, to: null, marker: marker};
3648 size += line.text.length;
3740 size += line.text.length;
3649 if (curLine == from.line) {span.from = from.ch; size -= from.ch;}
3741 if (curLine == from.line) {span.from = from.ch; size -= from.ch;}
3650 if (curLine == to.line) {span.to = to.ch; size -= line.text.length - to.ch;}
3742 if (curLine == to.line) {span.to = to.ch; size -= line.text.length - to.ch;}
3651 if (marker.collapsed) {
3743 if (marker.collapsed) {
3652 if (curLine == to.line) collapsedAtEnd = collapsedSpanAt(line, to.ch);
3744 if (curLine == to.line) collapsedAtEnd = collapsedSpanAt(line, to.ch);
3653 if (curLine == from.line) collapsedAtStart = collapsedSpanAt(line, from.ch);
3745 if (curLine == from.line) collapsedAtStart = collapsedSpanAt(line, from.ch);
3654 else updateLineHeight(line, 0);
3746 else updateLineHeight(line, 0);
3655 }
3747 }
3656 addMarkedSpan(line, span);
3748 addMarkedSpan(line, span);
3657 ++curLine;
3749 ++curLine;
3658 });
3750 });
3659 if (marker.collapsed) doc.iter(from.line, to.line + 1, function(line) {
3751 if (marker.collapsed) doc.iter(from.line, to.line + 1, function(line) {
3660 if (lineIsHidden(doc, line)) updateLineHeight(line, 0);
3752 if (lineIsHidden(doc, line)) updateLineHeight(line, 0);
3661 });
3753 });
3662
3754
3663 if (marker.clearOnEnter) on(marker, "beforeCursorEnter", function() { marker.clear(); });
3755 if (marker.clearOnEnter) on(marker, "beforeCursorEnter", function() { marker.clear(); });
3664
3756
3665 if (marker.readOnly) {
3757 if (marker.readOnly) {
3666 sawReadOnlySpans = true;
3758 sawReadOnlySpans = true;
3667 if (doc.history.done.length || doc.history.undone.length)
3759 if (doc.history.done.length || doc.history.undone.length)
3668 doc.clearHistory();
3760 doc.clearHistory();
3669 }
3761 }
3670 if (marker.collapsed) {
3762 if (marker.collapsed) {
3671 if (collapsedAtStart != collapsedAtEnd)
3763 if (collapsedAtStart != collapsedAtEnd)
3672 throw new Error("Inserting collapsed marker overlapping an existing one");
3764 throw new Error("Inserting collapsed marker overlapping an existing one");
3673 marker.size = size;
3765 marker.size = size;
3674 marker.atomic = true;
3766 marker.atomic = true;
3675 }
3767 }
3676 if (cm) {
3768 if (cm) {
3677 if (updateMaxLine) cm.curOp.updateMaxLine = true;
3769 if (updateMaxLine) cm.curOp.updateMaxLine = true;
3678 if (marker.className || marker.startStyle || marker.endStyle || marker.collapsed)
3770 if (marker.className || marker.startStyle || marker.endStyle || marker.collapsed)
3679 regChange(cm, from.line, to.line + 1);
3771 regChange(cm, from.line, to.line + 1);
3680 if (marker.atomic) reCheckSelection(cm);
3772 if (marker.atomic) reCheckSelection(cm);
3681 }
3773 }
3682 return marker;
3774 return marker;
3683 }
3775 }
3684
3776
3685 // SHARED TEXTMARKERS
3777 // SHARED TEXTMARKERS
3686
3778
3687 function SharedTextMarker(markers, primary) {
3779 function SharedTextMarker(markers, primary) {
3688 this.markers = markers;
3780 this.markers = markers;
3689 this.primary = primary;
3781 this.primary = primary;
3690 for (var i = 0, me = this; i < markers.length; ++i) {
3782 for (var i = 0, me = this; i < markers.length; ++i) {
3691 markers[i].parent = this;
3783 markers[i].parent = this;
3692 on(markers[i], "clear", function(){me.clear();});
3784 on(markers[i], "clear", function(){me.clear();});
3693 }
3785 }
3694 }
3786 }
3695 CodeMirror.SharedTextMarker = SharedTextMarker;
3787 CodeMirror.SharedTextMarker = SharedTextMarker;
3696
3788
3697 SharedTextMarker.prototype.clear = function() {
3789 SharedTextMarker.prototype.clear = function() {
3698 if (this.explicitlyCleared) return;
3790 if (this.explicitlyCleared) return;
3699 this.explicitlyCleared = true;
3791 this.explicitlyCleared = true;
3700 for (var i = 0; i < this.markers.length; ++i)
3792 for (var i = 0; i < this.markers.length; ++i)
3701 this.markers[i].clear();
3793 this.markers[i].clear();
3702 signalLater(this, "clear");
3794 signalLater(this, "clear");
3703 };
3795 };
3704 SharedTextMarker.prototype.find = function() {
3796 SharedTextMarker.prototype.find = function() {
3705 return this.primary.find();
3797 return this.primary.find();
3706 };
3798 };
3707 SharedTextMarker.prototype.getOptions = function(copyWidget) {
3708 var inner = this.primary.getOptions(copyWidget);
3709 inner.shared = true;
3710 return inner;
3711 };
3712
3799
3713 function markTextShared(doc, from, to, options, type) {
3800 function markTextShared(doc, from, to, options, type) {
3714 options = copyObj(options);
3801 options = copyObj(options);
3715 options.shared = false;
3802 options.shared = false;
3716 var markers = [markText(doc, from, to, options, type)], primary = markers[0];
3803 var markers = [markText(doc, from, to, options, type)], primary = markers[0];
3717 var widget = options.replacedWith;
3804 var widget = options.replacedWith;
3718 linkedDocs(doc, function(doc) {
3805 linkedDocs(doc, function(doc) {
3719 if (widget) options.replacedWith = widget.cloneNode(true);
3806 if (widget) options.replacedWith = widget.cloneNode(true);
3720 markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type));
3807 markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type));
3721 for (var i = 0; i < doc.linked.length; ++i)
3808 for (var i = 0; i < doc.linked.length; ++i)
3722 if (doc.linked[i].isParent) return;
3809 if (doc.linked[i].isParent) return;
3723 primary = lst(markers);
3810 primary = lst(markers);
3724 });
3811 });
3725 return new SharedTextMarker(markers, primary);
3812 return new SharedTextMarker(markers, primary);
3726 }
3813 }
3727
3814
3728 // TEXTMARKER SPANS
3815 // TEXTMARKER SPANS
3729
3816
3730 function getMarkedSpanFor(spans, marker) {
3817 function getMarkedSpanFor(spans, marker) {
3731 if (spans) for (var i = 0; i < spans.length; ++i) {
3818 if (spans) for (var i = 0; i < spans.length; ++i) {
3732 var span = spans[i];
3819 var span = spans[i];
3733 if (span.marker == marker) return span;
3820 if (span.marker == marker) return span;
3734 }
3821 }
3735 }
3822 }
3736 function removeMarkedSpan(spans, span) {
3823 function removeMarkedSpan(spans, span) {
3737 for (var r, i = 0; i < spans.length; ++i)
3824 for (var r, i = 0; i < spans.length; ++i)
3738 if (spans[i] != span) (r || (r = [])).push(spans[i]);
3825 if (spans[i] != span) (r || (r = [])).push(spans[i]);
3739 return r;
3826 return r;
3740 }
3827 }
3741 function addMarkedSpan(line, span) {
3828 function addMarkedSpan(line, span) {
3742 line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span];
3829 line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span];
3743 span.marker.attachLine(line);
3830 span.marker.attachLine(line);
3744 }
3831 }
3745
3832
3746 function markedSpansBefore(old, startCh, isInsert) {
3833 function markedSpansBefore(old, startCh, isInsert) {
3747 if (old) for (var i = 0, nw; i < old.length; ++i) {
3834 if (old) for (var i = 0, nw; i < old.length; ++i) {
3748 var span = old[i], marker = span.marker;
3835 var span = old[i], marker = span.marker;
3749 var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh);
3836 var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh);
3750 if (startsBefore || marker.type == "bookmark" && span.from == startCh && (!isInsert || !span.marker.insertLeft)) {
3837 if (startsBefore || marker.type == "bookmark" && span.from == startCh && (!isInsert || !span.marker.insertLeft)) {
3751 var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh);
3838 var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh);
3752 (nw || (nw = [])).push({from: span.from,
3839 (nw || (nw = [])).push({from: span.from,
3753 to: endsAfter ? null : span.to,
3840 to: endsAfter ? null : span.to,
3754 marker: marker});
3841 marker: marker});
3755 }
3842 }
3756 }
3843 }
3757 return nw;
3844 return nw;
3758 }
3845 }
3759
3846
3760 function markedSpansAfter(old, endCh, isInsert) {
3847 function markedSpansAfter(old, endCh, isInsert) {
3761 if (old) for (var i = 0, nw; i < old.length; ++i) {
3848 if (old) for (var i = 0, nw; i < old.length; ++i) {
3762 var span = old[i], marker = span.marker;
3849 var span = old[i], marker = span.marker;
3763 var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh);
3850 var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh);
3764 if (endsAfter || marker.type == "bookmark" && span.from == endCh && (!isInsert || span.marker.insertLeft)) {
3851 if (endsAfter || marker.type == "bookmark" && span.from == endCh && (!isInsert || span.marker.insertLeft)) {
3765 var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh);
3852 var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh);
3766 (nw || (nw = [])).push({from: startsBefore ? null : span.from - endCh,
3853 (nw || (nw = [])).push({from: startsBefore ? null : span.from - endCh,
3767 to: span.to == null ? null : span.to - endCh,
3854 to: span.to == null ? null : span.to - endCh,
3768 marker: marker});
3855 marker: marker});
3769 }
3856 }
3770 }
3857 }
3771 return nw;
3858 return nw;
3772 }
3859 }
3773
3860
3774 function stretchSpansOverChange(doc, change) {
3861 function stretchSpansOverChange(doc, change) {
3775 var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans;
3862 var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans;
3776 var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans;
3863 var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans;
3777 if (!oldFirst && !oldLast) return null;
3864 if (!oldFirst && !oldLast) return null;
3778
3865
3779 var startCh = change.from.ch, endCh = change.to.ch, isInsert = posEq(change.from, change.to);
3866 var startCh = change.from.ch, endCh = change.to.ch, isInsert = posEq(change.from, change.to);
3780 // Get the spans that 'stick out' on both sides
3867 // Get the spans that 'stick out' on both sides
3781 var first = markedSpansBefore(oldFirst, startCh, isInsert);
3868 var first = markedSpansBefore(oldFirst, startCh, isInsert);
3782 var last = markedSpansAfter(oldLast, endCh, isInsert);
3869 var last = markedSpansAfter(oldLast, endCh, isInsert);
3783
3870
3784 // Next, merge those two ends
3871 // Next, merge those two ends
3785 var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0);
3872 var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0);
3786 if (first) {
3873 if (first) {
3787 // Fix up .to properties of first
3874 // Fix up .to properties of first
3788 for (var i = 0; i < first.length; ++i) {
3875 for (var i = 0; i < first.length; ++i) {
3789 var span = first[i];
3876 var span = first[i];
3790 if (span.to == null) {
3877 if (span.to == null) {
3791 var found = getMarkedSpanFor(last, span.marker);
3878 var found = getMarkedSpanFor(last, span.marker);
3792 if (!found) span.to = startCh;
3879 if (!found) span.to = startCh;
3793 else if (sameLine) span.to = found.to == null ? null : found.to + offset;
3880 else if (sameLine) span.to = found.to == null ? null : found.to + offset;
3794 }
3881 }
3795 }
3882 }
3796 }
3883 }
3797 if (last) {
3884 if (last) {
3798 // Fix up .from in last (or move them into first in case of sameLine)
3885 // Fix up .from in last (or move them into first in case of sameLine)
3799 for (var i = 0; i < last.length; ++i) {
3886 for (var i = 0; i < last.length; ++i) {
3800 var span = last[i];
3887 var span = last[i];
3801 if (span.to != null) span.to += offset;
3888 if (span.to != null) span.to += offset;
3802 if (span.from == null) {
3889 if (span.from == null) {
3803 var found = getMarkedSpanFor(first, span.marker);
3890 var found = getMarkedSpanFor(first, span.marker);
3804 if (!found) {
3891 if (!found) {
3805 span.from = offset;
3892 span.from = offset;
3806 if (sameLine) (first || (first = [])).push(span);
3893 if (sameLine) (first || (first = [])).push(span);
3807 }
3894 }
3808 } else {
3895 } else {
3809 span.from += offset;
3896 span.from += offset;
3810 if (sameLine) (first || (first = [])).push(span);
3897 if (sameLine) (first || (first = [])).push(span);
3811 }
3898 }
3812 }
3899 }
3813 }
3900 }
3901 if (sameLine && first) {
3902 // Make sure we didn't create any zero-length spans
3903 for (var i = 0; i < first.length; ++i)
3904 if (first[i].from != null && first[i].from == first[i].to && first[i].marker.type != "bookmark")
3905 first.splice(i--, 1);
3906 if (!first.length) first = null;
3907 }
3814
3908
3815 var newMarkers = [first];
3909 var newMarkers = [first];
3816 if (!sameLine) {
3910 if (!sameLine) {
3817 // Fill gap with whole-line-spans
3911 // Fill gap with whole-line-spans
3818 var gap = change.text.length - 2, gapMarkers;
3912 var gap = change.text.length - 2, gapMarkers;
3819 if (gap > 0 && first)
3913 if (gap > 0 && first)
3820 for (var i = 0; i < first.length; ++i)
3914 for (var i = 0; i < first.length; ++i)
3821 if (first[i].to == null)
3915 if (first[i].to == null)
3822 (gapMarkers || (gapMarkers = [])).push({from: null, to: null, marker: first[i].marker});
3916 (gapMarkers || (gapMarkers = [])).push({from: null, to: null, marker: first[i].marker});
3823 for (var i = 0; i < gap; ++i)
3917 for (var i = 0; i < gap; ++i)
3824 newMarkers.push(gapMarkers);
3918 newMarkers.push(gapMarkers);
3825 newMarkers.push(last);
3919 newMarkers.push(last);
3826 }
3920 }
3827 return newMarkers;
3921 return newMarkers;
3828 }
3922 }
3829
3923
3830 function mergeOldSpans(doc, change) {
3924 function mergeOldSpans(doc, change) {
3831 var old = getOldSpans(doc, change);
3925 var old = getOldSpans(doc, change);
3832 var stretched = stretchSpansOverChange(doc, change);
3926 var stretched = stretchSpansOverChange(doc, change);
3833 if (!old) return stretched;
3927 if (!old) return stretched;
3834 if (!stretched) return old;
3928 if (!stretched) return old;
3835
3929
3836 for (var i = 0; i < old.length; ++i) {
3930 for (var i = 0; i < old.length; ++i) {
3837 var oldCur = old[i], stretchCur = stretched[i];
3931 var oldCur = old[i], stretchCur = stretched[i];
3838 if (oldCur && stretchCur) {
3932 if (oldCur && stretchCur) {
3839 spans: for (var j = 0; j < stretchCur.length; ++j) {
3933 spans: for (var j = 0; j < stretchCur.length; ++j) {
3840 var span = stretchCur[j];
3934 var span = stretchCur[j];
3841 for (var k = 0; k < oldCur.length; ++k)
3935 for (var k = 0; k < oldCur.length; ++k)
3842 if (oldCur[k].marker == span.marker) continue spans;
3936 if (oldCur[k].marker == span.marker) continue spans;
3843 oldCur.push(span);
3937 oldCur.push(span);
3844 }
3938 }
3845 } else if (stretchCur) {
3939 } else if (stretchCur) {
3846 old[i] = stretchCur;
3940 old[i] = stretchCur;
3847 }
3941 }
3848 }
3942 }
3849 return old;
3943 return old;
3850 }
3944 }
3851
3945
3852 function removeReadOnlyRanges(doc, from, to) {
3946 function removeReadOnlyRanges(doc, from, to) {
3853 var markers = null;
3947 var markers = null;
3854 doc.iter(from.line, to.line + 1, function(line) {
3948 doc.iter(from.line, to.line + 1, function(line) {
3855 if (line.markedSpans) for (var i = 0; i < line.markedSpans.length; ++i) {
3949 if (line.markedSpans) for (var i = 0; i < line.markedSpans.length; ++i) {
3856 var mark = line.markedSpans[i].marker;
3950 var mark = line.markedSpans[i].marker;
3857 if (mark.readOnly && (!markers || indexOf(markers, mark) == -1))
3951 if (mark.readOnly && (!markers || indexOf(markers, mark) == -1))
3858 (markers || (markers = [])).push(mark);
3952 (markers || (markers = [])).push(mark);
3859 }
3953 }
3860 });
3954 });
3861 if (!markers) return null;
3955 if (!markers) return null;
3862 var parts = [{from: from, to: to}];
3956 var parts = [{from: from, to: to}];
3863 for (var i = 0; i < markers.length; ++i) {
3957 for (var i = 0; i < markers.length; ++i) {
3864 var mk = markers[i], m = mk.find();
3958 var mk = markers[i], m = mk.find();
3865 for (var j = 0; j < parts.length; ++j) {
3959 for (var j = 0; j < parts.length; ++j) {
3866 var p = parts[j];
3960 var p = parts[j];
3867 if (posLess(p.to, m.from) || posLess(m.to, p.from)) continue;
3961 if (posLess(p.to, m.from) || posLess(m.to, p.from)) continue;
3868 var newParts = [j, 1];
3962 var newParts = [j, 1];
3869 if (posLess(p.from, m.from) || !mk.inclusiveLeft && posEq(p.from, m.from))
3963 if (posLess(p.from, m.from) || !mk.inclusiveLeft && posEq(p.from, m.from))
3870 newParts.push({from: p.from, to: m.from});
3964 newParts.push({from: p.from, to: m.from});
3871 if (posLess(m.to, p.to) || !mk.inclusiveRight && posEq(p.to, m.to))
3965 if (posLess(m.to, p.to) || !mk.inclusiveRight && posEq(p.to, m.to))
3872 newParts.push({from: m.to, to: p.to});
3966 newParts.push({from: m.to, to: p.to});
3873 parts.splice.apply(parts, newParts);
3967 parts.splice.apply(parts, newParts);
3874 j += newParts.length - 1;
3968 j += newParts.length - 1;
3875 }
3969 }
3876 }
3970 }
3877 return parts;
3971 return parts;
3878 }
3972 }
3879
3973
3880 function collapsedSpanAt(line, ch) {
3974 function collapsedSpanAt(line, ch) {
3881 var sps = sawCollapsedSpans && line.markedSpans, found;
3975 var sps = sawCollapsedSpans && line.markedSpans, found;
3882 if (sps) for (var sp, i = 0; i < sps.length; ++i) {
3976 if (sps) for (var sp, i = 0; i < sps.length; ++i) {
3883 sp = sps[i];
3977 sp = sps[i];
3884 if (!sp.marker.collapsed) continue;
3978 if (!sp.marker.collapsed) continue;
3885 if ((sp.from == null || sp.from < ch) &&
3979 if ((sp.from == null || sp.from < ch) &&
3886 (sp.to == null || sp.to > ch) &&
3980 (sp.to == null || sp.to > ch) &&
3887 (!found || found.width < sp.marker.width))
3981 (!found || found.width < sp.marker.width))
3888 found = sp.marker;
3982 found = sp.marker;
3889 }
3983 }
3890 return found;
3984 return found;
3891 }
3985 }
3892 function collapsedSpanAtStart(line) { return collapsedSpanAt(line, -1); }
3986 function collapsedSpanAtStart(line) { return collapsedSpanAt(line, -1); }
3893 function collapsedSpanAtEnd(line) { return collapsedSpanAt(line, line.text.length + 1); }
3987 function collapsedSpanAtEnd(line) { return collapsedSpanAt(line, line.text.length + 1); }
3894
3988
3895 function visualLine(doc, line) {
3989 function visualLine(doc, line) {
3896 var merged;
3990 var merged;
3897 while (merged = collapsedSpanAtStart(line))
3991 while (merged = collapsedSpanAtStart(line))
3898 line = getLine(doc, merged.find().from.line);
3992 line = getLine(doc, merged.find().from.line);
3899 return line;
3993 return line;
3900 }
3994 }
3901
3995
3902 function lineIsHidden(doc, line) {
3996 function lineIsHidden(doc, line) {
3903 var sps = sawCollapsedSpans && line.markedSpans;
3997 var sps = sawCollapsedSpans && line.markedSpans;
3904 if (sps) for (var sp, i = 0; i < sps.length; ++i) {
3998 if (sps) for (var sp, i = 0; i < sps.length; ++i) {
3905 sp = sps[i];
3999 sp = sps[i];
3906 if (!sp.marker.collapsed) continue;
4000 if (!sp.marker.collapsed) continue;
3907 if (sp.from == null) return true;
4001 if (sp.from == null) return true;
4002 if (sp.marker.replacedWith) continue;
3908 if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp))
4003 if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp))
3909 return true;
4004 return true;
3910 }
4005 }
3911 }
4006 }
3912 function lineIsHiddenInner(doc, line, span) {
4007 function lineIsHiddenInner(doc, line, span) {
3913 if (span.to == null) {
4008 if (span.to == null) {
3914 var end = span.marker.find().to, endLine = getLine(doc, end.line);
4009 var end = span.marker.find().to, endLine = getLine(doc, end.line);
3915 return lineIsHiddenInner(doc, endLine, getMarkedSpanFor(endLine.markedSpans, span.marker));
4010 return lineIsHiddenInner(doc, endLine, getMarkedSpanFor(endLine.markedSpans, span.marker));
3916 }
4011 }
3917 if (span.marker.inclusiveRight && span.to == line.text.length)
4012 if (span.marker.inclusiveRight && span.to == line.text.length)
3918 return true;
4013 return true;
3919 for (var sp, i = 0; i < line.markedSpans.length; ++i) {
4014 for (var sp, i = 0; i < line.markedSpans.length; ++i) {
3920 sp = line.markedSpans[i];
4015 sp = line.markedSpans[i];
3921 if (sp.marker.collapsed && sp.from == span.to &&
4016 if (sp.marker.collapsed && !sp.marker.replacedWith && sp.from == span.to &&
3922 (sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
4017 (sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
3923 lineIsHiddenInner(doc, line, sp)) return true;
4018 lineIsHiddenInner(doc, line, sp)) return true;
3924 }
4019 }
3925 }
4020 }
3926
4021
3927 function detachMarkedSpans(line) {
4022 function detachMarkedSpans(line) {
3928 var spans = line.markedSpans;
4023 var spans = line.markedSpans;
3929 if (!spans) return;
4024 if (!spans) return;
3930 for (var i = 0; i < spans.length; ++i)
4025 for (var i = 0; i < spans.length; ++i)
3931 spans[i].marker.detachLine(line);
4026 spans[i].marker.detachLine(line);
3932 line.markedSpans = null;
4027 line.markedSpans = null;
3933 }
4028 }
3934
4029
3935 function attachMarkedSpans(line, spans) {
4030 function attachMarkedSpans(line, spans) {
3936 if (!spans) return;
4031 if (!spans) return;
3937 for (var i = 0; i < spans.length; ++i)
4032 for (var i = 0; i < spans.length; ++i)
3938 spans[i].marker.attachLine(line);
4033 spans[i].marker.attachLine(line);
3939 line.markedSpans = spans;
4034 line.markedSpans = spans;
3940 }
4035 }
3941
4036
3942 // LINE WIDGETS
4037 // LINE WIDGETS
3943
4038
3944 var LineWidget = CodeMirror.LineWidget = function(cm, node, options) {
4039 var LineWidget = CodeMirror.LineWidget = function(cm, node, options) {
3945 for (var opt in options) if (options.hasOwnProperty(opt))
4040 for (var opt in options) if (options.hasOwnProperty(opt))
3946 this[opt] = options[opt];
4041 this[opt] = options[opt];
3947 this.cm = cm;
4042 this.cm = cm;
3948 this.node = node;
4043 this.node = node;
3949 };
4044 };
3950 function widgetOperation(f) {
4045 function widgetOperation(f) {
3951 return function() {
4046 return function() {
3952 var withOp = !this.cm.curOp;
4047 var withOp = !this.cm.curOp;
3953 if (withOp) startOperation(this.cm);
4048 if (withOp) startOperation(this.cm);
3954 try {var result = f.apply(this, arguments);}
4049 try {var result = f.apply(this, arguments);}
3955 finally {if (withOp) endOperation(this.cm);}
4050 finally {if (withOp) endOperation(this.cm);}
3956 return result;
4051 return result;
3957 };
4052 };
3958 }
4053 }
3959 LineWidget.prototype.clear = widgetOperation(function() {
4054 LineWidget.prototype.clear = widgetOperation(function() {
3960 var ws = this.line.widgets, no = lineNo(this.line);
4055 var ws = this.line.widgets, no = lineNo(this.line);
3961 if (no == null || !ws) return;
4056 if (no == null || !ws) return;
3962 for (var i = 0; i < ws.length; ++i) if (ws[i] == this) ws.splice(i--, 1);
4057 for (var i = 0; i < ws.length; ++i) if (ws[i] == this) ws.splice(i--, 1);
3963 if (!ws.length) this.line.widgets = null;
4058 if (!ws.length) this.line.widgets = null;
3964 updateLineHeight(this.line, Math.max(0, this.line.height - widgetHeight(this)));
4059 updateLineHeight(this.line, Math.max(0, this.line.height - widgetHeight(this)));
3965 regChange(this.cm, no, no + 1);
4060 regChange(this.cm, no, no + 1);
3966 });
4061 });
3967 LineWidget.prototype.changed = widgetOperation(function() {
4062 LineWidget.prototype.changed = widgetOperation(function() {
3968 var oldH = this.height;
4063 var oldH = this.height;
3969 this.height = null;
4064 this.height = null;
3970 var diff = widgetHeight(this) - oldH;
4065 var diff = widgetHeight(this) - oldH;
3971 if (!diff) return;
4066 if (!diff) return;
3972 updateLineHeight(this.line, this.line.height + diff);
4067 updateLineHeight(this.line, this.line.height + diff);
3973 var no = lineNo(this.line);
4068 var no = lineNo(this.line);
3974 regChange(this.cm, no, no + 1);
4069 regChange(this.cm, no, no + 1);
3975 });
4070 });
3976
4071
3977 function widgetHeight(widget) {
4072 function widgetHeight(widget) {
3978 if (widget.height != null) return widget.height;
4073 if (widget.height != null) return widget.height;
3979 if (!widget.node.parentNode || widget.node.parentNode.nodeType != 1)
4074 if (!widget.node.parentNode || widget.node.parentNode.nodeType != 1)
3980 removeChildrenAndAdd(widget.cm.display.measure, elt("div", [widget.node], null, "position: relative"));
4075 removeChildrenAndAdd(widget.cm.display.measure, elt("div", [widget.node], null, "position: relative"));
3981 return widget.height = widget.node.offsetHeight;
4076 return widget.height = widget.node.offsetHeight;
3982 }
4077 }
3983
4078
3984 function addLineWidget(cm, handle, node, options) {
4079 function addLineWidget(cm, handle, node, options) {
3985 var widget = new LineWidget(cm, node, options);
4080 var widget = new LineWidget(cm, node, options);
3986 if (widget.noHScroll) cm.display.alignWidgets = true;
4081 if (widget.noHScroll) cm.display.alignWidgets = true;
3987 changeLine(cm, handle, function(line) {
4082 changeLine(cm, handle, function(line) {
3988 (line.widgets || (line.widgets = [])).push(widget);
4083 (line.widgets || (line.widgets = [])).push(widget);
3989 widget.line = line;
4084 widget.line = line;
3990 if (!lineIsHidden(cm.doc, line) || widget.showIfHidden) {
4085 if (!lineIsHidden(cm.doc, line) || widget.showIfHidden) {
3991 var aboveVisible = heightAtLine(cm, line) < cm.display.scroller.scrollTop;
4086 var aboveVisible = heightAtLine(cm, line) < cm.display.scroller.scrollTop;
3992 updateLineHeight(line, line.height + widgetHeight(widget));
4087 updateLineHeight(line, line.height + widgetHeight(widget));
3993 if (aboveVisible) addToScrollPos(cm, 0, widget.height);
4088 if (aboveVisible) addToScrollPos(cm, 0, widget.height);
3994 }
4089 }
3995 return true;
4090 return true;
3996 });
4091 });
3997 return widget;
4092 return widget;
3998 }
4093 }
3999
4094
4000 // LINE DATA STRUCTURE
4095 // LINE DATA STRUCTURE
4001
4096
4002 // Line objects. These hold state related to a line, including
4097 // Line objects. These hold state related to a line, including
4003 // highlighting info (the styles array).
4098 // highlighting info (the styles array).
4004 function makeLine(text, markedSpans, estimateHeight) {
4099 function makeLine(text, markedSpans, estimateHeight) {
4005 var line = {text: text};
4100 var line = {text: text};
4006 attachMarkedSpans(line, markedSpans);
4101 attachMarkedSpans(line, markedSpans);
4007 line.height = estimateHeight ? estimateHeight(line) : 1;
4102 line.height = estimateHeight ? estimateHeight(line) : 1;
4008 return line;
4103 return line;
4009 }
4104 }
4010
4105
4011 function updateLine(line, text, markedSpans, estimateHeight) {
4106 function updateLine(line, text, markedSpans, estimateHeight) {
4012 line.text = text;
4107 line.text = text;
4013 if (line.stateAfter) line.stateAfter = null;
4108 if (line.stateAfter) line.stateAfter = null;
4014 if (line.styles) line.styles = null;
4109 if (line.styles) line.styles = null;
4015 if (line.order != null) line.order = null;
4110 if (line.order != null) line.order = null;
4016 detachMarkedSpans(line);
4111 detachMarkedSpans(line);
4017 attachMarkedSpans(line, markedSpans);
4112 attachMarkedSpans(line, markedSpans);
4018 var estHeight = estimateHeight ? estimateHeight(line) : 1;
4113 var estHeight = estimateHeight ? estimateHeight(line) : 1;
4019 if (estHeight != line.height) updateLineHeight(line, estHeight);
4114 if (estHeight != line.height) updateLineHeight(line, estHeight);
4020 }
4115 }
4021
4116
4022 function cleanUpLine(line) {
4117 function cleanUpLine(line) {
4023 line.parent = null;
4118 line.parent = null;
4024 detachMarkedSpans(line);
4119 detachMarkedSpans(line);
4025 }
4120 }
4026
4121
4027 // Run the given mode's parser over a line, update the styles
4122 // Run the given mode's parser over a line, update the styles
4028 // array, which contains alternating fragments of text and CSS
4123 // array, which contains alternating fragments of text and CSS
4029 // classes.
4124 // classes.
4030 function runMode(cm, text, mode, state, f) {
4125 function runMode(cm, text, mode, state, f) {
4031 var flattenSpans = mode.flattenSpans;
4126 var flattenSpans = mode.flattenSpans;
4032 if (flattenSpans == null) flattenSpans = cm.options.flattenSpans;
4127 if (flattenSpans == null) flattenSpans = cm.options.flattenSpans;
4033 var curText = "", curStyle = null;
4128 var curStart = 0, curStyle = null;
4034 var stream = new StringStream(text, cm.options.tabSize), style;
4129 var stream = new StringStream(text, cm.options.tabSize), style;
4035 if (text == "" && mode.blankLine) mode.blankLine(state);
4130 if (text == "" && mode.blankLine) mode.blankLine(state);
4036 while (!stream.eol()) {
4131 while (!stream.eol()) {
4037 if (stream.pos > cm.options.maxHighlightLength) {
4132 if (stream.pos > cm.options.maxHighlightLength) {
4038 flattenSpans = false;
4133 flattenSpans = false;
4039 // Webkit seems to refuse to render text nodes longer than 57444 characters
4134 // Webkit seems to refuse to render text nodes longer than 57444 characters
4040 stream.pos = Math.min(text.length, stream.start + 50000);
4135 stream.pos = Math.min(text.length, stream.start + 50000);
4041 style = null;
4136 style = null;
4042 } else {
4137 } else {
4043 style = mode.token(stream, state);
4138 style = mode.token(stream, state);
4044 }
4139 }
4045 var substr = stream.current();
4140 if (!flattenSpans || curStyle != style) {
4141 if (curStart < stream.start) f(stream.start, curStyle);
4142 curStart = stream.start; curStyle = style;
4143 }
4046 stream.start = stream.pos;
4144 stream.start = stream.pos;
4047 if (!flattenSpans || curStyle != style) {
4145 }
4048 if (curText) f(curText, curStyle);
4146 if (curStart < stream.pos) f(stream.pos, curStyle);
4049 curText = substr; curStyle = style;
4050 } else curText = curText + substr;
4051 }
4052 if (curText) f(curText, curStyle);
4053 }
4147 }
4054
4148
4055 function highlightLine(cm, line, state) {
4149 function highlightLine(cm, line, state) {
4056 // A styles array always starts with a number identifying the
4150 // A styles array always starts with a number identifying the
4057 // mode/overlays that it is based on (for easy invalidation).
4151 // mode/overlays that it is based on (for easy invalidation).
4058 var st = [cm.state.modeGen];
4152 var st = [cm.state.modeGen];
4059 // Compute the base array of styles
4153 // Compute the base array of styles
4060 runMode(cm, line.text, cm.doc.mode, state, function(txt, style) {st.push(txt, style);});
4154 runMode(cm, line.text, cm.doc.mode, state, function(end, style) {st.push(end, style);});
4061
4155
4062 // Run overlays, adjust style array.
4156 // Run overlays, adjust style array.
4063 for (var o = 0; o < cm.state.overlays.length; ++o) {
4157 for (var o = 0; o < cm.state.overlays.length; ++o) {
4064 var overlay = cm.state.overlays[o], i = 1;
4158 var overlay = cm.state.overlays[o], i = 1, at = 0;
4065 runMode(cm, line.text, overlay.mode, true, function(txt, style) {
4159 runMode(cm, line.text, overlay.mode, true, function(end, style) {
4066 var start = i, len = txt.length;
4160 var start = i;
4067 // Ensure there's a token end at the current position, and that i points at it
4161 // Ensure there's a token end at the current position, and that i points at it
4068 while (len) {
4162 while (at < end) {
4069 var cur = st[i], len_ = cur.length;
4163 var i_end = st[i];
4070 if (len_ <= len) {
4164 if (i_end > end)
4071 len -= len_;
4165 st.splice(i, 1, end, st[i+1], i_end);
4072 } else {
4073 st.splice(i, 1, cur.slice(0, len), st[i+1], cur.slice(len));
4074 len = 0;
4075 }
4076 i += 2;
4166 i += 2;
4167 at = Math.min(end, i_end);
4077 }
4168 }
4078 if (!style) return;
4169 if (!style) return;
4079 if (overlay.opaque) {
4170 if (overlay.opaque) {
4080 st.splice(start, i - start, txt, style);
4171 st.splice(start, i - start, end, style);
4081 i = start + 2;
4172 i = start + 2;
4082 } else {
4173 } else {
4083 for (; start < i; start += 2) {
4174 for (; start < i; start += 2) {
4084 var cur = st[start+1];
4175 var cur = st[start+1];
4085 st[start+1] = cur ? cur + " " + style : style;
4176 st[start+1] = cur ? cur + " " + style : style;
4086 }
4177 }
4087 }
4178 }
4088 });
4179 });
4089 }
4180 }
4090
4181
4091 return st;
4182 return st;
4092 }
4183 }
4093
4184
4094 function getLineStyles(cm, line) {
4185 function getLineStyles(cm, line) {
4095 if (!line.styles || line.styles[0] != cm.state.modeGen)
4186 if (!line.styles || line.styles[0] != cm.state.modeGen)
4096 line.styles = highlightLine(cm, line, line.stateAfter = getStateBefore(cm, lineNo(line)));
4187 line.styles = highlightLine(cm, line, line.stateAfter = getStateBefore(cm, lineNo(line)));
4097 return line.styles;
4188 return line.styles;
4098 }
4189 }
4099
4190
4100 // Lightweight form of highlight -- proceed over this line and
4191 // Lightweight form of highlight -- proceed over this line and
4101 // update state, but don't save a style array.
4192 // update state, but don't save a style array.
4102 function processLine(cm, line, state) {
4193 function processLine(cm, line, state) {
4103 var mode = cm.doc.mode;
4194 var mode = cm.doc.mode;
4104 var stream = new StringStream(line.text, cm.options.tabSize);
4195 var stream = new StringStream(line.text, cm.options.tabSize);
4105 if (line.text == "" && mode.blankLine) mode.blankLine(state);
4196 if (line.text == "" && mode.blankLine) mode.blankLine(state);
4106 while (!stream.eol() && stream.pos <= cm.options.maxHighlightLength) {
4197 while (!stream.eol() && stream.pos <= cm.options.maxHighlightLength) {
4107 mode.token(stream, state);
4198 mode.token(stream, state);
4108 stream.start = stream.pos;
4199 stream.start = stream.pos;
4109 }
4200 }
4110 }
4201 }
4111
4202
4112 var styleToClassCache = {};
4203 var styleToClassCache = {};
4113 function styleToClass(style) {
4204 function styleToClass(style) {
4114 if (!style) return null;
4205 if (!style) return null;
4115 return styleToClassCache[style] ||
4206 return styleToClassCache[style] ||
4116 (styleToClassCache[style] = "cm-" + style.replace(/ +/g, " cm-"));
4207 (styleToClassCache[style] = "cm-" + style.replace(/ +/g, " cm-"));
4117 }
4208 }
4118
4209
4119 function lineContent(cm, realLine, measure) {
4210 function lineContent(cm, realLine, measure) {
4120 var merged, line = realLine, lineBefore, sawBefore, simple = true;
4211 var merged, line = realLine, empty = true;
4121 while (merged = collapsedSpanAtStart(line)) {
4212 while (merged = collapsedSpanAtStart(line))
4122 simple = false;
4123 line = getLine(cm.doc, merged.find().from.line);
4213 line = getLine(cm.doc, merged.find().from.line);
4124 if (!lineBefore) lineBefore = line;
4125 }
4126
4214
4127 var builder = {pre: elt("pre"), col: 0, pos: 0, display: !measure,
4215 var builder = {pre: elt("pre"), col: 0, pos: 0, display: !measure,
4128 measure: null, addedOne: false, cm: cm};
4216 measure: null, measuredSomething: false, cm: cm};
4129 if (line.textClass) builder.pre.className = line.textClass;
4217 if (line.textClass) builder.pre.className = line.textClass;
4130
4218
4131 do {
4219 do {
4220 if (line.text) empty = false;
4132 builder.measure = line == realLine && measure;
4221 builder.measure = line == realLine && measure;
4133 builder.pos = 0;
4222 builder.pos = 0;
4134 builder.addToken = builder.measure ? buildTokenMeasure : buildToken;
4223 builder.addToken = builder.measure ? buildTokenMeasure : buildToken;
4135 if ((ie || webkit) && cm.getOption("lineWrapping"))
4224 if ((ie || webkit) && cm.getOption("lineWrapping"))
4136 builder.addToken = buildTokenSplitSpaces(builder.addToken);
4225 builder.addToken = buildTokenSplitSpaces(builder.addToken);
4137 if (measure && sawBefore && line != realLine && !builder.addedOne) {
4226 var next = insertLineContent(line, builder, getLineStyles(cm, line));
4227 if (measure && line == realLine && !builder.measuredSomething) {
4138 measure[0] = builder.pre.appendChild(zeroWidthElement(cm.display.measure));
4228 measure[0] = builder.pre.appendChild(zeroWidthElement(cm.display.measure));
4139 builder.addedOne = true;
4229 builder.measuredSomething = true;
4140 }
4230 }
4141 var next = insertLineContent(line, builder, getLineStyles(cm, line));
4231 if (next) line = getLine(cm.doc, next.to.line);
4142 sawBefore = line == lineBefore;
4143 if (next) {
4144 line = getLine(cm.doc, next.to.line);
4145 simple = false;
4146 }
4147 } while (next);
4232 } while (next);
4148
4233
4149 if (measure && !builder.addedOne)
4234 if (measure && !builder.measuredSomething && !measure[0])
4150 measure[0] = builder.pre.appendChild(simple ? elt("span", "\u00a0") : zeroWidthElement(cm.display.measure));
4235 measure[0] = builder.pre.appendChild(empty ? elt("span", "\u00a0") : zeroWidthElement(cm.display.measure));
4151 if (!builder.pre.firstChild && !lineIsHidden(cm.doc, realLine))
4236 if (!builder.pre.firstChild && !lineIsHidden(cm.doc, realLine))
4152 builder.pre.appendChild(document.createTextNode("\u00a0"));
4237 builder.pre.appendChild(document.createTextNode("\u00a0"));
4153
4238
4154 var order;
4239 var order;
4155 // Work around problem with the reported dimensions of single-char
4240 // Work around problem with the reported dimensions of single-char
4156 // direction spans on IE (issue #1129). See also the comment in
4241 // direction spans on IE (issue #1129). See also the comment in
4157 // cursorCoords.
4242 // cursorCoords.
4158 if (measure && ie && (order = getOrder(line))) {
4243 if (measure && ie && (order = getOrder(line))) {
4159 var l = order.length - 1;
4244 var l = order.length - 1;
4160 if (order[l].from == order[l].to) --l;
4245 if (order[l].from == order[l].to) --l;
4161 var last = order[l], prev = order[l - 1];
4246 var last = order[l], prev = order[l - 1];
4162 if (last.from + 1 == last.to && prev && last.level < prev.level) {
4247 if (last.from + 1 == last.to && prev && last.level < prev.level) {
4163 var span = measure[builder.pos - 1];
4248 var span = measure[builder.pos - 1];
4164 if (span) span.parentNode.insertBefore(span.measureRight = zeroWidthElement(cm.display.measure),
4249 if (span) span.parentNode.insertBefore(span.measureRight = zeroWidthElement(cm.display.measure),
4165 span.nextSibling);
4250 span.nextSibling);
4166 }
4251 }
4167 }
4252 }
4168
4253
4169 signal(cm, "renderLine", cm, realLine, builder.pre);
4254 signal(cm, "renderLine", cm, realLine, builder.pre);
4170 return builder.pre;
4255 return builder.pre;
4171 }
4256 }
4172
4257
4173 var tokenSpecialChars = /[\t\u0000-\u0019\u00ad\u200b\u2028\u2029\uFEFF]/g;
4258 var tokenSpecialChars = /[\t\u0000-\u0019\u00ad\u200b\u2028\u2029\uFEFF]/g;
4174 function buildToken(builder, text, style, startStyle, endStyle) {
4259 function buildToken(builder, text, style, startStyle, endStyle) {
4175 if (!text) return;
4260 if (!text) return;
4176 if (!tokenSpecialChars.test(text)) {
4261 if (!tokenSpecialChars.test(text)) {
4177 builder.col += text.length;
4262 builder.col += text.length;
4178 var content = document.createTextNode(text);
4263 var content = document.createTextNode(text);
4179 } else {
4264 } else {
4180 var content = document.createDocumentFragment(), pos = 0;
4265 var content = document.createDocumentFragment(), pos = 0;
4181 while (true) {
4266 while (true) {
4182 tokenSpecialChars.lastIndex = pos;
4267 tokenSpecialChars.lastIndex = pos;
4183 var m = tokenSpecialChars.exec(text);
4268 var m = tokenSpecialChars.exec(text);
4184 var skipped = m ? m.index - pos : text.length - pos;
4269 var skipped = m ? m.index - pos : text.length - pos;
4185 if (skipped) {
4270 if (skipped) {
4186 content.appendChild(document.createTextNode(text.slice(pos, pos + skipped)));
4271 content.appendChild(document.createTextNode(text.slice(pos, pos + skipped)));
4187 builder.col += skipped;
4272 builder.col += skipped;
4188 }
4273 }
4189 if (!m) break;
4274 if (!m) break;
4190 pos += skipped + 1;
4275 pos += skipped + 1;
4191 if (m[0] == "\t") {
4276 if (m[0] == "\t") {
4192 var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize;
4277 var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize;
4193 content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab"));
4278 content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab"));
4194 builder.col += tabWidth;
4279 builder.col += tabWidth;
4195 } else {
4280 } else {
4196 var token = elt("span", "\u2022", "cm-invalidchar");
4281 var token = elt("span", "\u2022", "cm-invalidchar");
4197 token.title = "\\u" + m[0].charCodeAt(0).toString(16);
4282 token.title = "\\u" + m[0].charCodeAt(0).toString(16);
4198 content.appendChild(token);
4283 content.appendChild(token);
4199 builder.col += 1;
4284 builder.col += 1;
4200 }
4285 }
4201 }
4286 }
4202 }
4287 }
4203 if (style || startStyle || endStyle || builder.measure) {
4288 if (style || startStyle || endStyle || builder.measure) {
4204 var fullStyle = style || "";
4289 var fullStyle = style || "";
4205 if (startStyle) fullStyle += startStyle;
4290 if (startStyle) fullStyle += startStyle;
4206 if (endStyle) fullStyle += endStyle;
4291 if (endStyle) fullStyle += endStyle;
4207 return builder.pre.appendChild(elt("span", [content], fullStyle));
4292 return builder.pre.appendChild(elt("span", [content], fullStyle));
4208 }
4293 }
4209 builder.pre.appendChild(content);
4294 builder.pre.appendChild(content);
4210 }
4295 }
4211
4296
4212 function buildTokenMeasure(builder, text, style, startStyle, endStyle) {
4297 function buildTokenMeasure(builder, text, style, startStyle, endStyle) {
4213 var wrapping = builder.cm.options.lineWrapping;
4298 var wrapping = builder.cm.options.lineWrapping;
4214 for (var i = 0; i < text.length; ++i) {
4299 for (var i = 0; i < text.length; ++i) {
4215 var ch = text.charAt(i), start = i == 0;
4300 var ch = text.charAt(i), start = i == 0;
4216 if (ch >= "\ud800" && ch < "\udbff" && i < text.length - 1) {
4301 if (ch >= "\ud800" && ch < "\udbff" && i < text.length - 1) {
4217 ch = text.slice(i, i + 2);
4302 ch = text.slice(i, i + 2);
4218 ++i;
4303 ++i;
4219 } else if (i && wrapping &&
4304 } else if (i && wrapping && spanAffectsWrapping(text, i)) {
4220 spanAffectsWrapping.test(text.slice(i - 1, i + 1))) {
4221 builder.pre.appendChild(elt("wbr"));
4305 builder.pre.appendChild(elt("wbr"));
4222 }
4306 }
4223 var span = builder.measure[builder.pos] =
4307 var span = builder.measure[builder.pos] =
4224 buildToken(builder, ch, style,
4308 buildToken(builder, ch, style,
4225 start && startStyle, i == text.length - 1 && endStyle);
4309 start && startStyle, i == text.length - 1 && endStyle);
4226 // In IE single-space nodes wrap differently than spaces
4310 // In IE single-space nodes wrap differently than spaces
4227 // embedded in larger text nodes, except when set to
4311 // embedded in larger text nodes, except when set to
4228 // white-space: normal (issue #1268).
4312 // white-space: normal (issue #1268).
4229 if (ie && wrapping && ch == " " && i && !/\s/.test(text.charAt(i - 1)) &&
4313 if (ie && wrapping && ch == " " && i && !/\s/.test(text.charAt(i - 1)) &&
4230 i < text.length - 1 && !/\s/.test(text.charAt(i + 1)))
4314 i < text.length - 1 && !/\s/.test(text.charAt(i + 1)))
4231 span.style.whiteSpace = "normal";
4315 span.style.whiteSpace = "normal";
4232 builder.pos += ch.length;
4316 builder.pos += ch.length;
4233 }
4317 }
4234 if (text.length) builder.addedOne = true;
4318 if (text.length) builder.measuredSomething = true;
4235 }
4319 }
4236
4320
4237 function buildTokenSplitSpaces(inner) {
4321 function buildTokenSplitSpaces(inner) {
4238 function split(old) {
4322 function split(old) {
4239 var out = " ";
4323 var out = " ";
4240 for (var i = 0; i < old.length - 2; ++i) out += i % 2 ? " " : "\u00a0";
4324 for (var i = 0; i < old.length - 2; ++i) out += i % 2 ? " " : "\u00a0";
4241 out += " ";
4325 out += " ";
4242 return out;
4326 return out;
4243 }
4327 }
4244 return function(builder, text, style, startStyle, endStyle) {
4328 return function(builder, text, style, startStyle, endStyle) {
4245 return inner(builder, text.replace(/ {3,}/, split), style, startStyle, endStyle);
4329 return inner(builder, text.replace(/ {3,}/, split), style, startStyle, endStyle);
4246 };
4330 };
4247 }
4331 }
4248
4332
4249 function buildCollapsedSpan(builder, size, widget) {
4333 function buildCollapsedSpan(builder, size, widget) {
4250 if (widget) {
4334 if (widget) {
4251 if (!builder.display) widget = widget.cloneNode(true);
4335 if (!builder.display) widget = widget.cloneNode(true);
4336 if (builder.measure) {
4337 builder.measure[builder.pos] = size ? widget
4338 : builder.pre.appendChild(zeroWidthElement(builder.cm.display.measure));
4339 builder.measuredSomething = true;
4340 }
4252 builder.pre.appendChild(widget);
4341 builder.pre.appendChild(widget);
4253 if (builder.measure && size) {
4254 builder.measure[builder.pos] = widget;
4255 builder.addedOne = true;
4256 }
4257 }
4342 }
4258 builder.pos += size;
4343 builder.pos += size;
4259 }
4344 }
4260
4345
4261 // Outputs a number of spans to make up a line, taking highlighting
4346 // Outputs a number of spans to make up a line, taking highlighting
4262 // and marked text into account.
4347 // and marked text into account.
4263 function insertLineContent(line, builder, styles) {
4348 function insertLineContent(line, builder, styles) {
4264 var spans = line.markedSpans;
4349 var spans = line.markedSpans, allText = line.text, at = 0;
4265 if (!spans) {
4350 if (!spans) {
4266 for (var i = 1; i < styles.length; i+=2)
4351 for (var i = 1; i < styles.length; i+=2)
4267 builder.addToken(builder, styles[i], styleToClass(styles[i+1]));
4352 builder.addToken(builder, allText.slice(at, at = styles[i]), styleToClass(styles[i+1]));
4268 return;
4353 return;
4269 }
4354 }
4270
4355
4271 var allText = line.text, len = allText.length;
4356 var len = allText.length, pos = 0, i = 1, text = "", style;
4272 var pos = 0, i = 1, text = "", style;
4273 var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, collapsed;
4357 var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, collapsed;
4274 for (;;) {
4358 for (;;) {
4275 if (nextChange == pos) { // Update current marker set
4359 if (nextChange == pos) { // Update current marker set
4276 spanStyle = spanEndStyle = spanStartStyle = "";
4360 spanStyle = spanEndStyle = spanStartStyle = "";
4277 collapsed = null; nextChange = Infinity;
4361 collapsed = null; nextChange = Infinity;
4278 var foundBookmark = null;
4362 var foundBookmark = null;
4279 for (var j = 0; j < spans.length; ++j) {
4363 for (var j = 0; j < spans.length; ++j) {
4280 var sp = spans[j], m = sp.marker;
4364 var sp = spans[j], m = sp.marker;
4281 if (sp.from <= pos && (sp.to == null || sp.to > pos)) {
4365 if (sp.from <= pos && (sp.to == null || sp.to > pos)) {
4282 if (sp.to != null && nextChange > sp.to) { nextChange = sp.to; spanEndStyle = ""; }
4366 if (sp.to != null && nextChange > sp.to) { nextChange = sp.to; spanEndStyle = ""; }
4283 if (m.className) spanStyle += " " + m.className;
4367 if (m.className) spanStyle += " " + m.className;
4284 if (m.startStyle && sp.from == pos) spanStartStyle += " " + m.startStyle;
4368 if (m.startStyle && sp.from == pos) spanStartStyle += " " + m.startStyle;
4285 if (m.endStyle && sp.to == nextChange) spanEndStyle += " " + m.endStyle;
4369 if (m.endStyle && sp.to == nextChange) spanEndStyle += " " + m.endStyle;
4286 if (m.collapsed && (!collapsed || collapsed.marker.width < m.width))
4370 if (m.collapsed && (!collapsed || collapsed.marker.size < m.size))
4287 collapsed = sp;
4371 collapsed = sp;
4288 } else if (sp.from > pos && nextChange > sp.from) {
4372 } else if (sp.from > pos && nextChange > sp.from) {
4289 nextChange = sp.from;
4373 nextChange = sp.from;
4290 }
4374 }
4291 if (m.type == "bookmark" && sp.from == pos && m.replacedWith)
4375 if (m.type == "bookmark" && sp.from == pos && m.replacedWith)
4292 foundBookmark = m.replacedWith;
4376 foundBookmark = m.replacedWith;
4293 }
4377 }
4294 if (collapsed && (collapsed.from || 0) == pos) {
4378 if (collapsed && (collapsed.from || 0) == pos) {
4295 buildCollapsedSpan(builder, (collapsed.to == null ? len : collapsed.to) - pos,
4379 buildCollapsedSpan(builder, (collapsed.to == null ? len : collapsed.to) - pos,
4296 collapsed.from != null && collapsed.marker.replacedWith);
4380 collapsed.from != null && collapsed.marker.replacedWith);
4297 if (collapsed.to == null) return collapsed.marker.find();
4381 if (collapsed.to == null) return collapsed.marker.find();
4298 }
4382 }
4299 if (foundBookmark && !collapsed) buildCollapsedSpan(builder, 0, foundBookmark);
4383 if (foundBookmark && !collapsed) buildCollapsedSpan(builder, 0, foundBookmark);
4300 }
4384 }
4301 if (pos >= len) break;
4385 if (pos >= len) break;
4302
4386
4303 var upto = Math.min(len, nextChange);
4387 var upto = Math.min(len, nextChange);
4304 while (true) {
4388 while (true) {
4305 if (text) {
4389 if (text) {
4306 var end = pos + text.length;
4390 var end = pos + text.length;
4307 if (!collapsed) {
4391 if (!collapsed) {
4308 var tokenText = end > upto ? text.slice(0, upto - pos) : text;
4392 var tokenText = end > upto ? text.slice(0, upto - pos) : text;
4309 builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle,
4393 builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle,
4310 spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "");
4394 spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "");
4311 }
4395 }
4312 if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;}
4396 if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;}
4313 pos = end;
4397 pos = end;
4314 spanStartStyle = "";
4398 spanStartStyle = "";
4315 }
4399 }
4316 text = styles[i++]; style = styleToClass(styles[i++]);
4400 text = allText.slice(at, at = styles[i++]);
4401 style = styleToClass(styles[i++]);
4317 }
4402 }
4318 }
4403 }
4319 }
4404 }
4320
4405
4321 // DOCUMENT DATA STRUCTURE
4406 // DOCUMENT DATA STRUCTURE
4322
4407
4323 function updateDoc(doc, change, markedSpans, selAfter, estimateHeight) {
4408 function updateDoc(doc, change, markedSpans, selAfter, estimateHeight) {
4324 function spansFor(n) {return markedSpans ? markedSpans[n] : null;}
4409 function spansFor(n) {return markedSpans ? markedSpans[n] : null;}
4325 function update(line, text, spans) {
4410 function update(line, text, spans) {
4326 updateLine(line, text, spans, estimateHeight);
4411 updateLine(line, text, spans, estimateHeight);
4327 signalLater(line, "change", line, change);
4412 signalLater(line, "change", line, change);
4328 }
4413 }
4329
4414
4330 var from = change.from, to = change.to, text = change.text;
4415 var from = change.from, to = change.to, text = change.text;
4331 var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line);
4416 var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line);
4332 var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line;
4417 var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line;
4333
4418
4334 // First adjust the line structure
4419 // First adjust the line structure
4335 if (from.ch == 0 && to.ch == 0 && lastText == "") {
4420 if (from.ch == 0 && to.ch == 0 && lastText == "") {
4336 // This is a whole-line replace. Treated specially to make
4421 // This is a whole-line replace. Treated specially to make
4337 // sure line objects move the way they are supposed to.
4422 // sure line objects move the way they are supposed to.
4338 for (var i = 0, e = text.length - 1, added = []; i < e; ++i)
4423 for (var i = 0, e = text.length - 1, added = []; i < e; ++i)
4339 added.push(makeLine(text[i], spansFor(i), estimateHeight));
4424 added.push(makeLine(text[i], spansFor(i), estimateHeight));
4340 update(lastLine, lastLine.text, lastSpans);
4425 update(lastLine, lastLine.text, lastSpans);
4341 if (nlines) doc.remove(from.line, nlines);
4426 if (nlines) doc.remove(from.line, nlines);
4342 if (added.length) doc.insert(from.line, added);
4427 if (added.length) doc.insert(from.line, added);
4343 } else if (firstLine == lastLine) {
4428 } else if (firstLine == lastLine) {
4344 if (text.length == 1) {
4429 if (text.length == 1) {
4345 update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans);
4430 update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans);
4346 } else {
4431 } else {
4347 for (var added = [], i = 1, e = text.length - 1; i < e; ++i)
4432 for (var added = [], i = 1, e = text.length - 1; i < e; ++i)
4348 added.push(makeLine(text[i], spansFor(i), estimateHeight));
4433 added.push(makeLine(text[i], spansFor(i), estimateHeight));
4349 added.push(makeLine(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight));
4434 added.push(makeLine(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight));
4350 update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
4435 update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
4351 doc.insert(from.line + 1, added);
4436 doc.insert(from.line + 1, added);
4352 }
4437 }
4353 } else if (text.length == 1) {
4438 } else if (text.length == 1) {
4354 update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0));
4439 update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0));
4355 doc.remove(from.line + 1, nlines);
4440 doc.remove(from.line + 1, nlines);
4356 } else {
4441 } else {
4357 update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
4442 update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
4358 update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans);
4443 update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans);
4359 for (var i = 1, e = text.length - 1, added = []; i < e; ++i)
4444 for (var i = 1, e = text.length - 1, added = []; i < e; ++i)
4360 added.push(makeLine(text[i], spansFor(i), estimateHeight));
4445 added.push(makeLine(text[i], spansFor(i), estimateHeight));
4361 if (nlines > 1) doc.remove(from.line + 1, nlines - 1);
4446 if (nlines > 1) doc.remove(from.line + 1, nlines - 1);
4362 doc.insert(from.line + 1, added);
4447 doc.insert(from.line + 1, added);
4363 }
4448 }
4364
4449
4365 signalLater(doc, "change", doc, change);
4450 signalLater(doc, "change", doc, change);
4366 setSelection(doc, selAfter.anchor, selAfter.head, null, true);
4451 setSelection(doc, selAfter.anchor, selAfter.head, null, true);
4367 }
4452 }
4368
4453
4369 function LeafChunk(lines) {
4454 function LeafChunk(lines) {
4370 this.lines = lines;
4455 this.lines = lines;
4371 this.parent = null;
4456 this.parent = null;
4372 for (var i = 0, e = lines.length, height = 0; i < e; ++i) {
4457 for (var i = 0, e = lines.length, height = 0; i < e; ++i) {
4373 lines[i].parent = this;
4458 lines[i].parent = this;
4374 height += lines[i].height;
4459 height += lines[i].height;
4375 }
4460 }
4376 this.height = height;
4461 this.height = height;
4377 }
4462 }
4378
4463
4379 LeafChunk.prototype = {
4464 LeafChunk.prototype = {
4380 chunkSize: function() { return this.lines.length; },
4465 chunkSize: function() { return this.lines.length; },
4381 removeInner: function(at, n) {
4466 removeInner: function(at, n) {
4382 for (var i = at, e = at + n; i < e; ++i) {
4467 for (var i = at, e = at + n; i < e; ++i) {
4383 var line = this.lines[i];
4468 var line = this.lines[i];
4384 this.height -= line.height;
4469 this.height -= line.height;
4385 cleanUpLine(line);
4470 cleanUpLine(line);
4386 signalLater(line, "delete");
4471 signalLater(line, "delete");
4387 }
4472 }
4388 this.lines.splice(at, n);
4473 this.lines.splice(at, n);
4389 },
4474 },
4390 collapse: function(lines) {
4475 collapse: function(lines) {
4391 lines.splice.apply(lines, [lines.length, 0].concat(this.lines));
4476 lines.splice.apply(lines, [lines.length, 0].concat(this.lines));
4392 },
4477 },
4393 insertInner: function(at, lines, height) {
4478 insertInner: function(at, lines, height) {
4394 this.height += height;
4479 this.height += height;
4395 this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at));
4480 this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at));
4396 for (var i = 0, e = lines.length; i < e; ++i) lines[i].parent = this;
4481 for (var i = 0, e = lines.length; i < e; ++i) lines[i].parent = this;
4397 },
4482 },
4398 iterN: function(at, n, op) {
4483 iterN: function(at, n, op) {
4399 for (var e = at + n; at < e; ++at)
4484 for (var e = at + n; at < e; ++at)
4400 if (op(this.lines[at])) return true;
4485 if (op(this.lines[at])) return true;
4401 }
4486 }
4402 };
4487 };
4403
4488
4404 function BranchChunk(children) {
4489 function BranchChunk(children) {
4405 this.children = children;
4490 this.children = children;
4406 var size = 0, height = 0;
4491 var size = 0, height = 0;
4407 for (var i = 0, e = children.length; i < e; ++i) {
4492 for (var i = 0, e = children.length; i < e; ++i) {
4408 var ch = children[i];
4493 var ch = children[i];
4409 size += ch.chunkSize(); height += ch.height;
4494 size += ch.chunkSize(); height += ch.height;
4410 ch.parent = this;
4495 ch.parent = this;
4411 }
4496 }
4412 this.size = size;
4497 this.size = size;
4413 this.height = height;
4498 this.height = height;
4414 this.parent = null;
4499 this.parent = null;
4415 }
4500 }
4416
4501
4417 BranchChunk.prototype = {
4502 BranchChunk.prototype = {
4418 chunkSize: function() { return this.size; },
4503 chunkSize: function() { return this.size; },
4419 removeInner: function(at, n) {
4504 removeInner: function(at, n) {
4420 this.size -= n;
4505 this.size -= n;
4421 for (var i = 0; i < this.children.length; ++i) {
4506 for (var i = 0; i < this.children.length; ++i) {
4422 var child = this.children[i], sz = child.chunkSize();
4507 var child = this.children[i], sz = child.chunkSize();
4423 if (at < sz) {
4508 if (at < sz) {
4424 var rm = Math.min(n, sz - at), oldHeight = child.height;
4509 var rm = Math.min(n, sz - at), oldHeight = child.height;
4425 child.removeInner(at, rm);
4510 child.removeInner(at, rm);
4426 this.height -= oldHeight - child.height;
4511 this.height -= oldHeight - child.height;
4427 if (sz == rm) { this.children.splice(i--, 1); child.parent = null; }
4512 if (sz == rm) { this.children.splice(i--, 1); child.parent = null; }
4428 if ((n -= rm) == 0) break;
4513 if ((n -= rm) == 0) break;
4429 at = 0;
4514 at = 0;
4430 } else at -= sz;
4515 } else at -= sz;
4431 }
4516 }
4432 if (this.size - n < 25) {
4517 if (this.size - n < 25) {
4433 var lines = [];
4518 var lines = [];
4434 this.collapse(lines);
4519 this.collapse(lines);
4435 this.children = [new LeafChunk(lines)];
4520 this.children = [new LeafChunk(lines)];
4436 this.children[0].parent = this;
4521 this.children[0].parent = this;
4437 }
4522 }
4438 },
4523 },
4439 collapse: function(lines) {
4524 collapse: function(lines) {
4440 for (var i = 0, e = this.children.length; i < e; ++i) this.children[i].collapse(lines);
4525 for (var i = 0, e = this.children.length; i < e; ++i) this.children[i].collapse(lines);
4441 },
4526 },
4442 insertInner: function(at, lines, height) {
4527 insertInner: function(at, lines, height) {
4443 this.size += lines.length;
4528 this.size += lines.length;
4444 this.height += height;
4529 this.height += height;
4445 for (var i = 0, e = this.children.length; i < e; ++i) {
4530 for (var i = 0, e = this.children.length; i < e; ++i) {
4446 var child = this.children[i], sz = child.chunkSize();
4531 var child = this.children[i], sz = child.chunkSize();
4447 if (at <= sz) {
4532 if (at <= sz) {
4448 child.insertInner(at, lines, height);
4533 child.insertInner(at, lines, height);
4449 if (child.lines && child.lines.length > 50) {
4534 if (child.lines && child.lines.length > 50) {
4450 while (child.lines.length > 50) {
4535 while (child.lines.length > 50) {
4451 var spilled = child.lines.splice(child.lines.length - 25, 25);
4536 var spilled = child.lines.splice(child.lines.length - 25, 25);
4452 var newleaf = new LeafChunk(spilled);
4537 var newleaf = new LeafChunk(spilled);
4453 child.height -= newleaf.height;
4538 child.height -= newleaf.height;
4454 this.children.splice(i + 1, 0, newleaf);
4539 this.children.splice(i + 1, 0, newleaf);
4455 newleaf.parent = this;
4540 newleaf.parent = this;
4456 }
4541 }
4457 this.maybeSpill();
4542 this.maybeSpill();
4458 }
4543 }
4459 break;
4544 break;
4460 }
4545 }
4461 at -= sz;
4546 at -= sz;
4462 }
4547 }
4463 },
4548 },
4464 maybeSpill: function() {
4549 maybeSpill: function() {
4465 if (this.children.length <= 10) return;
4550 if (this.children.length <= 10) return;
4466 var me = this;
4551 var me = this;
4467 do {
4552 do {
4468 var spilled = me.children.splice(me.children.length - 5, 5);
4553 var spilled = me.children.splice(me.children.length - 5, 5);
4469 var sibling = new BranchChunk(spilled);
4554 var sibling = new BranchChunk(spilled);
4470 if (!me.parent) { // Become the parent node
4555 if (!me.parent) { // Become the parent node
4471 var copy = new BranchChunk(me.children);
4556 var copy = new BranchChunk(me.children);
4472 copy.parent = me;
4557 copy.parent = me;
4473 me.children = [copy, sibling];
4558 me.children = [copy, sibling];
4474 me = copy;
4559 me = copy;
4475 } else {
4560 } else {
4476 me.size -= sibling.size;
4561 me.size -= sibling.size;
4477 me.height -= sibling.height;
4562 me.height -= sibling.height;
4478 var myIndex = indexOf(me.parent.children, me);
4563 var myIndex = indexOf(me.parent.children, me);
4479 me.parent.children.splice(myIndex + 1, 0, sibling);
4564 me.parent.children.splice(myIndex + 1, 0, sibling);
4480 }
4565 }
4481 sibling.parent = me.parent;
4566 sibling.parent = me.parent;
4482 } while (me.children.length > 10);
4567 } while (me.children.length > 10);
4483 me.parent.maybeSpill();
4568 me.parent.maybeSpill();
4484 },
4569 },
4485 iterN: function(at, n, op) {
4570 iterN: function(at, n, op) {
4486 for (var i = 0, e = this.children.length; i < e; ++i) {
4571 for (var i = 0, e = this.children.length; i < e; ++i) {
4487 var child = this.children[i], sz = child.chunkSize();
4572 var child = this.children[i], sz = child.chunkSize();
4488 if (at < sz) {
4573 if (at < sz) {
4489 var used = Math.min(n, sz - at);
4574 var used = Math.min(n, sz - at);
4490 if (child.iterN(at, used, op)) return true;
4575 if (child.iterN(at, used, op)) return true;
4491 if ((n -= used) == 0) break;
4576 if ((n -= used) == 0) break;
4492 at = 0;
4577 at = 0;
4493 } else at -= sz;
4578 } else at -= sz;
4494 }
4579 }
4495 }
4580 }
4496 };
4581 };
4497
4582
4498 var nextDocId = 0;
4583 var nextDocId = 0;
4499 var Doc = CodeMirror.Doc = function(text, mode, firstLine) {
4584 var Doc = CodeMirror.Doc = function(text, mode, firstLine) {
4500 if (!(this instanceof Doc)) return new Doc(text, mode, firstLine);
4585 if (!(this instanceof Doc)) return new Doc(text, mode, firstLine);
4501 if (firstLine == null) firstLine = 0;
4586 if (firstLine == null) firstLine = 0;
4502
4587
4503 BranchChunk.call(this, [new LeafChunk([makeLine("", null)])]);
4588 BranchChunk.call(this, [new LeafChunk([makeLine("", null)])]);
4504 this.first = firstLine;
4589 this.first = firstLine;
4505 this.scrollTop = this.scrollLeft = 0;
4590 this.scrollTop = this.scrollLeft = 0;
4506 this.cantEdit = false;
4591 this.cantEdit = false;
4507 this.history = makeHistory();
4592 this.history = makeHistory();
4593 this.cleanGeneration = 1;
4508 this.frontier = firstLine;
4594 this.frontier = firstLine;
4509 var start = Pos(firstLine, 0);
4595 var start = Pos(firstLine, 0);
4510 this.sel = {from: start, to: start, head: start, anchor: start, shift: false, extend: false, goalColumn: null};
4596 this.sel = {from: start, to: start, head: start, anchor: start, shift: false, extend: false, goalColumn: null};
4511 this.id = ++nextDocId;
4597 this.id = ++nextDocId;
4512 this.modeOption = mode;
4598 this.modeOption = mode;
4513
4599
4514 if (typeof text == "string") text = splitLines(text);
4600 if (typeof text == "string") text = splitLines(text);
4515 updateDoc(this, {from: start, to: start, text: text}, null, {head: start, anchor: start});
4601 updateDoc(this, {from: start, to: start, text: text}, null, {head: start, anchor: start});
4516 };
4602 };
4517
4603
4518 Doc.prototype = createObj(BranchChunk.prototype, {
4604 Doc.prototype = createObj(BranchChunk.prototype, {
4605 constructor: Doc,
4519 iter: function(from, to, op) {
4606 iter: function(from, to, op) {
4520 if (op) this.iterN(from - this.first, to - from, op);
4607 if (op) this.iterN(from - this.first, to - from, op);
4521 else this.iterN(this.first, this.first + this.size, from);
4608 else this.iterN(this.first, this.first + this.size, from);
4522 },
4609 },
4523
4610
4524 insert: function(at, lines) {
4611 insert: function(at, lines) {
4525 var height = 0;
4612 var height = 0;
4526 for (var i = 0, e = lines.length; i < e; ++i) height += lines[i].height;
4613 for (var i = 0, e = lines.length; i < e; ++i) height += lines[i].height;
4527 this.insertInner(at - this.first, lines, height);
4614 this.insertInner(at - this.first, lines, height);
4528 },
4615 },
4529 remove: function(at, n) { this.removeInner(at - this.first, n); },
4616 remove: function(at, n) { this.removeInner(at - this.first, n); },
4530
4617
4531 getValue: function(lineSep) {
4618 getValue: function(lineSep) {
4532 var lines = getLines(this, this.first, this.first + this.size);
4619 var lines = getLines(this, this.first, this.first + this.size);
4533 if (lineSep === false) return lines;
4620 if (lineSep === false) return lines;
4534 return lines.join(lineSep || "\n");
4621 return lines.join(lineSep || "\n");
4535 },
4622 },
4536 setValue: function(code) {
4623 setValue: function(code) {
4537 var top = Pos(this.first, 0), last = this.first + this.size - 1;
4624 var top = Pos(this.first, 0), last = this.first + this.size - 1;
4538 makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length),
4625 makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length),
4539 text: splitLines(code), origin: "setValue"},
4626 text: splitLines(code), origin: "setValue"},
4540 {head: top, anchor: top}, true);
4627 {head: top, anchor: top}, true);
4541 },
4628 },
4542 replaceRange: function(code, from, to, origin) {
4629 replaceRange: function(code, from, to, origin) {
4543 from = clipPos(this, from);
4630 from = clipPos(this, from);
4544 to = to ? clipPos(this, to) : from;
4631 to = to ? clipPos(this, to) : from;
4545 replaceRange(this, code, from, to, origin);
4632 replaceRange(this, code, from, to, origin);
4546 },
4633 },
4547 getRange: function(from, to, lineSep) {
4634 getRange: function(from, to, lineSep) {
4548 var lines = getBetween(this, clipPos(this, from), clipPos(this, to));
4635 var lines = getBetween(this, clipPos(this, from), clipPos(this, to));
4549 if (lineSep === false) return lines;
4636 if (lineSep === false) return lines;
4550 return lines.join(lineSep || "\n");
4637 return lines.join(lineSep || "\n");
4551 },
4638 },
4552
4639
4553 getLine: function(line) {var l = this.getLineHandle(line); return l && l.text;},
4640 getLine: function(line) {var l = this.getLineHandle(line); return l && l.text;},
4554 setLine: function(line, text) {
4641 setLine: function(line, text) {
4555 if (isLine(this, line))
4642 if (isLine(this, line))
4556 replaceRange(this, text, Pos(line, 0), clipPos(this, Pos(line)));
4643 replaceRange(this, text, Pos(line, 0), clipPos(this, Pos(line)));
4557 },
4644 },
4558 removeLine: function(line) {
4645 removeLine: function(line) {
4559 if (line) replaceRange(this, "", clipPos(this, Pos(line - 1)), clipPos(this, Pos(line)));
4646 if (line) replaceRange(this, "", clipPos(this, Pos(line - 1)), clipPos(this, Pos(line)));
4560 else replaceRange(this, "", Pos(0, 0), clipPos(this, Pos(1, 0)));
4647 else replaceRange(this, "", Pos(0, 0), clipPos(this, Pos(1, 0)));
4561 },
4648 },
4562
4649
4563 getLineHandle: function(line) {if (isLine(this, line)) return getLine(this, line);},
4650 getLineHandle: function(line) {if (isLine(this, line)) return getLine(this, line);},
4564 getLineNumber: function(line) {return lineNo(line);},
4651 getLineNumber: function(line) {return lineNo(line);},
4565
4652
4566 lineCount: function() {return this.size;},
4653 lineCount: function() {return this.size;},
4567 firstLine: function() {return this.first;},
4654 firstLine: function() {return this.first;},
4568 lastLine: function() {return this.first + this.size - 1;},
4655 lastLine: function() {return this.first + this.size - 1;},
4569
4656
4570 clipPos: function(pos) {return clipPos(this, pos);},
4657 clipPos: function(pos) {return clipPos(this, pos);},
4571
4658
4572 getCursor: function(start) {
4659 getCursor: function(start) {
4573 var sel = this.sel, pos;
4660 var sel = this.sel, pos;
4574 if (start == null || start == "head") pos = sel.head;
4661 if (start == null || start == "head") pos = sel.head;
4575 else if (start == "anchor") pos = sel.anchor;
4662 else if (start == "anchor") pos = sel.anchor;
4576 else if (start == "end" || start === false) pos = sel.to;
4663 else if (start == "end" || start === false) pos = sel.to;
4577 else pos = sel.from;
4664 else pos = sel.from;
4578 return copyPos(pos);
4665 return copyPos(pos);
4579 },
4666 },
4580 somethingSelected: function() {return !posEq(this.sel.head, this.sel.anchor);},
4667 somethingSelected: function() {return !posEq(this.sel.head, this.sel.anchor);},
4581
4668
4582 setCursor: docOperation(function(line, ch, extend) {
4669 setCursor: docOperation(function(line, ch, extend) {
4583 var pos = clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line);
4670 var pos = clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line);
4584 if (extend) extendSelection(this, pos);
4671 if (extend) extendSelection(this, pos);
4585 else setSelection(this, pos, pos);
4672 else setSelection(this, pos, pos);
4586 }),
4673 }),
4587 setSelection: docOperation(function(anchor, head) {
4674 setSelection: docOperation(function(anchor, head) {
4588 setSelection(this, clipPos(this, anchor), clipPos(this, head || anchor));
4675 setSelection(this, clipPos(this, anchor), clipPos(this, head || anchor));
4589 }),
4676 }),
4590 extendSelection: docOperation(function(from, to) {
4677 extendSelection: docOperation(function(from, to) {
4591 extendSelection(this, clipPos(this, from), to && clipPos(this, to));
4678 extendSelection(this, clipPos(this, from), to && clipPos(this, to));
4592 }),
4679 }),
4593
4680
4594 getSelection: function(lineSep) {return this.getRange(this.sel.from, this.sel.to, lineSep);},
4681 getSelection: function(lineSep) {return this.getRange(this.sel.from, this.sel.to, lineSep);},
4595 replaceSelection: function(code, collapse, origin) {
4682 replaceSelection: function(code, collapse, origin) {
4596 makeChange(this, {from: this.sel.from, to: this.sel.to, text: splitLines(code), origin: origin}, collapse || "around");
4683 makeChange(this, {from: this.sel.from, to: this.sel.to, text: splitLines(code), origin: origin}, collapse || "around");
4597 },
4684 },
4598 undo: docOperation(function() {makeChangeFromHistory(this, "undo");}),
4685 undo: docOperation(function() {makeChangeFromHistory(this, "undo");}),
4599 redo: docOperation(function() {makeChangeFromHistory(this, "redo");}),
4686 redo: docOperation(function() {makeChangeFromHistory(this, "redo");}),
4600
4687
4601 setExtending: function(val) {this.sel.extend = val;},
4688 setExtending: function(val) {this.sel.extend = val;},
4602
4689
4603 historySize: function() {
4690 historySize: function() {
4604 var hist = this.history;
4691 var hist = this.history;
4605 return {undo: hist.done.length, redo: hist.undone.length};
4692 return {undo: hist.done.length, redo: hist.undone.length};
4606 },
4693 },
4607 clearHistory: function() {this.history = makeHistory();},
4694 clearHistory: function() {this.history = makeHistory(this.history.maxGeneration);},
4608
4695
4609 markClean: function() {
4696 markClean: function() {
4610 this.history.dirtyCounter = 0;
4697 this.cleanGeneration = this.changeGeneration();
4698 },
4699 changeGeneration: function() {
4611 this.history.lastOp = this.history.lastOrigin = null;
4700 this.history.lastOp = this.history.lastOrigin = null;
4701 return this.history.generation;
4612 },
4702 },
4613 isClean: function () {return this.history.dirtyCounter == 0;},
4703 isClean: function (gen) {
4704 return this.history.generation == (gen || this.cleanGeneration);
4705 },
4614
4706
4615 getHistory: function() {
4707 getHistory: function() {
4616 return {done: copyHistoryArray(this.history.done),
4708 return {done: copyHistoryArray(this.history.done),
4617 undone: copyHistoryArray(this.history.undone)};
4709 undone: copyHistoryArray(this.history.undone)};
4618 },
4710 },
4619 setHistory: function(histData) {
4711 setHistory: function(histData) {
4620 var hist = this.history = makeHistory();
4712 var hist = this.history = makeHistory(this.history.maxGeneration);
4621 hist.done = histData.done.slice(0);
4713 hist.done = histData.done.slice(0);
4622 hist.undone = histData.undone.slice(0);
4714 hist.undone = histData.undone.slice(0);
4623 },
4715 },
4624
4716
4625 markText: function(from, to, options) {
4717 markText: function(from, to, options) {
4626 return markText(this, clipPos(this, from), clipPos(this, to), options, "range");
4718 return markText(this, clipPos(this, from), clipPos(this, to), options, "range");
4627 },
4719 },
4628 setBookmark: function(pos, options) {
4720 setBookmark: function(pos, options) {
4629 var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options),
4721 var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options),
4630 insertLeft: options && options.insertLeft};
4722 insertLeft: options && options.insertLeft};
4631 pos = clipPos(this, pos);
4723 pos = clipPos(this, pos);
4632 return markText(this, pos, pos, realOpts, "bookmark");
4724 return markText(this, pos, pos, realOpts, "bookmark");
4633 },
4725 },
4634 findMarksAt: function(pos) {
4726 findMarksAt: function(pos) {
4635 pos = clipPos(this, pos);
4727 pos = clipPos(this, pos);
4636 var markers = [], spans = getLine(this, pos.line).markedSpans;
4728 var markers = [], spans = getLine(this, pos.line).markedSpans;
4637 if (spans) for (var i = 0; i < spans.length; ++i) {
4729 if (spans) for (var i = 0; i < spans.length; ++i) {
4638 var span = spans[i];
4730 var span = spans[i];
4639 if ((span.from == null || span.from <= pos.ch) &&
4731 if ((span.from == null || span.from <= pos.ch) &&
4640 (span.to == null || span.to >= pos.ch))
4732 (span.to == null || span.to >= pos.ch))
4641 markers.push(span.marker.parent || span.marker);
4733 markers.push(span.marker.parent || span.marker);
4642 }
4734 }
4643 return markers;
4735 return markers;
4644 },
4736 },
4645 getAllMarks: function() {
4737 getAllMarks: function() {
4646 var markers = [];
4738 var markers = [];
4647 this.iter(function(line) {
4739 this.iter(function(line) {
4648 var sps = line.markedSpans;
4740 var sps = line.markedSpans;
4649 if (sps) for (var i = 0; i < sps.length; ++i)
4741 if (sps) for (var i = 0; i < sps.length; ++i)
4650 if (sps[i].from != null) markers.push(sps[i].marker);
4742 if (sps[i].from != null) markers.push(sps[i].marker);
4651 });
4743 });
4652 return markers;
4744 return markers;
4653 },
4745 },
4654
4746
4655 posFromIndex: function(off) {
4747 posFromIndex: function(off) {
4656 var ch, lineNo = this.first;
4748 var ch, lineNo = this.first;
4657 this.iter(function(line) {
4749 this.iter(function(line) {
4658 var sz = line.text.length + 1;
4750 var sz = line.text.length + 1;
4659 if (sz > off) { ch = off; return true; }
4751 if (sz > off) { ch = off; return true; }
4660 off -= sz;
4752 off -= sz;
4661 ++lineNo;
4753 ++lineNo;
4662 });
4754 });
4663 return clipPos(this, Pos(lineNo, ch));
4755 return clipPos(this, Pos(lineNo, ch));
4664 },
4756 },
4665 indexFromPos: function (coords) {
4757 indexFromPos: function (coords) {
4666 coords = clipPos(this, coords);
4758 coords = clipPos(this, coords);
4667 var index = coords.ch;
4759 var index = coords.ch;
4668 if (coords.line < this.first || coords.ch < 0) return 0;
4760 if (coords.line < this.first || coords.ch < 0) return 0;
4669 this.iter(this.first, coords.line, function (line) {
4761 this.iter(this.first, coords.line, function (line) {
4670 index += line.text.length + 1;
4762 index += line.text.length + 1;
4671 });
4763 });
4672 return index;
4764 return index;
4673 },
4765 },
4674
4766
4675 copy: function(copyHistory) {
4767 copy: function(copyHistory) {
4676 var doc = new Doc(getLines(this, this.first, this.first + this.size), this.modeOption, this.first);
4768 var doc = new Doc(getLines(this, this.first, this.first + this.size), this.modeOption, this.first);
4677 doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft;
4769 doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft;
4678 doc.sel = {from: this.sel.from, to: this.sel.to, head: this.sel.head, anchor: this.sel.anchor,
4770 doc.sel = {from: this.sel.from, to: this.sel.to, head: this.sel.head, anchor: this.sel.anchor,
4679 shift: this.sel.shift, extend: false, goalColumn: this.sel.goalColumn};
4771 shift: this.sel.shift, extend: false, goalColumn: this.sel.goalColumn};
4680 if (copyHistory) {
4772 if (copyHistory) {
4681 doc.history.undoDepth = this.history.undoDepth;
4773 doc.history.undoDepth = this.history.undoDepth;
4682 doc.setHistory(this.getHistory());
4774 doc.setHistory(this.getHistory());
4683 }
4775 }
4684 return doc;
4776 return doc;
4685 },
4777 },
4686
4778
4687 linkedDoc: function(options) {
4779 linkedDoc: function(options) {
4688 if (!options) options = {};
4780 if (!options) options = {};
4689 var from = this.first, to = this.first + this.size;
4781 var from = this.first, to = this.first + this.size;
4690 if (options.from != null && options.from > from) from = options.from;
4782 if (options.from != null && options.from > from) from = options.from;
4691 if (options.to != null && options.to < to) to = options.to;
4783 if (options.to != null && options.to < to) to = options.to;
4692 var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from);
4784 var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from);
4693 if (options.sharedHist) copy.history = this.history;
4785 if (options.sharedHist) copy.history = this.history;
4694 (this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist});
4786 (this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist});
4695 copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}];
4787 copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}];
4696 return copy;
4788 return copy;
4697 },
4789 },
4698 unlinkDoc: function(other) {
4790 unlinkDoc: function(other) {
4699 if (other instanceof CodeMirror) other = other.doc;
4791 if (other instanceof CodeMirror) other = other.doc;
4700 if (this.linked) for (var i = 0; i < this.linked.length; ++i) {
4792 if (this.linked) for (var i = 0; i < this.linked.length; ++i) {
4701 var link = this.linked[i];
4793 var link = this.linked[i];
4702 if (link.doc != other) continue;
4794 if (link.doc != other) continue;
4703 this.linked.splice(i, 1);
4795 this.linked.splice(i, 1);
4704 other.unlinkDoc(this);
4796 other.unlinkDoc(this);
4705 break;
4797 break;
4706 }
4798 }
4707 // If the histories were shared, split them again
4799 // If the histories were shared, split them again
4708 if (other.history == this.history) {
4800 if (other.history == this.history) {
4709 var splitIds = [other.id];
4801 var splitIds = [other.id];
4710 linkedDocs(other, function(doc) {splitIds.push(doc.id);}, true);
4802 linkedDocs(other, function(doc) {splitIds.push(doc.id);}, true);
4711 other.history = makeHistory();
4803 other.history = makeHistory();
4712 other.history.done = copyHistoryArray(this.history.done, splitIds);
4804 other.history.done = copyHistoryArray(this.history.done, splitIds);
4713 other.history.undone = copyHistoryArray(this.history.undone, splitIds);
4805 other.history.undone = copyHistoryArray(this.history.undone, splitIds);
4714 }
4806 }
4715 },
4807 },
4716 iterLinkedDocs: function(f) {linkedDocs(this, f);},
4808 iterLinkedDocs: function(f) {linkedDocs(this, f);},
4717
4809
4718 getMode: function() {return this.mode;},
4810 getMode: function() {return this.mode;},
4719 getEditor: function() {return this.cm;}
4811 getEditor: function() {return this.cm;}
4720 });
4812 });
4721
4813
4722 Doc.prototype.eachLine = Doc.prototype.iter;
4814 Doc.prototype.eachLine = Doc.prototype.iter;
4723
4815
4724 // The Doc methods that should be available on CodeMirror instances
4816 // The Doc methods that should be available on CodeMirror instances
4725 var dontDelegate = "iter insert remove copy getEditor".split(" ");
4817 var dontDelegate = "iter insert remove copy getEditor".split(" ");
4726 for (var prop in Doc.prototype) if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0)
4818 for (var prop in Doc.prototype) if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0)
4727 CodeMirror.prototype[prop] = (function(method) {
4819 CodeMirror.prototype[prop] = (function(method) {
4728 return function() {return method.apply(this.doc, arguments);};
4820 return function() {return method.apply(this.doc, arguments);};
4729 })(Doc.prototype[prop]);
4821 })(Doc.prototype[prop]);
4730
4822
4731 function linkedDocs(doc, f, sharedHistOnly) {
4823 function linkedDocs(doc, f, sharedHistOnly) {
4732 function propagate(doc, skip, sharedHist) {
4824 function propagate(doc, skip, sharedHist) {
4733 if (doc.linked) for (var i = 0; i < doc.linked.length; ++i) {
4825 if (doc.linked) for (var i = 0; i < doc.linked.length; ++i) {
4734 var rel = doc.linked[i];
4826 var rel = doc.linked[i];
4735 if (rel.doc == skip) continue;
4827 if (rel.doc == skip) continue;
4736 var shared = sharedHist && rel.sharedHist;
4828 var shared = sharedHist && rel.sharedHist;
4737 if (sharedHistOnly && !shared) continue;
4829 if (sharedHistOnly && !shared) continue;
4738 f(rel.doc, shared);
4830 f(rel.doc, shared);
4739 propagate(rel.doc, doc, shared);
4831 propagate(rel.doc, doc, shared);
4740 }
4832 }
4741 }
4833 }
4742 propagate(doc, null, true);
4834 propagate(doc, null, true);
4743 }
4835 }
4744
4836
4745 function attachDoc(cm, doc) {
4837 function attachDoc(cm, doc) {
4746 if (doc.cm) throw new Error("This document is already in use.");
4838 if (doc.cm) throw new Error("This document is already in use.");
4747 cm.doc = doc;
4839 cm.doc = doc;
4748 doc.cm = cm;
4840 doc.cm = cm;
4749 estimateLineHeights(cm);
4841 estimateLineHeights(cm);
4750 loadMode(cm);
4842 loadMode(cm);
4751 if (!cm.options.lineWrapping) computeMaxLength(cm);
4843 if (!cm.options.lineWrapping) computeMaxLength(cm);
4752 cm.options.mode = doc.modeOption;
4844 cm.options.mode = doc.modeOption;
4753 regChange(cm);
4845 regChange(cm);
4754 }
4846 }
4755
4847
4756 // LINE UTILITIES
4848 // LINE UTILITIES
4757
4849
4758 function getLine(chunk, n) {
4850 function getLine(chunk, n) {
4759 n -= chunk.first;
4851 n -= chunk.first;
4760 while (!chunk.lines) {
4852 while (!chunk.lines) {
4761 for (var i = 0;; ++i) {
4853 for (var i = 0;; ++i) {
4762 var child = chunk.children[i], sz = child.chunkSize();
4854 var child = chunk.children[i], sz = child.chunkSize();
4763 if (n < sz) { chunk = child; break; }
4855 if (n < sz) { chunk = child; break; }
4764 n -= sz;
4856 n -= sz;
4765 }
4857 }
4766 }
4858 }
4767 return chunk.lines[n];
4859 return chunk.lines[n];
4768 }
4860 }
4769
4861
4770 function getBetween(doc, start, end) {
4862 function getBetween(doc, start, end) {
4771 var out = [], n = start.line;
4863 var out = [], n = start.line;
4772 doc.iter(start.line, end.line + 1, function(line) {
4864 doc.iter(start.line, end.line + 1, function(line) {
4773 var text = line.text;
4865 var text = line.text;
4774 if (n == end.line) text = text.slice(0, end.ch);
4866 if (n == end.line) text = text.slice(0, end.ch);
4775 if (n == start.line) text = text.slice(start.ch);
4867 if (n == start.line) text = text.slice(start.ch);
4776 out.push(text);
4868 out.push(text);
4777 ++n;
4869 ++n;
4778 });
4870 });
4779 return out;
4871 return out;
4780 }
4872 }
4781 function getLines(doc, from, to) {
4873 function getLines(doc, from, to) {
4782 var out = [];
4874 var out = [];
4783 doc.iter(from, to, function(line) { out.push(line.text); });
4875 doc.iter(from, to, function(line) { out.push(line.text); });
4784 return out;
4876 return out;
4785 }
4877 }
4786
4878
4787 function updateLineHeight(line, height) {
4879 function updateLineHeight(line, height) {
4788 var diff = height - line.height;
4880 var diff = height - line.height;
4789 for (var n = line; n; n = n.parent) n.height += diff;
4881 for (var n = line; n; n = n.parent) n.height += diff;
4790 }
4882 }
4791
4883
4792 function lineNo(line) {
4884 function lineNo(line) {
4793 if (line.parent == null) return null;
4885 if (line.parent == null) return null;
4794 var cur = line.parent, no = indexOf(cur.lines, line);
4886 var cur = line.parent, no = indexOf(cur.lines, line);
4795 for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {
4887 for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {
4796 for (var i = 0;; ++i) {
4888 for (var i = 0;; ++i) {
4797 if (chunk.children[i] == cur) break;
4889 if (chunk.children[i] == cur) break;
4798 no += chunk.children[i].chunkSize();
4890 no += chunk.children[i].chunkSize();
4799 }
4891 }
4800 }
4892 }
4801 return no + cur.first;
4893 return no + cur.first;
4802 }
4894 }
4803
4895
4804 function lineAtHeight(chunk, h) {
4896 function lineAtHeight(chunk, h) {
4805 var n = chunk.first;
4897 var n = chunk.first;
4806 outer: do {
4898 outer: do {
4807 for (var i = 0, e = chunk.children.length; i < e; ++i) {
4899 for (var i = 0, e = chunk.children.length; i < e; ++i) {
4808 var child = chunk.children[i], ch = child.height;
4900 var child = chunk.children[i], ch = child.height;
4809 if (h < ch) { chunk = child; continue outer; }
4901 if (h < ch) { chunk = child; continue outer; }
4810 h -= ch;
4902 h -= ch;
4811 n += child.chunkSize();
4903 n += child.chunkSize();
4812 }
4904 }
4813 return n;
4905 return n;
4814 } while (!chunk.lines);
4906 } while (!chunk.lines);
4815 for (var i = 0, e = chunk.lines.length; i < e; ++i) {
4907 for (var i = 0, e = chunk.lines.length; i < e; ++i) {
4816 var line = chunk.lines[i], lh = line.height;
4908 var line = chunk.lines[i], lh = line.height;
4817 if (h < lh) break;
4909 if (h < lh) break;
4818 h -= lh;
4910 h -= lh;
4819 }
4911 }
4820 return n + i;
4912 return n + i;
4821 }
4913 }
4822
4914
4823 function heightAtLine(cm, lineObj) {
4915 function heightAtLine(cm, lineObj) {
4824 lineObj = visualLine(cm.doc, lineObj);
4916 lineObj = visualLine(cm.doc, lineObj);
4825
4917
4826 var h = 0, chunk = lineObj.parent;
4918 var h = 0, chunk = lineObj.parent;
4827 for (var i = 0; i < chunk.lines.length; ++i) {
4919 for (var i = 0; i < chunk.lines.length; ++i) {
4828 var line = chunk.lines[i];
4920 var line = chunk.lines[i];
4829 if (line == lineObj) break;
4921 if (line == lineObj) break;
4830 else h += line.height;
4922 else h += line.height;
4831 }
4923 }
4832 for (var p = chunk.parent; p; chunk = p, p = chunk.parent) {
4924 for (var p = chunk.parent; p; chunk = p, p = chunk.parent) {
4833 for (var i = 0; i < p.children.length; ++i) {
4925 for (var i = 0; i < p.children.length; ++i) {
4834 var cur = p.children[i];
4926 var cur = p.children[i];
4835 if (cur == chunk) break;
4927 if (cur == chunk) break;
4836 else h += cur.height;
4928 else h += cur.height;
4837 }
4929 }
4838 }
4930 }
4839 return h;
4931 return h;
4840 }
4932 }
4841
4933
4842 function getOrder(line) {
4934 function getOrder(line) {
4843 var order = line.order;
4935 var order = line.order;
4844 if (order == null) order = line.order = bidiOrdering(line.text);
4936 if (order == null) order = line.order = bidiOrdering(line.text);
4845 return order;
4937 return order;
4846 }
4938 }
4847
4939
4848 // HISTORY
4940 // HISTORY
4849
4941
4850 function makeHistory() {
4942 function makeHistory(startGen) {
4851 return {
4943 return {
4852 // Arrays of history events. Doing something adds an event to
4944 // Arrays of history events. Doing something adds an event to
4853 // done and clears undo. Undoing moves events from done to
4945 // done and clears undo. Undoing moves events from done to
4854 // undone, redoing moves them in the other direction.
4946 // undone, redoing moves them in the other direction.
4855 done: [], undone: [], undoDepth: Infinity,
4947 done: [], undone: [], undoDepth: Infinity,
4856 // Used to track when changes can be merged into a single undo
4948 // Used to track when changes can be merged into a single undo
4857 // event
4949 // event
4858 lastTime: 0, lastOp: null, lastOrigin: null,
4950 lastTime: 0, lastOp: null, lastOrigin: null,
4859 // Used by the isClean() method
4951 // Used by the isClean() method
4860 dirtyCounter: 0
4952 generation: startGen || 1, maxGeneration: startGen || 1
4861 };
4953 };
4862 }
4954 }
4863
4955
4864 function attachLocalSpans(doc, change, from, to) {
4956 function attachLocalSpans(doc, change, from, to) {
4865 var existing = change["spans_" + doc.id], n = 0;
4957 var existing = change["spans_" + doc.id], n = 0;
4866 doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function(line) {
4958 doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function(line) {
4867 if (line.markedSpans)
4959 if (line.markedSpans)
4868 (existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans;
4960 (existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans;
4869 ++n;
4961 ++n;
4870 });
4962 });
4871 }
4963 }
4872
4964
4873 function historyChangeFromChange(doc, change) {
4965 function historyChangeFromChange(doc, change) {
4874 var histChange = {from: change.from, to: changeEnd(change), text: getBetween(doc, change.from, change.to)};
4966 var histChange = {from: change.from, to: changeEnd(change), text: getBetween(doc, change.from, change.to)};
4875 attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);
4967 attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);
4876 linkedDocs(doc, function(doc) {attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);}, true);
4968 linkedDocs(doc, function(doc) {attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);}, true);
4877 return histChange;
4969 return histChange;
4878 }
4970 }
4879
4971
4880 function addToHistory(doc, change, selAfter, opId) {
4972 function addToHistory(doc, change, selAfter, opId) {
4881 var hist = doc.history;
4973 var hist = doc.history;
4882 hist.undone.length = 0;
4974 hist.undone.length = 0;
4883 var time = +new Date, cur = lst(hist.done);
4975 var time = +new Date, cur = lst(hist.done);
4884
4976
4885 if (cur &&
4977 if (cur &&
4886 (hist.lastOp == opId ||
4978 (hist.lastOp == opId ||
4887 hist.lastOrigin == change.origin && change.origin &&
4979 hist.lastOrigin == change.origin && change.origin &&
4888 ((change.origin.charAt(0) == "+" && doc.cm && hist.lastTime > time - doc.cm.options.historyEventDelay) ||
4980 ((change.origin.charAt(0) == "+" && doc.cm && hist.lastTime > time - doc.cm.options.historyEventDelay) ||
4889 change.origin.charAt(0) == "*"))) {
4981 change.origin.charAt(0) == "*"))) {
4890 // Merge this change into the last event
4982 // Merge this change into the last event
4891 var last = lst(cur.changes);
4983 var last = lst(cur.changes);
4892 if (posEq(change.from, change.to) && posEq(change.from, last.to)) {
4984 if (posEq(change.from, change.to) && posEq(change.from, last.to)) {
4893 // Optimized case for simple insertion -- don't want to add
4985 // Optimized case for simple insertion -- don't want to add
4894 // new changesets for every character typed
4986 // new changesets for every character typed
4895 last.to = changeEnd(change);
4987 last.to = changeEnd(change);
4896 } else {
4988 } else {
4897 // Add new sub-event
4989 // Add new sub-event
4898 cur.changes.push(historyChangeFromChange(doc, change));
4990 cur.changes.push(historyChangeFromChange(doc, change));
4899 }
4991 }
4900 cur.anchorAfter = selAfter.anchor; cur.headAfter = selAfter.head;
4992 cur.anchorAfter = selAfter.anchor; cur.headAfter = selAfter.head;
4901 } else {
4993 } else {
4902 // Can not be merged, start a new event.
4994 // Can not be merged, start a new event.
4903 cur = {changes: [historyChangeFromChange(doc, change)],
4995 cur = {changes: [historyChangeFromChange(doc, change)],
4996 generation: hist.generation,
4904 anchorBefore: doc.sel.anchor, headBefore: doc.sel.head,
4997 anchorBefore: doc.sel.anchor, headBefore: doc.sel.head,
4905 anchorAfter: selAfter.anchor, headAfter: selAfter.head};
4998 anchorAfter: selAfter.anchor, headAfter: selAfter.head};
4906 hist.done.push(cur);
4999 hist.done.push(cur);
5000 hist.generation = ++hist.maxGeneration;
4907 while (hist.done.length > hist.undoDepth)
5001 while (hist.done.length > hist.undoDepth)
4908 hist.done.shift();
5002 hist.done.shift();
4909 if (hist.dirtyCounter < 0)
4910 // The user has made a change after undoing past the last clean state.
4911 // We can never get back to a clean state now until markClean() is called.
4912 hist.dirtyCounter = NaN;
4913 else
4914 hist.dirtyCounter++;
4915 }
5003 }
4916 hist.lastTime = time;
5004 hist.lastTime = time;
4917 hist.lastOp = opId;
5005 hist.lastOp = opId;
4918 hist.lastOrigin = change.origin;
5006 hist.lastOrigin = change.origin;
4919 }
5007 }
4920
5008
4921 function removeClearedSpans(spans) {
5009 function removeClearedSpans(spans) {
4922 if (!spans) return null;
5010 if (!spans) return null;
4923 for (var i = 0, out; i < spans.length; ++i) {
5011 for (var i = 0, out; i < spans.length; ++i) {
4924 if (spans[i].marker.explicitlyCleared) { if (!out) out = spans.slice(0, i); }
5012 if (spans[i].marker.explicitlyCleared) { if (!out) out = spans.slice(0, i); }
4925 else if (out) out.push(spans[i]);
5013 else if (out) out.push(spans[i]);
4926 }
5014 }
4927 return !out ? spans : out.length ? out : null;
5015 return !out ? spans : out.length ? out : null;
4928 }
5016 }
4929
5017
4930 function getOldSpans(doc, change) {
5018 function getOldSpans(doc, change) {
4931 var found = change["spans_" + doc.id];
5019 var found = change["spans_" + doc.id];
4932 if (!found) return null;
5020 if (!found) return null;
4933 for (var i = 0, nw = []; i < change.text.length; ++i)
5021 for (var i = 0, nw = []; i < change.text.length; ++i)
4934 nw.push(removeClearedSpans(found[i]));
5022 nw.push(removeClearedSpans(found[i]));
4935 return nw;
5023 return nw;
4936 }
5024 }
4937
5025
4938 // Used both to provide a JSON-safe object in .getHistory, and, when
5026 // Used both to provide a JSON-safe object in .getHistory, and, when
4939 // detaching a document, to split the history in two
5027 // detaching a document, to split the history in two
4940 function copyHistoryArray(events, newGroup) {
5028 function copyHistoryArray(events, newGroup) {
4941 for (var i = 0, copy = []; i < events.length; ++i) {
5029 for (var i = 0, copy = []; i < events.length; ++i) {
4942 var event = events[i], changes = event.changes, newChanges = [];
5030 var event = events[i], changes = event.changes, newChanges = [];
4943 copy.push({changes: newChanges, anchorBefore: event.anchorBefore, headBefore: event.headBefore,
5031 copy.push({changes: newChanges, anchorBefore: event.anchorBefore, headBefore: event.headBefore,
4944 anchorAfter: event.anchorAfter, headAfter: event.headAfter});
5032 anchorAfter: event.anchorAfter, headAfter: event.headAfter});
4945 for (var j = 0; j < changes.length; ++j) {
5033 for (var j = 0; j < changes.length; ++j) {
4946 var change = changes[j], m;
5034 var change = changes[j], m;
4947 newChanges.push({from: change.from, to: change.to, text: change.text});
5035 newChanges.push({from: change.from, to: change.to, text: change.text});
4948 if (newGroup) for (var prop in change) if (m = prop.match(/^spans_(\d+)$/)) {
5036 if (newGroup) for (var prop in change) if (m = prop.match(/^spans_(\d+)$/)) {
4949 if (indexOf(newGroup, Number(m[1])) > -1) {
5037 if (indexOf(newGroup, Number(m[1])) > -1) {
4950 lst(newChanges)[prop] = change[prop];
5038 lst(newChanges)[prop] = change[prop];
4951 delete change[prop];
5039 delete change[prop];
4952 }
5040 }
4953 }
5041 }
4954 }
5042 }
4955 }
5043 }
4956 return copy;
5044 return copy;
4957 }
5045 }
4958
5046
4959 // Rebasing/resetting history to deal with externally-sourced changes
5047 // Rebasing/resetting history to deal with externally-sourced changes
4960
5048
4961 function rebaseHistSel(pos, from, to, diff) {
5049 function rebaseHistSel(pos, from, to, diff) {
4962 if (to < pos.line) {
5050 if (to < pos.line) {
4963 pos.line += diff;
5051 pos.line += diff;
4964 } else if (from < pos.line) {
5052 } else if (from < pos.line) {
4965 pos.line = from;
5053 pos.line = from;
4966 pos.ch = 0;
5054 pos.ch = 0;
4967 }
5055 }
4968 }
5056 }
4969
5057
4970 // Tries to rebase an array of history events given a change in the
5058 // Tries to rebase an array of history events given a change in the
4971 // document. If the change touches the same lines as the event, the
5059 // document. If the change touches the same lines as the event, the
4972 // event, and everything 'behind' it, is discarded. If the change is
5060 // event, and everything 'behind' it, is discarded. If the change is
4973 // before the event, the event's positions are updated. Uses a
5061 // before the event, the event's positions are updated. Uses a
4974 // copy-on-write scheme for the positions, to avoid having to
5062 // copy-on-write scheme for the positions, to avoid having to
4975 // reallocate them all on every rebase, but also avoid problems with
5063 // reallocate them all on every rebase, but also avoid problems with
4976 // shared position objects being unsafely updated.
5064 // shared position objects being unsafely updated.
4977 function rebaseHistArray(array, from, to, diff) {
5065 function rebaseHistArray(array, from, to, diff) {
4978 for (var i = 0; i < array.length; ++i) {
5066 for (var i = 0; i < array.length; ++i) {
4979 var sub = array[i], ok = true;
5067 var sub = array[i], ok = true;
4980 for (var j = 0; j < sub.changes.length; ++j) {
5068 for (var j = 0; j < sub.changes.length; ++j) {
4981 var cur = sub.changes[j];
5069 var cur = sub.changes[j];
4982 if (!sub.copied) { cur.from = copyPos(cur.from); cur.to = copyPos(cur.to); }
5070 if (!sub.copied) { cur.from = copyPos(cur.from); cur.to = copyPos(cur.to); }
4983 if (to < cur.from.line) {
5071 if (to < cur.from.line) {
4984 cur.from.line += diff;
5072 cur.from.line += diff;
4985 cur.to.line += diff;
5073 cur.to.line += diff;
4986 } else if (from <= cur.to.line) {
5074 } else if (from <= cur.to.line) {
4987 ok = false;
5075 ok = false;
4988 break;
5076 break;
4989 }
5077 }
4990 }
5078 }
4991 if (!sub.copied) {
5079 if (!sub.copied) {
4992 sub.anchorBefore = copyPos(sub.anchorBefore); sub.headBefore = copyPos(sub.headBefore);
5080 sub.anchorBefore = copyPos(sub.anchorBefore); sub.headBefore = copyPos(sub.headBefore);
4993 sub.anchorAfter = copyPos(sub.anchorAfter); sub.readAfter = copyPos(sub.headAfter);
5081 sub.anchorAfter = copyPos(sub.anchorAfter); sub.readAfter = copyPos(sub.headAfter);
4994 sub.copied = true;
5082 sub.copied = true;
4995 }
5083 }
4996 if (!ok) {
5084 if (!ok) {
4997 array.splice(0, i + 1);
5085 array.splice(0, i + 1);
4998 i = 0;
5086 i = 0;
4999 } else {
5087 } else {
5000 rebaseHistSel(sub.anchorBefore); rebaseHistSel(sub.headBefore);
5088 rebaseHistSel(sub.anchorBefore); rebaseHistSel(sub.headBefore);
5001 rebaseHistSel(sub.anchorAfter); rebaseHistSel(sub.headAfter);
5089 rebaseHistSel(sub.anchorAfter); rebaseHistSel(sub.headAfter);
5002 }
5090 }
5003 }
5091 }
5004 }
5092 }
5005
5093
5006 function rebaseHist(hist, change) {
5094 function rebaseHist(hist, change) {
5007 var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1;
5095 var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1;
5008 rebaseHistArray(hist.done, from, to, diff);
5096 rebaseHistArray(hist.done, from, to, diff);
5009 rebaseHistArray(hist.undone, from, to, diff);
5097 rebaseHistArray(hist.undone, from, to, diff);
5010 }
5098 }
5011
5099
5012 // EVENT OPERATORS
5100 // EVENT OPERATORS
5013
5101
5014 function stopMethod() {e_stop(this);}
5102 function stopMethod() {e_stop(this);}
5015 // Ensure an event has a stop method.
5103 // Ensure an event has a stop method.
5016 function addStop(event) {
5104 function addStop(event) {
5017 if (!event.stop) event.stop = stopMethod;
5105 if (!event.stop) event.stop = stopMethod;
5018 return event;
5106 return event;
5019 }
5107 }
5020
5108
5021 function e_preventDefault(e) {
5109 function e_preventDefault(e) {
5022 if (e.preventDefault) e.preventDefault();
5110 if (e.preventDefault) e.preventDefault();
5023 else e.returnValue = false;
5111 else e.returnValue = false;
5024 }
5112 }
5025 function e_stopPropagation(e) {
5113 function e_stopPropagation(e) {
5026 if (e.stopPropagation) e.stopPropagation();
5114 if (e.stopPropagation) e.stopPropagation();
5027 else e.cancelBubble = true;
5115 else e.cancelBubble = true;
5028 }
5116 }
5117 function e_defaultPrevented(e) {
5118 return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false;
5119 }
5029 function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);}
5120 function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);}
5030 CodeMirror.e_stop = e_stop;
5121 CodeMirror.e_stop = e_stop;
5031 CodeMirror.e_preventDefault = e_preventDefault;
5122 CodeMirror.e_preventDefault = e_preventDefault;
5032 CodeMirror.e_stopPropagation = e_stopPropagation;
5123 CodeMirror.e_stopPropagation = e_stopPropagation;
5033
5124
5034 function e_target(e) {return e.target || e.srcElement;}
5125 function e_target(e) {return e.target || e.srcElement;}
5035 function e_button(e) {
5126 function e_button(e) {
5036 var b = e.which;
5127 var b = e.which;
5037 if (b == null) {
5128 if (b == null) {
5038 if (e.button & 1) b = 1;
5129 if (e.button & 1) b = 1;
5039 else if (e.button & 2) b = 3;
5130 else if (e.button & 2) b = 3;
5040 else if (e.button & 4) b = 2;
5131 else if (e.button & 4) b = 2;
5041 }
5132 }
5042 if (mac && e.ctrlKey && b == 1) b = 3;
5133 if (mac && e.ctrlKey && b == 1) b = 3;
5043 return b;
5134 return b;
5044 }
5135 }
5045
5136
5046 // EVENT HANDLING
5137 // EVENT HANDLING
5047
5138
5048 function on(emitter, type, f) {
5139 function on(emitter, type, f) {
5049 if (emitter.addEventListener)
5140 if (emitter.addEventListener)
5050 emitter.addEventListener(type, f, false);
5141 emitter.addEventListener(type, f, false);
5051 else if (emitter.attachEvent)
5142 else if (emitter.attachEvent)
5052 emitter.attachEvent("on" + type, f);
5143 emitter.attachEvent("on" + type, f);
5053 else {
5144 else {
5054 var map = emitter._handlers || (emitter._handlers = {});
5145 var map = emitter._handlers || (emitter._handlers = {});
5055 var arr = map[type] || (map[type] = []);
5146 var arr = map[type] || (map[type] = []);
5056 arr.push(f);
5147 arr.push(f);
5057 }
5148 }
5058 }
5149 }
5059
5150
5060 function off(emitter, type, f) {
5151 function off(emitter, type, f) {
5061 if (emitter.removeEventListener)
5152 if (emitter.removeEventListener)
5062 emitter.removeEventListener(type, f, false);
5153 emitter.removeEventListener(type, f, false);
5063 else if (emitter.detachEvent)
5154 else if (emitter.detachEvent)
5064 emitter.detachEvent("on" + type, f);
5155 emitter.detachEvent("on" + type, f);
5065 else {
5156 else {
5066 var arr = emitter._handlers && emitter._handlers[type];
5157 var arr = emitter._handlers && emitter._handlers[type];
5067 if (!arr) return;
5158 if (!arr) return;
5068 for (var i = 0; i < arr.length; ++i)
5159 for (var i = 0; i < arr.length; ++i)
5069 if (arr[i] == f) { arr.splice(i, 1); break; }
5160 if (arr[i] == f) { arr.splice(i, 1); break; }
5070 }
5161 }
5071 }
5162 }
5072
5163
5073 function signal(emitter, type /*, values...*/) {
5164 function signal(emitter, type /*, values...*/) {
5074 var arr = emitter._handlers && emitter._handlers[type];
5165 var arr = emitter._handlers && emitter._handlers[type];
5075 if (!arr) return;
5166 if (!arr) return;
5076 var args = Array.prototype.slice.call(arguments, 2);
5167 var args = Array.prototype.slice.call(arguments, 2);
5077 for (var i = 0; i < arr.length; ++i) arr[i].apply(null, args);
5168 for (var i = 0; i < arr.length; ++i) arr[i].apply(null, args);
5078 }
5169 }
5079
5170
5080 var delayedCallbacks, delayedCallbackDepth = 0;
5171 var delayedCallbacks, delayedCallbackDepth = 0;
5081 function signalLater(emitter, type /*, values...*/) {
5172 function signalLater(emitter, type /*, values...*/) {
5082 var arr = emitter._handlers && emitter._handlers[type];
5173 var arr = emitter._handlers && emitter._handlers[type];
5083 if (!arr) return;
5174 if (!arr) return;
5084 var args = Array.prototype.slice.call(arguments, 2);
5175 var args = Array.prototype.slice.call(arguments, 2);
5085 if (!delayedCallbacks) {
5176 if (!delayedCallbacks) {
5086 ++delayedCallbackDepth;
5177 ++delayedCallbackDepth;
5087 delayedCallbacks = [];
5178 delayedCallbacks = [];
5088 setTimeout(fireDelayed, 0);
5179 setTimeout(fireDelayed, 0);
5089 }
5180 }
5090 function bnd(f) {return function(){f.apply(null, args);};};
5181 function bnd(f) {return function(){f.apply(null, args);};};
5091 for (var i = 0; i < arr.length; ++i)
5182 for (var i = 0; i < arr.length; ++i)
5092 delayedCallbacks.push(bnd(arr[i]));
5183 delayedCallbacks.push(bnd(arr[i]));
5093 }
5184 }
5094
5185
5186 function signalDOMEvent(cm, e) {
5187 signal(cm, e.type, cm, e);
5188 return e_defaultPrevented(e);
5189 }
5190
5095 function fireDelayed() {
5191 function fireDelayed() {
5096 --delayedCallbackDepth;
5192 --delayedCallbackDepth;
5097 var delayed = delayedCallbacks;
5193 var delayed = delayedCallbacks;
5098 delayedCallbacks = null;
5194 delayedCallbacks = null;
5099 for (var i = 0; i < delayed.length; ++i) delayed[i]();
5195 for (var i = 0; i < delayed.length; ++i) delayed[i]();
5100 }
5196 }
5101
5197
5102 function hasHandler(emitter, type) {
5198 function hasHandler(emitter, type) {
5103 var arr = emitter._handlers && emitter._handlers[type];
5199 var arr = emitter._handlers && emitter._handlers[type];
5104 return arr && arr.length > 0;
5200 return arr && arr.length > 0;
5105 }
5201 }
5106
5202
5107 CodeMirror.on = on; CodeMirror.off = off; CodeMirror.signal = signal;
5203 CodeMirror.on = on; CodeMirror.off = off; CodeMirror.signal = signal;
5108
5204
5109 // MISC UTILITIES
5205 // MISC UTILITIES
5110
5206
5111 // Number of pixels added to scroller and sizer to hide scrollbar
5207 // Number of pixels added to scroller and sizer to hide scrollbar
5112 var scrollerCutOff = 30;
5208 var scrollerCutOff = 30;
5113
5209
5114 // Returned or thrown by various protocols to signal 'I'm not
5210 // Returned or thrown by various protocols to signal 'I'm not
5115 // handling this'.
5211 // handling this'.
5116 var Pass = CodeMirror.Pass = {toString: function(){return "CodeMirror.Pass";}};
5212 var Pass = CodeMirror.Pass = {toString: function(){return "CodeMirror.Pass";}};
5117
5213
5118 function Delayed() {this.id = null;}
5214 function Delayed() {this.id = null;}
5119 Delayed.prototype = {set: function(ms, f) {clearTimeout(this.id); this.id = setTimeout(f, ms);}};
5215 Delayed.prototype = {set: function(ms, f) {clearTimeout(this.id); this.id = setTimeout(f, ms);}};
5120
5216
5121 // Counts the column offset in a string, taking tabs into account.
5217 // Counts the column offset in a string, taking tabs into account.
5122 // Used mostly to find indentation.
5218 // Used mostly to find indentation.
5123 function countColumn(string, end, tabSize, startIndex, startValue) {
5219 function countColumn(string, end, tabSize, startIndex, startValue) {
5124 if (end == null) {
5220 if (end == null) {
5125 end = string.search(/[^\s\u00a0]/);
5221 end = string.search(/[^\s\u00a0]/);
5126 if (end == -1) end = string.length;
5222 if (end == -1) end = string.length;
5127 }
5223 }
5128 for (var i = startIndex || 0, n = startValue || 0; i < end; ++i) {
5224 for (var i = startIndex || 0, n = startValue || 0; i < end; ++i) {
5129 if (string.charAt(i) == "\t") n += tabSize - (n % tabSize);
5225 if (string.charAt(i) == "\t") n += tabSize - (n % tabSize);
5130 else ++n;
5226 else ++n;
5131 }
5227 }
5132 return n;
5228 return n;
5133 }
5229 }
5134 CodeMirror.countColumn = countColumn;
5230 CodeMirror.countColumn = countColumn;
5135
5231
5136 var spaceStrs = [""];
5232 var spaceStrs = [""];
5137 function spaceStr(n) {
5233 function spaceStr(n) {
5138 while (spaceStrs.length <= n)
5234 while (spaceStrs.length <= n)
5139 spaceStrs.push(lst(spaceStrs) + " ");
5235 spaceStrs.push(lst(spaceStrs) + " ");
5140 return spaceStrs[n];
5236 return spaceStrs[n];
5141 }
5237 }
5142
5238
5143 function lst(arr) { return arr[arr.length-1]; }
5239 function lst(arr) { return arr[arr.length-1]; }
5144
5240
5145 function selectInput(node) {
5241 function selectInput(node) {
5146 if (ios) { // Mobile Safari apparently has a bug where select() is broken.
5242 if (ios) { // Mobile Safari apparently has a bug where select() is broken.
5147 node.selectionStart = 0;
5243 node.selectionStart = 0;
5148 node.selectionEnd = node.value.length;
5244 node.selectionEnd = node.value.length;
5149 } else node.select();
5245 } else {
5246 // Suppress mysterious IE10 errors
5247 try { node.select(); }
5248 catch(_e) {}
5249 }
5150 }
5250 }
5151
5251
5152 function indexOf(collection, elt) {
5252 function indexOf(collection, elt) {
5153 if (collection.indexOf) return collection.indexOf(elt);
5253 if (collection.indexOf) return collection.indexOf(elt);
5154 for (var i = 0, e = collection.length; i < e; ++i)
5254 for (var i = 0, e = collection.length; i < e; ++i)
5155 if (collection[i] == elt) return i;
5255 if (collection[i] == elt) return i;
5156 return -1;
5256 return -1;
5157 }
5257 }
5158
5258
5159 function createObj(base, props) {
5259 function createObj(base, props) {
5160 function Obj() {}
5260 function Obj() {}
5161 Obj.prototype = base;
5261 Obj.prototype = base;
5162 var inst = new Obj();
5262 var inst = new Obj();
5163 if (props) copyObj(props, inst);
5263 if (props) copyObj(props, inst);
5164 return inst;
5264 return inst;
5165 }
5265 }
5166
5266
5167 function copyObj(obj, target) {
5267 function copyObj(obj, target) {
5168 if (!target) target = {};
5268 if (!target) target = {};
5169 for (var prop in obj) if (obj.hasOwnProperty(prop)) target[prop] = obj[prop];
5269 for (var prop in obj) if (obj.hasOwnProperty(prop)) target[prop] = obj[prop];
5170 return target;
5270 return target;
5171 }
5271 }
5172
5272
5173 function emptyArray(size) {
5273 function emptyArray(size) {
5174 for (var a = [], i = 0; i < size; ++i) a.push(undefined);
5274 for (var a = [], i = 0; i < size; ++i) a.push(undefined);
5175 return a;
5275 return a;
5176 }
5276 }
5177
5277
5178 function bind(f) {
5278 function bind(f) {
5179 var args = Array.prototype.slice.call(arguments, 1);
5279 var args = Array.prototype.slice.call(arguments, 1);
5180 return function(){return f.apply(null, args);};
5280 return function(){return f.apply(null, args);};
5181 }
5281 }
5182
5282
5183 var nonASCIISingleCaseWordChar = /[\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc]/;
5283 var nonASCIISingleCaseWordChar = /[\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/;
5184 function isWordChar(ch) {
5284 function isWordChar(ch) {
5185 return /\w/.test(ch) || ch > "\x80" &&
5285 return /\w/.test(ch) || ch > "\x80" &&
5186 (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch));
5286 (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch));
5187 }
5287 }
5188
5288
5189 function isEmpty(obj) {
5289 function isEmpty(obj) {
5190 for (var n in obj) if (obj.hasOwnProperty(n) && obj[n]) return false;
5290 for (var n in obj) if (obj.hasOwnProperty(n) && obj[n]) return false;
5191 return true;
5291 return true;
5192 }
5292 }
5193
5293
5194 var isExtendingChar = /[\u0300-\u036F\u0483-\u0487\u0488-\u0489\u0591-\u05BD\u05BF\u05C1-\u05C2\u05C4-\u05C5\u05C7\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7-\u06E8\u06EA-\u06ED\uA66F\uA670-\uA672\uA674-\uA67D\uA69F\udc00-\udfff]/;
5294 var isExtendingChar = /[\u0300-\u036F\u0483-\u0487\u0488-\u0489\u0591-\u05BD\u05BF\u05C1-\u05C2\u05C4-\u05C5\u05C7\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7-\u06E8\u06EA-\u06ED\uA66F\uA670-\uA672\uA674-\uA67D\uA69F\udc00-\udfff]/;
5195
5295
5196 // DOM UTILITIES
5296 // DOM UTILITIES
5197
5297
5198 function elt(tag, content, className, style) {
5298 function elt(tag, content, className, style) {
5199 var e = document.createElement(tag);
5299 var e = document.createElement(tag);
5200 if (className) e.className = className;
5300 if (className) e.className = className;
5201 if (style) e.style.cssText = style;
5301 if (style) e.style.cssText = style;
5202 if (typeof content == "string") setTextContent(e, content);
5302 if (typeof content == "string") setTextContent(e, content);
5203 else if (content) for (var i = 0; i < content.length; ++i) e.appendChild(content[i]);
5303 else if (content) for (var i = 0; i < content.length; ++i) e.appendChild(content[i]);
5204 return e;
5304 return e;
5205 }
5305 }
5206
5306
5207 function removeChildren(e) {
5307 function removeChildren(e) {
5208 for (var count = e.childNodes.length; count > 0; --count)
5308 for (var count = e.childNodes.length; count > 0; --count)
5209 e.removeChild(e.firstChild);
5309 e.removeChild(e.firstChild);
5210 return e;
5310 return e;
5211 }
5311 }
5212
5312
5213 function removeChildrenAndAdd(parent, e) {
5313 function removeChildrenAndAdd(parent, e) {
5214 return removeChildren(parent).appendChild(e);
5314 return removeChildren(parent).appendChild(e);
5215 }
5315 }
5216
5316
5217 function setTextContent(e, str) {
5317 function setTextContent(e, str) {
5218 if (ie_lt9) {
5318 if (ie_lt9) {
5219 e.innerHTML = "";
5319 e.innerHTML = "";
5220 e.appendChild(document.createTextNode(str));
5320 e.appendChild(document.createTextNode(str));
5221 } else e.textContent = str;
5321 } else e.textContent = str;
5222 }
5322 }
5223
5323
5224 function getRect(node) {
5324 function getRect(node) {
5225 return node.getBoundingClientRect();
5325 return node.getBoundingClientRect();
5226 }
5326 }
5227 CodeMirror.replaceGetRect = function(f) { getRect = f; };
5327 CodeMirror.replaceGetRect = function(f) { getRect = f; };
5228
5328
5229 // FEATURE DETECTION
5329 // FEATURE DETECTION
5230
5330
5231 // Detect drag-and-drop
5331 // Detect drag-and-drop
5232 var dragAndDrop = function() {
5332 var dragAndDrop = function() {
5233 // There is *some* kind of drag-and-drop support in IE6-8, but I
5333 // There is *some* kind of drag-and-drop support in IE6-8, but I
5234 // couldn't get it to work yet.
5334 // couldn't get it to work yet.
5235 if (ie_lt9) return false;
5335 if (ie_lt9) return false;
5236 var div = elt('div');
5336 var div = elt('div');
5237 return "draggable" in div || "dragDrop" in div;
5337 return "draggable" in div || "dragDrop" in div;
5238 }();
5338 }();
5239
5339
5240 // For a reason I have yet to figure out, some browsers disallow
5340 // For a reason I have yet to figure out, some browsers disallow
5241 // word wrapping between certain characters *only* if a new inline
5341 // word wrapping between certain characters *only* if a new inline
5242 // element is started between them. This makes it hard to reliably
5342 // element is started between them. This makes it hard to reliably
5243 // measure the position of things, since that requires inserting an
5343 // measure the position of things, since that requires inserting an
5244 // extra span. This terribly fragile set of regexps matches the
5344 // extra span. This terribly fragile set of tests matches the
5245 // character combinations that suffer from this phenomenon on the
5345 // character combinations that suffer from this phenomenon on the
5246 // various browsers.
5346 // various browsers.
5247 var spanAffectsWrapping = /^$/; // Won't match any two-character string
5347 function spanAffectsWrapping() { return false; }
5248 if (gecko) spanAffectsWrapping = /$'/;
5348 if (gecko) // Only for "$'"
5249 else if (safari && !/Version\/([6-9]|\d\d)\b/.test(navigator.userAgent)) spanAffectsWrapping = /\-[^ \-?]|\?[^ !'\"\),.\-\/:;\?\]\}]/;
5349 spanAffectsWrapping = function(str, i) {
5250 else if (webkit) spanAffectsWrapping = /[~!#%&*)=+}\]|\"\.>,:;][({[<]|-[^\-?\.]|\?[\w~`@#$%\^&*(_=+{[|><]/;
5350 return str.charCodeAt(i - 1) == 36 && str.charCodeAt(i) == 39;
5351 };
5352 else if (safari && !/Version\/([6-9]|\d\d)\b/.test(navigator.userAgent))
5353 spanAffectsWrapping = function(str, i) {
5354 return /\-[^ \-?]|\?[^ !\'\"\),.\-\/:;\?\]\}]/.test(str.slice(i - 1, i + 1));
5355 };
5356 else if (webkit)
5357 spanAffectsWrapping = function(str, i) {
5358 if (i > 1 && str.charCodeAt(i - 1) == 45 && /\w/.test(str.charAt(i - 2)) && /[^\-?\.]/.test(str.charAt(i)))
5359 return true;
5360 return /[~!#%&*)=+}\]|\"\.>,:;][({[<]|-[^\-?\.\u2010-\u201f\u2026]|\?[\w~`@#$%\^&*(_=+{[|><]|…[\w~`@#$%\^&*(_=+{[><]/.test(str.slice(i - 1, i + 1));
5361 };
5251
5362
5252 var knownScrollbarWidth;
5363 var knownScrollbarWidth;
5253 function scrollbarWidth(measure) {
5364 function scrollbarWidth(measure) {
5254 if (knownScrollbarWidth != null) return knownScrollbarWidth;
5365 if (knownScrollbarWidth != null) return knownScrollbarWidth;
5255 var test = elt("div", null, null, "width: 50px; height: 50px; overflow-x: scroll");
5366 var test = elt("div", null, null, "width: 50px; height: 50px; overflow-x: scroll");
5256 removeChildrenAndAdd(measure, test);
5367 removeChildrenAndAdd(measure, test);
5257 if (test.offsetWidth)
5368 if (test.offsetWidth)
5258 knownScrollbarWidth = test.offsetHeight - test.clientHeight;
5369 knownScrollbarWidth = test.offsetHeight - test.clientHeight;
5259 return knownScrollbarWidth || 0;
5370 return knownScrollbarWidth || 0;
5260 }
5371 }
5261
5372
5262 var zwspSupported;
5373 var zwspSupported;
5263 function zeroWidthElement(measure) {
5374 function zeroWidthElement(measure) {
5264 if (zwspSupported == null) {
5375 if (zwspSupported == null) {
5265 var test = elt("span", "\u200b");
5376 var test = elt("span", "\u200b");
5266 removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")]));
5377 removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")]));
5267 if (measure.firstChild.offsetHeight != 0)
5378 if (measure.firstChild.offsetHeight != 0)
5268 zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !ie_lt8;
5379 zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !ie_lt8;
5269 }
5380 }
5270 if (zwspSupported) return elt("span", "\u200b");
5381 if (zwspSupported) return elt("span", "\u200b");
5271 else return elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px");
5382 else return elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px");
5272 }
5383 }
5273
5384
5274 // See if "".split is the broken IE version, if so, provide an
5385 // See if "".split is the broken IE version, if so, provide an
5275 // alternative way to split lines.
5386 // alternative way to split lines.
5276 var splitLines = "\n\nb".split(/\n/).length != 3 ? function(string) {
5387 var splitLines = "\n\nb".split(/\n/).length != 3 ? function(string) {
5277 var pos = 0, result = [], l = string.length;
5388 var pos = 0, result = [], l = string.length;
5278 while (pos <= l) {
5389 while (pos <= l) {
5279 var nl = string.indexOf("\n", pos);
5390 var nl = string.indexOf("\n", pos);
5280 if (nl == -1) nl = string.length;
5391 if (nl == -1) nl = string.length;
5281 var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl);
5392 var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl);
5282 var rt = line.indexOf("\r");
5393 var rt = line.indexOf("\r");
5283 if (rt != -1) {
5394 if (rt != -1) {
5284 result.push(line.slice(0, rt));
5395 result.push(line.slice(0, rt));
5285 pos += rt + 1;
5396 pos += rt + 1;
5286 } else {
5397 } else {
5287 result.push(line);
5398 result.push(line);
5288 pos = nl + 1;
5399 pos = nl + 1;
5289 }
5400 }
5290 }
5401 }
5291 return result;
5402 return result;
5292 } : function(string){return string.split(/\r\n?|\n/);};
5403 } : function(string){return string.split(/\r\n?|\n/);};
5293 CodeMirror.splitLines = splitLines;
5404 CodeMirror.splitLines = splitLines;
5294
5405
5295 var hasSelection = window.getSelection ? function(te) {
5406 var hasSelection = window.getSelection ? function(te) {
5296 try { return te.selectionStart != te.selectionEnd; }
5407 try { return te.selectionStart != te.selectionEnd; }
5297 catch(e) { return false; }
5408 catch(e) { return false; }
5298 } : function(te) {
5409 } : function(te) {
5299 try {var range = te.ownerDocument.selection.createRange();}
5410 try {var range = te.ownerDocument.selection.createRange();}
5300 catch(e) {}
5411 catch(e) {}
5301 if (!range || range.parentElement() != te) return false;
5412 if (!range || range.parentElement() != te) return false;
5302 return range.compareEndPoints("StartToEnd", range) != 0;
5413 return range.compareEndPoints("StartToEnd", range) != 0;
5303 };
5414 };
5304
5415
5305 var hasCopyEvent = (function() {
5416 var hasCopyEvent = (function() {
5306 var e = elt("div");
5417 var e = elt("div");
5307 if ("oncopy" in e) return true;
5418 if ("oncopy" in e) return true;
5308 e.setAttribute("oncopy", "return;");
5419 e.setAttribute("oncopy", "return;");
5309 return typeof e.oncopy == 'function';
5420 return typeof e.oncopy == 'function';
5310 })();
5421 })();
5311
5422
5312 // KEY NAMING
5423 // KEY NAMING
5313
5424
5314 var keyNames = {3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
5425 var keyNames = {3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
5315 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
5426 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
5316 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
5427 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
5317 46: "Delete", 59: ";", 91: "Mod", 92: "Mod", 93: "Mod", 109: "-", 107: "=", 127: "Delete",
5428 46: "Delete", 59: ";", 91: "Mod", 92: "Mod", 93: "Mod", 109: "-", 107: "=", 127: "Delete",
5318 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\",
5429 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\",
5319 221: "]", 222: "'", 63276: "PageUp", 63277: "PageDown", 63275: "End", 63273: "Home",
5430 221: "]", 222: "'", 63276: "PageUp", 63277: "PageDown", 63275: "End", 63273: "Home",
5320 63234: "Left", 63232: "Up", 63235: "Right", 63233: "Down", 63302: "Insert", 63272: "Delete"};
5431 63234: "Left", 63232: "Up", 63235: "Right", 63233: "Down", 63302: "Insert", 63272: "Delete"};
5321 CodeMirror.keyNames = keyNames;
5432 CodeMirror.keyNames = keyNames;
5322 (function() {
5433 (function() {
5323 // Number keys
5434 // Number keys
5324 for (var i = 0; i < 10; i++) keyNames[i + 48] = String(i);
5435 for (var i = 0; i < 10; i++) keyNames[i + 48] = String(i);
5325 // Alphabetic keys
5436 // Alphabetic keys
5326 for (var i = 65; i <= 90; i++) keyNames[i] = String.fromCharCode(i);
5437 for (var i = 65; i <= 90; i++) keyNames[i] = String.fromCharCode(i);
5327 // Function keys
5438 // Function keys
5328 for (var i = 1; i <= 12; i++) keyNames[i + 111] = keyNames[i + 63235] = "F" + i;
5439 for (var i = 1; i <= 12; i++) keyNames[i + 111] = keyNames[i + 63235] = "F" + i;
5329 })();
5440 })();
5330
5441
5331 // BIDI HELPERS
5442 // BIDI HELPERS
5332
5443
5333 function iterateBidiSections(order, from, to, f) {
5444 function iterateBidiSections(order, from, to, f) {
5334 if (!order) return f(from, to, "ltr");
5445 if (!order) return f(from, to, "ltr");
5335 for (var i = 0; i < order.length; ++i) {
5446 for (var i = 0; i < order.length; ++i) {
5336 var part = order[i];
5447 var part = order[i];
5337 if (part.from < to && part.to > from || from == to && part.to == from)
5448 if (part.from < to && part.to > from || from == to && part.to == from)
5338 f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr");
5449 f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr");
5339 }
5450 }
5340 }
5451 }
5341
5452
5342 function bidiLeft(part) { return part.level % 2 ? part.to : part.from; }
5453 function bidiLeft(part) { return part.level % 2 ? part.to : part.from; }
5343 function bidiRight(part) { return part.level % 2 ? part.from : part.to; }
5454 function bidiRight(part) { return part.level % 2 ? part.from : part.to; }
5344
5455
5345 function lineLeft(line) { var order = getOrder(line); return order ? bidiLeft(order[0]) : 0; }
5456 function lineLeft(line) { var order = getOrder(line); return order ? bidiLeft(order[0]) : 0; }
5346 function lineRight(line) {
5457 function lineRight(line) {
5347 var order = getOrder(line);
5458 var order = getOrder(line);
5348 if (!order) return line.text.length;
5459 if (!order) return line.text.length;
5349 return bidiRight(lst(order));
5460 return bidiRight(lst(order));
5350 }
5461 }
5351
5462
5352 function lineStart(cm, lineN) {
5463 function lineStart(cm, lineN) {
5353 var line = getLine(cm.doc, lineN);
5464 var line = getLine(cm.doc, lineN);
5354 var visual = visualLine(cm.doc, line);
5465 var visual = visualLine(cm.doc, line);
5355 if (visual != line) lineN = lineNo(visual);
5466 if (visual != line) lineN = lineNo(visual);
5356 var order = getOrder(visual);
5467 var order = getOrder(visual);
5357 var ch = !order ? 0 : order[0].level % 2 ? lineRight(visual) : lineLeft(visual);
5468 var ch = !order ? 0 : order[0].level % 2 ? lineRight(visual) : lineLeft(visual);
5358 return Pos(lineN, ch);
5469 return Pos(lineN, ch);
5359 }
5470 }
5360 function lineEnd(cm, lineN) {
5471 function lineEnd(cm, lineN) {
5361 var merged, line;
5472 var merged, line;
5362 while (merged = collapsedSpanAtEnd(line = getLine(cm.doc, lineN)))
5473 while (merged = collapsedSpanAtEnd(line = getLine(cm.doc, lineN)))
5363 lineN = merged.find().to.line;
5474 lineN = merged.find().to.line;
5364 var order = getOrder(line);
5475 var order = getOrder(line);
5365 var ch = !order ? line.text.length : order[0].level % 2 ? lineLeft(line) : lineRight(line);
5476 var ch = !order ? line.text.length : order[0].level % 2 ? lineLeft(line) : lineRight(line);
5366 return Pos(lineN, ch);
5477 return Pos(lineN, ch);
5367 }
5478 }
5368
5479
5480 function compareBidiLevel(order, a, b) {
5481 var linedir = order[0].level;
5482 if (a == linedir) return true;
5483 if (b == linedir) return false;
5484 return a < b;
5485 }
5486 var bidiOther;
5487 function getBidiPartAt(order, pos) {
5488 for (var i = 0, found; i < order.length; ++i) {
5489 var cur = order[i];
5490 if (cur.from < pos && cur.to > pos) { bidiOther = null; return i; }
5491 if (cur.from == pos || cur.to == pos) {
5492 if (found == null) {
5493 found = i;
5494 } else if (compareBidiLevel(order, cur.level, order[found].level)) {
5495 bidiOther = found;
5496 return i;
5497 } else {
5498 bidiOther = i;
5499 return found;
5500 }
5501 }
5502 }
5503 bidiOther = null;
5504 return found;
5505 }
5506
5507 function moveInLine(line, pos, dir, byUnit) {
5508 if (!byUnit) return pos + dir;
5509 do pos += dir;
5510 while (pos > 0 && isExtendingChar.test(line.text.charAt(pos)));
5511 return pos;
5512 }
5513
5369 // This is somewhat involved. It is needed in order to move
5514 // This is somewhat involved. It is needed in order to move
5370 // 'visually' through bi-directional text -- i.e., pressing left
5515 // 'visually' through bi-directional text -- i.e., pressing left
5371 // should make the cursor go left, even when in RTL text. The
5516 // should make the cursor go left, even when in RTL text. The
5372 // tricky part is the 'jumps', where RTL and LTR text touch each
5517 // tricky part is the 'jumps', where RTL and LTR text touch each
5373 // other. This often requires the cursor offset to move more than
5518 // other. This often requires the cursor offset to move more than
5374 // one unit, in order to visually move one unit.
5519 // one unit, in order to visually move one unit.
5375 function moveVisually(line, start, dir, byUnit) {
5520 function moveVisually(line, start, dir, byUnit) {
5376 var bidi = getOrder(line);
5521 var bidi = getOrder(line);
5377 if (!bidi) return moveLogically(line, start, dir, byUnit);
5522 if (!bidi) return moveLogically(line, start, dir, byUnit);
5378 var moveOneUnit = byUnit ? function(pos, dir) {
5523 var pos = getBidiPartAt(bidi, start), part = bidi[pos];
5379 do pos += dir;
5524 var target = moveInLine(line, start, part.level % 2 ? -dir : dir, byUnit);
5380 while (pos > 0 && isExtendingChar.test(line.text.charAt(pos)));
5525
5381 return pos;
5526 for (;;) {
5382 } : function(pos, dir) { return pos + dir; };
5527 if (target > part.from && target < part.to) return target;
5383 var linedir = bidi[0].level;
5528 if (target == part.from || target == part.to) {
5384 for (var i = 0; i < bidi.length; ++i) {
5529 if (getBidiPartAt(bidi, target) == pos) return target;
5385 var part = bidi[i], sticky = part.level % 2 == linedir;
5530 part = bidi[pos += dir];
5386 if ((part.from < start && part.to > start) ||
5531 return (dir > 0) == part.level % 2 ? part.to : part.from;
5387 (sticky && (part.from == start || part.to == start))) break;
5388 }
5389 var target = moveOneUnit(start, part.level % 2 ? -dir : dir);
5390
5391 while (target != null) {
5392 if (part.level % 2 == linedir) {
5393 if (target < part.from || target > part.to) {
5394 part = bidi[i += dir];
5395 target = part && (dir > 0 == part.level % 2 ? moveOneUnit(part.to, -1) : moveOneUnit(part.from, 1));
5396 } else break;
5397 } else {
5532 } else {
5398 if (target == bidiLeft(part)) {
5533 part = bidi[pos += dir];
5399 part = bidi[--i];
5534 if (!part) return null;
5400 target = part && bidiRight(part);
5535 if ((dir > 0) == part.level % 2)
5401 } else if (target == bidiRight(part)) {
5536 target = moveInLine(line, part.to, -1, byUnit);
5402 part = bidi[++i];
5537 else
5403 target = part && bidiLeft(part);
5538 target = moveInLine(line, part.from, 1, byUnit);
5404 } else break;
5405 }
5539 }
5406 }
5540 }
5407
5408 return target < 0 || target > line.text.length ? null : target;
5409 }
5541 }
5410
5542
5411 function moveLogically(line, start, dir, byUnit) {
5543 function moveLogically(line, start, dir, byUnit) {
5412 var target = start + dir;
5544 var target = start + dir;
5413 if (byUnit) while (target > 0 && isExtendingChar.test(line.text.charAt(target))) target += dir;
5545 if (byUnit) while (target > 0 && isExtendingChar.test(line.text.charAt(target))) target += dir;
5414 return target < 0 || target > line.text.length ? null : target;
5546 return target < 0 || target > line.text.length ? null : target;
5415 }
5547 }
5416
5548
5417 // Bidirectional ordering algorithm
5549 // Bidirectional ordering algorithm
5418 // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm
5550 // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm
5419 // that this (partially) implements.
5551 // that this (partially) implements.
5420
5552
5421 // One-char codes used for character types:
5553 // One-char codes used for character types:
5422 // L (L): Left-to-Right
5554 // L (L): Left-to-Right
5423 // R (R): Right-to-Left
5555 // R (R): Right-to-Left
5424 // r (AL): Right-to-Left Arabic
5556 // r (AL): Right-to-Left Arabic
5425 // 1 (EN): European Number
5557 // 1 (EN): European Number
5426 // + (ES): European Number Separator
5558 // + (ES): European Number Separator
5427 // % (ET): European Number Terminator
5559 // % (ET): European Number Terminator
5428 // n (AN): Arabic Number
5560 // n (AN): Arabic Number
5429 // , (CS): Common Number Separator
5561 // , (CS): Common Number Separator
5430 // m (NSM): Non-Spacing Mark
5562 // m (NSM): Non-Spacing Mark
5431 // b (BN): Boundary Neutral
5563 // b (BN): Boundary Neutral
5432 // s (B): Paragraph Separator
5564 // s (B): Paragraph Separator
5433 // t (S): Segment Separator
5565 // t (S): Segment Separator
5434 // w (WS): Whitespace
5566 // w (WS): Whitespace
5435 // N (ON): Other Neutrals
5567 // N (ON): Other Neutrals
5436
5568
5437 // Returns null if characters are ordered as they appear
5569 // Returns null if characters are ordered as they appear
5438 // (left-to-right), or an array of sections ({from, to, level}
5570 // (left-to-right), or an array of sections ({from, to, level}
5439 // objects) in the order in which they occur visually.
5571 // objects) in the order in which they occur visually.
5440 var bidiOrdering = (function() {
5572 var bidiOrdering = (function() {
5441 // Character types for codepoints 0 to 0xff
5573 // Character types for codepoints 0 to 0xff
5442 var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLL";
5574 var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLL";
5443 // Character types for codepoints 0x600 to 0x6ff
5575 // Character types for codepoints 0x600 to 0x6ff
5444 var arabicTypes = "rrrrrrrrrrrr,rNNmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmrrrrrrrnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmNmmmmrrrrrrrrrrrrrrrrrr";
5576 var arabicTypes = "rrrrrrrrrrrr,rNNmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmrrrrrrrnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmNmmmmrrrrrrrrrrrrrrrrrr";
5445 function charType(code) {
5577 function charType(code) {
5446 if (code <= 0xff) return lowTypes.charAt(code);
5578 if (code <= 0xff) return lowTypes.charAt(code);
5447 else if (0x590 <= code && code <= 0x5f4) return "R";
5579 else if (0x590 <= code && code <= 0x5f4) return "R";
5448 else if (0x600 <= code && code <= 0x6ff) return arabicTypes.charAt(code - 0x600);
5580 else if (0x600 <= code && code <= 0x6ff) return arabicTypes.charAt(code - 0x600);
5449 else if (0x700 <= code && code <= 0x8ac) return "r";
5581 else if (0x700 <= code && code <= 0x8ac) return "r";
5450 else return "L";
5582 else return "L";
5451 }
5583 }
5452
5584
5453 var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;
5585 var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;
5454 var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/;
5586 var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/;
5455 // Browsers seem to always treat the boundaries of block elements as being L.
5587 // Browsers seem to always treat the boundaries of block elements as being L.
5456 var outerType = "L";
5588 var outerType = "L";
5457
5589
5458 return function(str) {
5590 return function(str) {
5459 if (!bidiRE.test(str)) return false;
5591 if (!bidiRE.test(str)) return false;
5460 var len = str.length, types = [];
5592 var len = str.length, types = [];
5461 for (var i = 0, type; i < len; ++i)
5593 for (var i = 0, type; i < len; ++i)
5462 types.push(type = charType(str.charCodeAt(i)));
5594 types.push(type = charType(str.charCodeAt(i)));
5463
5595
5464 // W1. Examine each non-spacing mark (NSM) in the level run, and
5596 // W1. Examine each non-spacing mark (NSM) in the level run, and
5465 // change the type of the NSM to the type of the previous
5597 // change the type of the NSM to the type of the previous
5466 // character. If the NSM is at the start of the level run, it will
5598 // character. If the NSM is at the start of the level run, it will
5467 // get the type of sor.
5599 // get the type of sor.
5468 for (var i = 0, prev = outerType; i < len; ++i) {
5600 for (var i = 0, prev = outerType; i < len; ++i) {
5469 var type = types[i];
5601 var type = types[i];
5470 if (type == "m") types[i] = prev;
5602 if (type == "m") types[i] = prev;
5471 else prev = type;
5603 else prev = type;
5472 }
5604 }
5473
5605
5474 // W2. Search backwards from each instance of a European number
5606 // W2. Search backwards from each instance of a European number
5475 // until the first strong type (R, L, AL, or sor) is found. If an
5607 // until the first strong type (R, L, AL, or sor) is found. If an
5476 // AL is found, change the type of the European number to Arabic
5608 // AL is found, change the type of the European number to Arabic
5477 // number.
5609 // number.
5478 // W3. Change all ALs to R.
5610 // W3. Change all ALs to R.
5479 for (var i = 0, cur = outerType; i < len; ++i) {
5611 for (var i = 0, cur = outerType; i < len; ++i) {
5480 var type = types[i];
5612 var type = types[i];
5481 if (type == "1" && cur == "r") types[i] = "n";
5613 if (type == "1" && cur == "r") types[i] = "n";
5482 else if (isStrong.test(type)) { cur = type; if (type == "r") types[i] = "R"; }
5614 else if (isStrong.test(type)) { cur = type; if (type == "r") types[i] = "R"; }
5483 }
5615 }
5484
5616
5485 // W4. A single European separator between two European numbers
5617 // W4. A single European separator between two European numbers
5486 // changes to a European number. A single common separator between
5618 // changes to a European number. A single common separator between
5487 // two numbers of the same type changes to that type.
5619 // two numbers of the same type changes to that type.
5488 for (var i = 1, prev = types[0]; i < len - 1; ++i) {
5620 for (var i = 1, prev = types[0]; i < len - 1; ++i) {
5489 var type = types[i];
5621 var type = types[i];
5490 if (type == "+" && prev == "1" && types[i+1] == "1") types[i] = "1";
5622 if (type == "+" && prev == "1" && types[i+1] == "1") types[i] = "1";
5491 else if (type == "," && prev == types[i+1] &&
5623 else if (type == "," && prev == types[i+1] &&
5492 (prev == "1" || prev == "n")) types[i] = prev;
5624 (prev == "1" || prev == "n")) types[i] = prev;
5493 prev = type;
5625 prev = type;
5494 }
5626 }
5495
5627
5496 // W5. A sequence of European terminators adjacent to European
5628 // W5. A sequence of European terminators adjacent to European
5497 // numbers changes to all European numbers.
5629 // numbers changes to all European numbers.
5498 // W6. Otherwise, separators and terminators change to Other
5630 // W6. Otherwise, separators and terminators change to Other
5499 // Neutral.
5631 // Neutral.
5500 for (var i = 0; i < len; ++i) {
5632 for (var i = 0; i < len; ++i) {
5501 var type = types[i];
5633 var type = types[i];
5502 if (type == ",") types[i] = "N";
5634 if (type == ",") types[i] = "N";
5503 else if (type == "%") {
5635 else if (type == "%") {
5504 for (var end = i + 1; end < len && types[end] == "%"; ++end) {}
5636 for (var end = i + 1; end < len && types[end] == "%"; ++end) {}
5505 var replace = (i && types[i-1] == "!") || (end < len - 1 && types[end] == "1") ? "1" : "N";
5637 var replace = (i && types[i-1] == "!") || (end < len - 1 && types[end] == "1") ? "1" : "N";
5506 for (var j = i; j < end; ++j) types[j] = replace;
5638 for (var j = i; j < end; ++j) types[j] = replace;
5507 i = end - 1;
5639 i = end - 1;
5508 }
5640 }
5509 }
5641 }
5510
5642
5511 // W7. Search backwards from each instance of a European number
5643 // W7. Search backwards from each instance of a European number
5512 // until the first strong type (R, L, or sor) is found. If an L is
5644 // until the first strong type (R, L, or sor) is found. If an L is
5513 // found, then change the type of the European number to L.
5645 // found, then change the type of the European number to L.
5514 for (var i = 0, cur = outerType; i < len; ++i) {
5646 for (var i = 0, cur = outerType; i < len; ++i) {
5515 var type = types[i];
5647 var type = types[i];
5516 if (cur == "L" && type == "1") types[i] = "L";
5648 if (cur == "L" && type == "1") types[i] = "L";
5517 else if (isStrong.test(type)) cur = type;
5649 else if (isStrong.test(type)) cur = type;
5518 }
5650 }
5519
5651
5520 // N1. A sequence of neutrals takes the direction of the
5652 // N1. A sequence of neutrals takes the direction of the
5521 // surrounding strong text if the text on both sides has the same
5653 // surrounding strong text if the text on both sides has the same
5522 // direction. European and Arabic numbers act as if they were R in
5654 // direction. European and Arabic numbers act as if they were R in
5523 // terms of their influence on neutrals. Start-of-level-run (sor)
5655 // terms of their influence on neutrals. Start-of-level-run (sor)
5524 // and end-of-level-run (eor) are used at level run boundaries.
5656 // and end-of-level-run (eor) are used at level run boundaries.
5525 // N2. Any remaining neutrals take the embedding direction.
5657 // N2. Any remaining neutrals take the embedding direction.
5526 for (var i = 0; i < len; ++i) {
5658 for (var i = 0; i < len; ++i) {
5527 if (isNeutral.test(types[i])) {
5659 if (isNeutral.test(types[i])) {
5528 for (var end = i + 1; end < len && isNeutral.test(types[end]); ++end) {}
5660 for (var end = i + 1; end < len && isNeutral.test(types[end]); ++end) {}
5529 var before = (i ? types[i-1] : outerType) == "L";
5661 var before = (i ? types[i-1] : outerType) == "L";
5530 var after = (end < len - 1 ? types[end] : outerType) == "L";
5662 var after = (end < len - 1 ? types[end] : outerType) == "L";
5531 var replace = before || after ? "L" : "R";
5663 var replace = before || after ? "L" : "R";
5532 for (var j = i; j < end; ++j) types[j] = replace;
5664 for (var j = i; j < end; ++j) types[j] = replace;
5533 i = end - 1;
5665 i = end - 1;
5534 }
5666 }
5535 }
5667 }
5536
5668
5537 // Here we depart from the documented algorithm, in order to avoid
5669 // Here we depart from the documented algorithm, in order to avoid
5538 // building up an actual levels array. Since there are only three
5670 // building up an actual levels array. Since there are only three
5539 // levels (0, 1, 2) in an implementation that doesn't take
5671 // levels (0, 1, 2) in an implementation that doesn't take
5540 // explicit embedding into account, we can build up the order on
5672 // explicit embedding into account, we can build up the order on
5541 // the fly, without following the level-based algorithm.
5673 // the fly, without following the level-based algorithm.
5542 var order = [], m;
5674 var order = [], m;
5543 for (var i = 0; i < len;) {
5675 for (var i = 0; i < len;) {
5544 if (countsAsLeft.test(types[i])) {
5676 if (countsAsLeft.test(types[i])) {
5545 var start = i;
5677 var start = i;
5546 for (++i; i < len && countsAsLeft.test(types[i]); ++i) {}
5678 for (++i; i < len && countsAsLeft.test(types[i]); ++i) {}
5547 order.push({from: start, to: i, level: 0});
5679 order.push({from: start, to: i, level: 0});
5548 } else {
5680 } else {
5549 var pos = i, at = order.length;
5681 var pos = i, at = order.length;
5550 for (++i; i < len && types[i] != "L"; ++i) {}
5682 for (++i; i < len && types[i] != "L"; ++i) {}
5551 for (var j = pos; j < i;) {
5683 for (var j = pos; j < i;) {
5552 if (countsAsNum.test(types[j])) {
5684 if (countsAsNum.test(types[j])) {
5553 if (pos < j) order.splice(at, 0, {from: pos, to: j, level: 1});
5685 if (pos < j) order.splice(at, 0, {from: pos, to: j, level: 1});
5554 var nstart = j;
5686 var nstart = j;
5555 for (++j; j < i && countsAsNum.test(types[j]); ++j) {}
5687 for (++j; j < i && countsAsNum.test(types[j]); ++j) {}
5556 order.splice(at, 0, {from: nstart, to: j, level: 2});
5688 order.splice(at, 0, {from: nstart, to: j, level: 2});
5557 pos = j;
5689 pos = j;
5558 } else ++j;
5690 } else ++j;
5559 }
5691 }
5560 if (pos < i) order.splice(at, 0, {from: pos, to: i, level: 1});
5692 if (pos < i) order.splice(at, 0, {from: pos, to: i, level: 1});
5561 }
5693 }
5562 }
5694 }
5563 if (order[0].level == 1 && (m = str.match(/^\s+/))) {
5695 if (order[0].level == 1 && (m = str.match(/^\s+/))) {
5564 order[0].from = m[0].length;
5696 order[0].from = m[0].length;
5565 order.unshift({from: 0, to: m[0].length, level: 0});
5697 order.unshift({from: 0, to: m[0].length, level: 0});
5566 }
5698 }
5567 if (lst(order).level == 1 && (m = str.match(/\s+$/))) {
5699 if (lst(order).level == 1 && (m = str.match(/\s+$/))) {
5568 lst(order).to -= m[0].length;
5700 lst(order).to -= m[0].length;
5569 order.push({from: len - m[0].length, to: len, level: 0});
5701 order.push({from: len - m[0].length, to: len, level: 0});
5570 }
5702 }
5571 if (order[0].level != lst(order).level)
5703 if (order[0].level != lst(order).level)
5572 order.push({from: len, to: len, level: order[0].level});
5704 order.push({from: len, to: len, level: order[0].level});
5573
5705
5574 return order;
5706 return order;
5575 };
5707 };
5576 })();
5708 })();
5577
5709
5578 // THE END
5710 // THE END
5579
5711
5580 CodeMirror.version = "3.12";
5712 CodeMirror.version = "3.14.0";
5581
5713
5582 return CodeMirror;
5714 return CodeMirror;
5583 })();
5715 })();
@@ -1,61 +1,61 b''
1 <!doctype html>
1 <!doctype html>
2 <html>
2 <html>
3 <head>
3 <head>
4 <meta charset="utf-8">
4 <meta charset="utf-8">
5 <title>CodeMirror: APL mode</title>
5 <title>CodeMirror: APL mode</title>
6 <link rel="stylesheet" href="../../doc/docs.css">
6 <link rel="stylesheet" href="../../doc/docs.css">
7 <link rel="stylesheet" href="../../lib/codemirror.css">
7 <link rel="stylesheet" href="../../lib/codemirror.css">
8 <script src="../../lib/codemirror.js"></script>
8 <script src="../../lib/codemirror.js"></script>
9 <script src="../../addon/edit/matchbrackets.js"></script>
9 <script src="../../addon/edit/matchbrackets.js"></script>
10 <script src="apl.js"></script>
10 <script src="./apl.js"></script>
11 <style>
11 <style>
12 .CodeMirror { border: 2px inset #dee; }
12 .CodeMirror { border: 2px inset #dee; }
13 </style>
13 </style>
14 </head>
14 </head>
15 <body>
15 <body>
16 <h1>CodeMirror: APL mode</h1>
16 <h1>CodeMirror: APL mode</h1>
17
17
18 <form><textarea id="code" name="code">
18 <form><textarea id="code" name="code">
19 ⍝ Conway's game of life
19 ⍝ Conway's game of life
20
20
21 ⍝ This example was inspired by the impressive demo at
21 ⍝ This example was inspired by the impressive demo at
22 ⍝ http://www.youtube.com/watch?v=a9xAKttWgP4
22 ⍝ http://www.youtube.com/watch?v=a9xAKttWgP4
23
23
24 ⍝ Create a matrix:
24 ⍝ Create a matrix:
25 ⍝ 0 1 1
25 ⍝ 0 1 1
26 ⍝ 1 1 0
26 ⍝ 1 1 0
27 ⍝ 0 1 0
27 ⍝ 0 1 0
28 creature ← (3 3 ⍴ ⍳ 9) ∈ 1 2 3 4 7 ⍝ Original creature from demo
28 creature ← (3 3 ⍴ ⍳ 9) ∈ 1 2 3 4 7 ⍝ Original creature from demo
29 creature ← (3 3 ⍴ ⍳ 9) ∈ 1 3 6 7 8 ⍝ Glider
29 creature ← (3 3 ⍴ ⍳ 9) ∈ 1 3 6 7 8 ⍝ Glider
30
30
31 ⍝ Place the creature on a larger board, near the centre
31 ⍝ Place the creature on a larger board, near the centre
32 board ← Β―1 βŠ– Β―2 ⌽ 5 7 ↑ creature
32 board ← Β―1 βŠ– Β―2 ⌽ 5 7 ↑ creature
33
33
34 ⍝ A function to move from one generation to the next
34 ⍝ A function to move from one generation to the next
35 life ← {∨/ 1 ⍡ ∧ 3 4 = βŠ‚+/ +⌿ 1 0 Β―1 ∘.βŠ– 1 0 Β―1 ⌽¨ βŠ‚β΅}
35 life ← {∨/ 1 ⍡ ∧ 3 4 = βŠ‚+/ +⌿ 1 0 Β―1 ∘.βŠ– 1 0 Β―1 ⌽¨ βŠ‚β΅}
36
36
37 ⍝ Compute n-th generation and format it as a
37 ⍝ Compute n-th generation and format it as a
38 ⍝ character matrix
38 ⍝ character matrix
39 gen ← {' #'[(life ⍣ ⍡) board]}
39 gen ← {' #'[(life ⍣ ⍡) board]}
40
40
41 ⍝ Show first three generations
41 ⍝ Show first three generations
42 (gen 1) (gen 2) (gen 3)
42 (gen 1) (gen 2) (gen 3)
43 </textarea></form>
43 </textarea></form>
44
44
45 <script>
45 <script>
46 var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
46 var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
47 lineNumbers: true,
47 lineNumbers: true,
48 matchBrackets: true,
48 matchBrackets: true,
49 mode: "text/apl"
49 mode: "text/apl"
50 });
50 });
51 </script>
51 </script>
52
52
53 <p>Simple mode that tries to handle APL as well as it can.</p>
53 <p>Simple mode that tries to handle APL as well as it can.</p>
54 <p>It attempts to label functions/operators based upon
54 <p>It attempts to label functions/operators based upon
55 monadic/dyadic usage (but this is far from fully fleshed out).
55 monadic/dyadic usage (but this is far from fully fleshed out).
56 This means there are meaningful classnames so hover states can
56 This means there are meaningful classnames so hover states can
57 have popups etc.</p>
57 have popups etc.</p>
58
58
59 <p><strong>MIME types defined:</strong> <code>text/apl</code> (APL code)</p>
59 <p><strong>MIME types defined:</strong> <code>text/apl</code> (APL code)</p>
60 </body>
60 </body>
61 </html>
61 </html>
@@ -1,305 +1,77 b''
1 CodeMirror.modeInfo = [
1 CodeMirror.modeInfo = [
2 {name: 'APL', mime: 'text/apl', mode: 'apl'},
2 {name: 'APL', mime: 'text/apl', mode: 'apl'},
3 {name: 'Asterisk', mime: 'text/x-asterisk', mode: 'asterisk'},
3 {name: 'Asterisk', mime: 'text/x-asterisk', mode: 'asterisk'},
4 {name: 'C', mime: 'text/x-csrc', mode: 'clike'},
4 {name: 'C', mime: 'text/x-csrc', mode: 'clike'},
5 {name: 'C++', mime: 'text/x-c++src', mode: 'clike'},
5 {name: 'C++', mime: 'text/x-c++src', mode: 'clike'},
6 {name: 'Cobol', mime: 'text/x-cobol', mode: 'cobol'},
6 {name: 'Cobol', mime: 'text/x-cobol', mode: 'cobol'},
7 {name: 'Java', mime: 'text/x-java', mode: 'clike'},
7 {name: 'Java', mime: 'text/x-java', mode: 'clike'},
8 {name: 'C#', mime: 'text/x-csharp', mode: 'clike'},
8 {name: 'C#', mime: 'text/x-csharp', mode: 'clike'},
9 {name: 'Scala', mime: 'text/x-scala', mode: 'clike'},
9 {name: 'Scala', mime: 'text/x-scala', mode: 'clike'},
10 {name: 'Clojure', mime: 'text/x-clojure', mode: 'clojure'},
10 {name: 'Clojure', mime: 'text/x-clojure', mode: 'clojure'},
11 {name: 'CoffeeScript', mime: 'text/x-coffeescript', mode: 'coffeescript'},
11 {name: 'CoffeeScript', mime: 'text/x-coffeescript', mode: 'coffeescript'},
12 {name: 'Common Lisp', mime: 'text/x-common-lisp', mode: 'commonlisp'},
12 {name: 'Common Lisp', mime: 'text/x-common-lisp', mode: 'commonlisp'},
13 {name: 'CSS', mime: 'text/css', mode: 'css'},
13 {name: 'CSS', mime: 'text/css', mode: 'css'},
14 {name: 'D', mime: 'text/x-d', mode: 'd'},
14 {name: 'D', mime: 'text/x-d', mode: 'd'},
15 {name: 'diff', mime: 'text/x-diff', mode: 'diff'},
15 {name: 'diff', mime: 'text/x-diff', mode: 'diff'},
16 {name: 'ECL', mime: 'text/x-ecl', mode: 'ecl'},
16 {name: 'ECL', mime: 'text/x-ecl', mode: 'ecl'},
17 {name: 'Erlang', mime: 'text/x-erlang', mode: 'erlang'},
17 {name: 'Erlang', mime: 'text/x-erlang', mode: 'erlang'},
18 {name: 'Gas', mime: 'text/x-gas', mode: 'gas'},
18 {name: 'Gas', mime: 'text/x-gas', mode: 'gas'},
19 {name: 'GitHub Flavored Markdown', mode: 'gfm'},
19 {name: 'GO', mime: 'text/x-go', mode: 'go'},
20 {name: 'GO', mime: 'text/x-go', mode: 'go'},
20 {name: 'Groovy', mime: 'text/x-groovy', mode: 'groovy'},
21 {name: 'Groovy', mime: 'text/x-groovy', mode: 'groovy'},
21 {name: 'Haskell', mime: 'text/x-haskell', mode: 'haskell'},
22 {name: 'Haskell', mime: 'text/x-haskell', mode: 'haskell'},
22 {name: 'Haxe', mime: 'text/x-haxe', mode: 'haxe'},
23 {name: 'Haxe', mime: 'text/x-haxe', mode: 'haxe'},
23 {name: 'ASP.NET', mime: 'application/x-aspx', mode: 'htmlembedded'},
24 {name: 'ASP.NET', mime: 'application/x-aspx', mode: 'htmlembedded'},
24 {name: 'Embedded Javascript', mime: 'application/x-ejs', mode: 'htmlembedded'},
25 {name: 'Embedded Javascript', mime: 'application/x-ejs', mode: 'htmlembedded'},
25 {name: 'JavaServer Pages', mime: 'application/x-jsp', mode: 'htmlembedded'},
26 {name: 'JavaServer Pages', mime: 'application/x-jsp', mode: 'htmlembedded'},
26 {name: 'HTML', mime: 'text/html', mode: 'htmlmixed'},
27 {name: 'HTML', mime: 'text/html', mode: 'htmlmixed'},
27 {name: 'HTTP', mime: 'message/http', mode: 'http'},
28 {name: 'HTTP', mime: 'message/http', mode: 'http'},
28 {name: 'JavaScript', mime: 'text/javascript', mode: 'javascript'},
29 {name: 'JavaScript', mime: 'text/javascript', mode: 'javascript'},
29 {name: 'JSON', mime: 'application/x-json', mode: 'javascript'},
30 {name: 'JSON', mime: 'application/x-json', mode: 'javascript'},
30 {name: 'JSON', mime: 'application/json', mode: 'javascript'},
31 {name: 'JSON', mime: 'application/json', mode: 'javascript'},
31 {name: 'TypeScript', mime: 'application/typescript', mode: 'javascript'},
32 {name: 'TypeScript', mime: 'application/typescript', mode: 'javascript'},
32 {name: 'Jinja2', mime: 'jinja2', mode: 'jinja2'},
33 {name: 'Jinja2', mime: 'jinja2', mode: 'jinja2'},
33 {name: 'LESS', mime: 'text/x-less', mode: 'less'},
34 {name: 'LESS', mime: 'text/x-less', mode: 'less'},
34 {name: 'LiveScript', mime: 'text/x-livescript', mode: 'livescript'},
35 {name: 'LiveScript', mime: 'text/x-livescript', mode: 'livescript'},
35 {name: 'Lua', mime: 'text/x-lua', mode: 'lua'},
36 {name: 'Lua', mime: 'text/x-lua', mode: 'lua'},
36 {name: 'Markdown (GitHub-flavour)', mime: 'text/x-markdown', mode: 'markdown'},
37 {name: 'Markdown (GitHub-flavour)', mime: 'text/x-markdown', mode: 'markdown'},
37 {name: 'mIRC', mime: 'text/mirc', mode: 'mirc'},
38 {name: 'mIRC', mime: 'text/mirc', mode: 'mirc'},
38 {name: 'NTriples', mime: 'text/n-triples', mode: 'ntriples'},
39 {name: 'NTriples', mime: 'text/n-triples', mode: 'ntriples'},
39 {name: 'OCaml', mime: 'text/x-ocaml', mode: 'ocaml'},
40 {name: 'OCaml', mime: 'text/x-ocaml', mode: 'ocaml'},
40 {name: 'Pascal', mime: 'text/x-pascal', mode: 'pascal'},
41 {name: 'Pascal', mime: 'text/x-pascal', mode: 'pascal'},
41 {name: 'Perl', mime: 'text/x-perl', mode: 'perl'},
42 {name: 'Perl', mime: 'text/x-perl', mode: 'perl'},
42 {name: 'PHP', mime: 'text/x-php', mode: 'php'},
43 {name: 'PHP', mime: 'text/x-php', mode: 'php'},
43 {name: 'PHP(HTML)', mime: 'application/x-httpd-php', mode: 'php'},
44 {name: 'PHP(HTML)', mime: 'application/x-httpd-php', mode: 'php'},
44 {name: 'Pig', mime: 'text/x-pig', mode: 'pig'},
45 {name: 'Pig', mime: 'text/x-pig', mode: 'pig'},
45 {name: 'Plain Text', mime: 'text/plain', mode: 'null'},
46 {name: 'Plain Text', mime: 'text/plain', mode: 'null'},
46 {name: 'Properties files', mime: 'text/x-properties', mode: 'clike'},
47 {name: 'Properties files', mime: 'text/x-properties', mode: 'clike'},
47 {name: 'Python', mime: 'text/x-python', mode: 'python'},
48 {name: 'Python', mime: 'text/x-python', mode: 'python'},
48 {name: 'R', mime: 'text/x-rsrc', mode: 'r'},
49 {name: 'R', mime: 'text/x-rsrc', mode: 'r'},
49 {name: 'reStructuredText', mime: 'text/x-rst', mode: 'rst'},
50 {name: 'reStructuredText', mime: 'text/x-rst', mode: 'rst'},
50 {name: 'Ruby', mime: 'text/x-ruby', mode: 'ruby'},
51 {name: 'Ruby', mime: 'text/x-ruby', mode: 'ruby'},
51 {name: 'Rust', mime: 'text/x-rustsrc', mode: 'rust'},
52 {name: 'Rust', mime: 'text/x-rustsrc', mode: 'rust'},
52 {name: 'Sass', mime: 'text/x-sass', mode: 'sass'},
53 {name: 'Sass', mime: 'text/x-sass', mode: 'sass'},
53 {name: 'Scheme', mime: 'text/x-scheme', mode: 'scheme'},
54 {name: 'Scheme', mime: 'text/x-scheme', mode: 'scheme'},
54 {name: 'SCSS', mime: 'text/x-scss', mode: 'css'},
55 {name: 'SCSS', mime: 'text/x-scss', mode: 'css'},
55 {name: 'Shell', mime: 'text/x-sh', mode: 'shell'},
56 {name: 'Shell', mime: 'text/x-sh', mode: 'shell'},
56 {name: 'Sieve', mime: 'application/sieve', mode: 'sieve'},
57 {name: 'Sieve', mime: 'application/sieve', mode: 'sieve'},
57 {name: 'Smalltalk', mime: 'text/x-stsrc', mode: 'smalltalk'},
58 {name: 'Smalltalk', mime: 'text/x-stsrc', mode: 'smalltalk'},
58 {name: 'Smarty', mime: 'text/x-smarty', mode: 'smarty'},
59 {name: 'Smarty', mime: 'text/x-smarty', mode: 'smarty'},
59 {name: 'SPARQL', mime: 'application/x-sparql-query', mode: 'sparql'},
60 {name: 'SPARQL', mime: 'application/x-sparql-query', mode: 'sparql'},
60 {name: 'SQL', mime: 'text/x-sql', mode: 'sql'},
61 {name: 'SQL', mime: 'text/x-sql', mode: 'sql'},
61 {name: 'MariaDB', mime: 'text/x-mariadb', mode: 'sql'},
62 {name: 'MariaDB', mime: 'text/x-mariadb', mode: 'sql'},
62 {name: 'sTeX', mime: 'text/x-stex', mode: 'stex'},
63 {name: 'sTeX', mime: 'text/x-stex', mode: 'stex'},
63 {name: 'LaTeX', mime: 'text/x-latex', mode: 'stex'},
64 {name: 'LaTeX', mime: 'text/x-latex', mode: 'stex'},
64 {name: 'Tcl', mime: 'text/x-tcl', mode: 'tcl'},
65 {name: 'Tcl', mime: 'text/x-tcl', mode: 'tcl'},
65 {name: 'TiddlyWiki ', mime: 'text/x-tiddlywiki', mode: 'tiddlywiki'},
66 {name: 'TiddlyWiki ', mime: 'text/x-tiddlywiki', mode: 'tiddlywiki'},
66 {name: 'Tiki wiki', mime: 'text/tiki', mode: 'tiki'},
67 {name: 'Tiki wiki', mime: 'text/tiki', mode: 'tiki'},
67 {name: 'VB.NET', mime: 'text/x-vb', mode: 'vb'},
68 {name: 'VB.NET', mime: 'text/x-vb', mode: 'vb'},
68 {name: 'VBScript', mime: 'text/vbscript', mode: 'vbscript'},
69 {name: 'VBScript', mime: 'text/vbscript', mode: 'vbscript'},
69 {name: 'Velocity', mime: 'text/velocity', mode: 'velocity'},
70 {name: 'Velocity', mime: 'text/velocity', mode: 'velocity'},
70 {name: 'Verilog', mime: 'text/x-verilog', mode: 'verilog'},
71 {name: 'Verilog', mime: 'text/x-verilog', mode: 'verilog'},
71 {name: 'XML', mime: 'application/xml', mode: 'xml'},
72 {name: 'XML', mime: 'application/xml', mode: 'xml'},
72 {name: 'HTML', mime: 'text/html', mode: 'xml'},
73 {name: 'HTML', mime: 'text/html', mode: 'xml'},
73 {name: 'XQuery', mime: 'application/xquery', mode: 'xquery'},
74 {name: 'XQuery', mime: 'application/xquery', mode: 'xquery'},
74 {name: 'YAML', mime: 'text/x-yaml', mode: 'yaml'},
75 {name: 'YAML', mime: 'text/x-yaml', mode: 'yaml'},
75 {name: 'Z80', mime: 'text/x-z80', mode: 'z80'}
76 {name: 'Z80', mime: 'text/x-z80', mode: 'z80'}
76 ];
77 ];
77
78
79 MIME_TO_EXT = {
80 'application/javascript': ['*.js'],
81 'text/javascript': ['*.js'],
82 'application/json': ['*.json'],
83 'application/postscript': ['*.ps', '*.eps'],
84 'application/x-actionscript': ['*.as'],
85 'application/x-actionscript3': ['*.as'],
86 'application/x-awk': ['*.awk'],
87 'application/x-befunge': ['*.befunge'],
88 'application/x-brainfuck': ['*.bf', '*.b'],
89 'application/x-cheetah': ['*.tmpl', '*.spt'],
90 'application/x-coldfusion': ['*.cfm', '*.cfml', '*.cfc'],
91 'application/x-csh': ['*.tcsh', '*.csh'],
92 'application/x-dos-batch': ['*.bat', '*.cmd'],
93 'application/x-ecl': ['*.ecl'],
94 'application/x-evoque': ['*.evoque'],
95 'application/x-fantom': ['*.fan'],
96 'application/x-genshi': ['*.kid'],
97 'application/x-gettext': ['*.pot', '*.po'],
98 'application/x-jsp': ['*.jsp'],
99 'application/x-mako': ['*.mao'],
100 'application/x-mason': ['*.m', '*.mhtml', '*.mc', '*.mi', 'autohandler', 'dhandler'],
101 'application/x-myghty': ['*.myt', 'autodelegate'],
102 'application/x-php': ['*.phtml'],
103 'application/x-pypylog': ['*.pypylog'],
104 'application/x-qml': ['*.qml'],
105 'application/x-sh': ['*.sh', '*.ksh', '*.bash', '*.ebuild', '*.eclass', '.bashrc', 'bashrc', '.bash_*', 'bash_*'],
106 'application/x-sh-session': ['*.shell-session'],
107 'application/x-shell-session': ['*.sh-session'],
108 'application/x-smarty': ['*.tpl'],
109 'application/x-ssp': ['*.ssp'],
110 'application/x-troff': ['*.[1234567]', '*.man'],
111 'application/x-urbiscript': ['*.u'],
112 'application/xml+evoque': ['*.xml'],
113 'application/xml-dtd': ['*.dtd'],
114 'application/xsl+xml': ['*.xsl', '*.xslt', '*.xpl'],
115 'text/S-plus': ['*.S', '*.R', '.Rhistory', '.Rprofile'],
116 'text/coffeescript': ['*.coffee'],
117 'text/css': ['*.css'],
118 'text/haxe': ['*.hx'],
119 'text/html': ['*.html', '*.htm', '*.xhtml', '*.xslt'],
120 'text/html+evoque': ['*.html'],
121 'text/html+ruby': ['*.rhtml'],
122 'text/idl': ['*.pro'],
123 'text/livescript': ['*.ls'],
124 'text/matlab': ['*.m'],
125 'text/octave': ['*.m'],
126 'text/plain': ['*.txt'],
127 'text/scilab': ['*.sci', '*.sce', '*.tst'],
128 'text/smali': ['*.smali'],
129 'text/x-abap': ['*.abap'],
130 'text/x-ada': ['*.adb', '*.ads', '*.ada'],
131 'text/x-apacheconf': ['.htaccess', 'apache.conf', 'apache2.conf'],
132 'text/x-aspectj': ['*.aj'],
133 'text/x-asymptote': ['*.asy'],
134 'text/x-autohotkey': ['*.ahk', '*.ahkl'],
135 'text/x-autoit': ['*.au3'],
136 'text/x-bmx': ['*.bmx'],
137 'text/x-boo': ['*.boo'],
138 'text/x-c++hdr': ['*.cpp', '*.hpp', '*.c++', '*.h++', '*.cc', '*.hh', '*.cxx', '*.hxx', '*.C', '*.H', '*.cp','*.CPP'],
139 'text/x-c-objdump': ['*.c-objdump'],
140 'text/x-ceylon': ['*.ceylon'],
141 'text/x-chdr': ['*.c', '*.h', '*.idc'],
142 'text/x-clojure': ['*.clj'],
143 'text/x-cmake': ['*.cmake', 'CMakeLists.txt'],
144 'text/x-cobol': ['*.cob', '*.COB', '*.cpy', '*.CPY'],
145 'text/x-common-lisp': ['*.cl', '*.lisp', '*.el'],
146 'text/x-coq': ['*.v'],
147 'text/x-cpp-objdump': ['*.cpp-objdump', '*.c++-objdump', '*.cxx-objdump'],
148 'text/x-crocsrc': ['*.croc'],
149 'text/x-csharp': ['*.cs'],
150 'text/x-cuda': ['*.cu', '*.cuh'],
151 'text/x-cython': ['*.pyx', '*.pxd', '*.pxi'],
152 'text/x-d-objdump': ['*.d-objdump'],
153 'text/x-dart': ['*.dart'],
154 'text/x-dg': ['*.dg'],
155 'text/x-diff': ['*.diff', '*.patch'],
156 'text/x-dsrc': ['*.d', '*.di'],
157 'text/x-duel': ['*.duel', '*.jbst'],
158 'text/x-dylan': ['*.dylan', '*.dyl', '*.intr'],
159 'text/x-dylan-console': ['*.dylan-console'],
160 'text/x-dylan-lid': ['*.lid', '*.hdp'],
161 'text/x-echdr': ['*.ec', '*.eh'],
162 'text/x-elixir': ['*.ex', '*.exs'],
163 'text/x-erl-shellsession': ['*.erl-sh'],
164 'text/x-erlang': ['*.erl', '*.hrl', '*.es', '*.escript'],
165 'text/x-factor': ['*.factor'],
166 'text/x-fancysrc': ['*.fy', '*.fancypack'],
167 'text/x-felix': ['*.flx', '*.flxh'],
168 'text/x-fortran': ['*.f', '*.f90', '*.F', '*.F90'],
169 'text/x-fsharp': ['*.fs', '*.fsi'],
170 'text/x-gas': ['*.s', '*.S'],
171 'text/x-gherkin': ['*.feature'],
172 'text/x-glslsrc': ['*.vert', '*.frag', '*.geo'],
173 'text/x-gnuplot': ['*.plot', '*.plt'],
174 'text/x-gooddata-cl': ['*.gdc'],
175 'text/x-gooddata-maql': ['*.maql'],
176 'text/x-gosrc': ['*.go'],
177 'text/x-gosu': ['*.gs', '*.gsx', '*.gsp', '*.vark'],
178 'text/x-gosu-template': ['*.gst'],
179 'text/x-groovy': ['*.groovy'],
180 'text/x-haml': ['*.haml'],
181 'text/x-haskell': ['*.hs'],
182 'text/x-hybris': ['*.hy', '*.hyb'],
183 'text/x-ini': ['*.ini', '*.cfg'],
184 'text/x-iokesrc': ['*.ik'],
185 'text/x-iosrc': ['*.io'],
186 'text/x-irclog': ['*.weechatlog'],
187 'text/x-jade': ['*.jade'],
188 'text/x-java': ['*.java'],
189 'text/x-java-properties': ['*.properties'],
190 'text/x-julia': ['*.jl'],
191 'text/x-kconfig': ['Kconfig', '*Config.in*', 'external.in*', 'standard-modules.in'],
192 'text/x-koka': ['*.kk', '*.kki'],
193 'text/x-kotlin': ['*.kt'],
194 'text/x-lasso': ['*.lasso', '*.lasso[89]'],
195 'text/x-literate-haskell': ['*.lhs'],
196 'text/x-llvm': ['*.ll'],
197 'text/x-logos': ['*.x', '*.xi', '*.xm', '*.xmi'],
198 'text/x-logtalk': ['*.lgt'],
199 'text/x-lua': ['*.lua', '*.wlua'],
200 'text/x-makefile': ['*.mak', 'Makefile', 'makefile', 'Makefile.*', 'GNUmakefile'],
201 'text/x-minidsrc': ['*.md'],
202 'text/x-markdown': ['*.md'],
203 'text/x-modelica': ['*.mo'],
204 'text/x-modula2': ['*.def', '*.mod'],
205 'text/x-monkey': ['*.monkey'],
206 'text/x-moocode': ['*.moo'],
207 'text/x-moonscript': ['*.moon'],
208 'text/x-nasm': ['*.asm', '*.ASM'],
209 'text/x-nemerle': ['*.n'],
210 'text/x-newlisp': ['*.lsp', '*.nl'],
211 'text/x-newspeak': ['*.ns2'],
212 'text/x-nimrod': ['*.nim', '*.nimrod'],
213 'text/x-nsis': ['*.nsi', '*.nsh'],
214 'text/x-objdump': ['*.objdump'],
215 'text/x-objective-c': ['*.m', '*.h'],
216 'text/x-objective-c++': ['*.mm', '*.hh'],
217 'text/x-objective-j': ['*.j'],
218 'text/x-ocaml': ['*.ml', '*.mli', '*.mll', '*.mly'],
219 'text/x-ooc': ['*.ooc'],
220 'text/x-opa': ['*.opa'],
221 'text/x-openedge': ['*.p', '*.cls'],
222 'text/x-pascal': ['*.pas'],
223 'text/x-perl': ['*.pl', '*.pm'],
224 'text/x-php': ['*.php', '*.php[345]', '*.inc'],
225 'text/x-povray': ['*.pov', '*.inc'],
226 'text/x-powershell': ['*.ps1'],
227 'text/x-prolog': ['*.prolog', '*.pro', '*.pl'],
228 'text/x-python': ['*.py', '*.pyw', '*.sc', 'SConstruct', 'SConscript', '*.tac', '*.sage'],
229 'text/x-python-traceback': ['*.pytb'],
230 'text/x-python3-traceback': ['*.py3tb'],
231 'text/x-r-doc': ['*.Rd'],
232 'text/x-racket': ['*.rkt', '*.rktl'],
233 'text/x-rebol': ['*.r', '*.r3'],
234 'text/x-robotframework': ['*.txt', '*.robot'],
235 'text/x-rpm-spec': ['*.spec'],
236 'text/x-rst': ['*.rst', '*.rest'],
237 'text/x-ruby': ['*.rb', '*.rbw', 'Rakefile', '*.rake', '*.gemspec', '*.rbx', '*.duby'],
238 'text/x-rustsrc': ['*.rs', '*.rc'],
239 'text/x-sass': ['*.sass'],
240 'text/x-scala': ['*.scala'],
241 'text/x-scaml': ['*.scaml'],
242 'text/x-scheme': ['*.scm', '*.ss'],
243 'text/x-scss': ['*.scss'],
244 'text/x-smalltalk': ['*.st'],
245 'text/x-snobol': ['*.snobol'],
246 'text/x-sourcepawn': ['*.sp'],
247 'text/x-sql': ['*.sql'],
248 'text/x-sqlite3-console': ['*.sqlite3-console'],
249 'text/x-squidconf': ['squid.conf'],
250 'text/x-standardml': ['*.sml', '*.sig', '*.fun'],
251 'text/x-systemverilog': ['*.sv', '*.svh'],
252 'text/x-tcl': ['*.tcl'],
253 'text/x-tea': ['*.tea'],
254 'text/x-tex': ['*.tex', '*.aux', '*.toc'],
255 'text/x-typescript': ['*.ts'],
256 'text/x-vala': ['*.vala', '*.vapi'],
257 'text/x-vbnet': ['*.vb', '*.bas'],
258 'text/x-verilog': ['*.v'],
259 'text/x-vhdl': ['*.vhdl', '*.vhd'],
260 'text/x-vim': ['*.vim', '.vimrc', '.exrc', '.gvimrc', '_vimrc', '_exrc', '_gvimrc', 'vimrc', 'gvimrc'],
261 'text/x-windows-registry': ['*.reg'],
262 'text/x-xtend': ['*.xtend'],
263 'text/x-yaml': ['*.yaml', '*.yml'],
264 'text/xml': ['*.xml', '*.xsl', '*.rss', '*.xslt', '*.xsd', '*.wsdl'],
265 'text/xquery': ['*.xqy', '*.xquery', '*.xq', '*.xql', '*.xqm'],
266 'text/apl': [],
267 'text/x-asterisk': [],
268 'text/x-csrc': [],
269 'text/x-c++src': ['*.cpp'],
270 'text/x-coffeescript': ['*.coffee'],
271 'text/x-d': ["*.d"],
272 'text/x-ecl': ['*.ecl'],
273 'text/x-go': ['*.go'],
274 'text/x-haxe': ['*.hx'],
275 'application/x-aspx': ['*.aspx'],
276 'application/x-ejs': [],
277 'message/http': [],
278 'application/x-json': ['*.json'],
279 'application/typescript': ['*.ts'],
280 'jinja2': ['.jinja2'],
281 'text/x-less': ['*.less'],
282 'text/x-livescript': ['*.ls'],
283 'text/mirc': [],
284 'text/n-triples': [],
285 'application/x-httpd-php': ['*.php'],
286 'text/x-pig': [],
287 'text/x-properties': ['*.properties'],
288 'text/x-rsrc': [],
289 'text/x-sh': ['*.sh', '*.ksh', '*.bash', '*.ebuild', '*.eclass', '.bashrc', 'bashrc', '.bash_*', 'bash_*'],
290 'application/sieve': [],
291 'text/x-stsrc': ['*.rs', '*.rc'],
292 'text/x-smarty': ['*.tpl'],
293 'application/x-sparql-query': [],
294 'text/x-mariadb': ['*.sql'],
295 'text/x-stex': [],
296 'text/x-latex': ["*.ltx"],
297 'text/x-tiddlywiki': [],
298 'text/tiki': [],
299 'text/x-vb': ['*.vb'],
300 'text/vbscript': ['*.vb'],
301 'text/velocity': [],
302 'application/xml': ['*.xml', '*.xsl', '*.rss', '*.xslt', '*.xsd', '*.wsdl'],
303 'application/xquery': ['*.xqy', '*.xquery', '*.xq', '*.xql', '*.xqm'],
304 'text/x-z80': [],
305 }
@@ -1,51 +1,51 b''
1 <!doctype html>
1 <!doctype html>
2 <html>
2 <html>
3 <head>
3 <head>
4 <meta charset="utf-8">
4 <meta charset="utf-8">
5 <title>CodeMirror: PHP mode</title>
5 <title>CodeMirror: PHP mode</title>
6 <link rel="stylesheet" href="../../lib/codemirror.css">
6 <link rel="stylesheet" href="../../lib/codemirror.css">
7 <script src="../../lib/codemirror.js"></script>
7 <script src="../../lib/codemirror.js"></script>
8 <script src="../../addon/edit/matchbrackets.js"></script>
8 <script src="../../addon/edit/matchbrackets.js"></script>
9 <script src="../htmlmixed/htmlmixed.js"></script>
9 <script src="../htmlmixed/htmlmixed.js"></script>
10 <script src="../xml/xml.js"></script>
10 <script src="../xml/xml.js"></script>
11 <script src="../javascript/javascript.js"></script>
11 <script src="../javascript/javascript.js"></script>
12 <script src="../css/css.js"></script>
12 <script src="../css/css.js"></script>
13 <script src="../clike/clike.js"></script>
13 <script src="../clike/clike.js"></script>
14 <script src="php.js"></script>
14 <script src="php.js"></script>
15 <style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
15 <style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
16 <link rel="stylesheet" href="../../doc/docs.css">
16 <link rel="stylesheet" href="../../doc/docs.css">
17 </head>
17 </head>
18 <body>
18 <body>
19 <h1>CodeMirror: PHP mode</h1>
19 <h1>CodeMirror: PHP mode</h1>
20
20
21 <form><textarea id="code" name="code">
21 <form><textarea id="code" name="code">
22 <?php
22 <?php
23 function hello($who) {
23 function hello($who) {
24 return "Hello " . $who;
24 return "Hello " . $who;
25 }
25 }
26 ?>
26 ?>
27 <p>The program says <?= hello("World") ?>.</p>
27 <p>The program says <?= hello("World") ?>.</p>
28 <script>
28 <script>
29 alert("And here is some JS code"); // also colored
29 alert("And here is some JS code"); // also colored
30 </script>
30 </script>
31 </textarea></form>
31 </textarea></form>
32
32
33 <script>
33 <script>
34 var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
34 var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
35 lineNumbers: true,
35 lineNumbers: true,
36 matchBrackets: true,
36 matchBrackets: true,
37 mode: "application/x-httpd-php",
37 mode: "application/x-httpd-php",
38 indentUnit: 4,
38 indentUnit: 4,
39 indentWithTabs: true,
39 indentWithTabs: true,
40 enterMode: "keep",
40 enterMode: "keep",
41 tabMode: "shift"
41 tabMode: "shift"
42 });
42 });
43 </script>
43 </script>
44
44
45 <p>Simple HTML/PHP mode based on
45 <p>Simple HTML/PHP mode based on
46 the <a href="../clike">C-like</a> mode. Depends on XML,
46 the <a href="../clike/">C-like</a> mode. Depends on XML,
47 JavaScript, CSS, HTMLMixed, and C-like modes.</p>
47 JavaScript, CSS, HTMLMixed, and C-like modes.</p>
48
48
49 <p><strong>MIME types defined:</strong> <code>application/x-httpd-php</code> (HTML with PHP code), <code>text/x-php</code> (plain, non-wrapped PHP code).</p>
49 <p><strong>MIME types defined:</strong> <code>application/x-httpd-php</code> (HTML with PHP code), <code>text/x-php</code> (plain, non-wrapped PHP code).</p>
50 </body>
50 </body>
51 </html>
51 </html>
General Comments 0
You need to be logged in to leave comments. Login now