WP-Paginate - Version 1.3.3

Version Description

  • Added settings tab for entering custom CSS code
Download this release

Release Info

Developer AlanP57
Plugin Icon 128x128 WP-Paginate
Version 1.3.3
Comparing to
See all releases

Code changes from version 1.3.2 to 1.3.3

Files changed (4) hide show
  1. lib/codemirror.css +341 -0
  2. lib/codemirror.js +9112 -0
  3. readme.txt +8 -2
  4. wp-paginate.php +194 -5
lib/codemirror.css ADDED
@@ -0,0 +1,341 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* BASICS */
2
+
3
+ .CodeMirror {
4
+ /* Set height, width, borders, and global font properties here */
5
+ font-family: monospace;
6
+ height: 300px;
7
+ color: black;
8
+ }
9
+
10
+ /* PADDING */
11
+
12
+ .CodeMirror-lines {
13
+ padding: 4px 0; /* Vertical padding around content */
14
+ }
15
+ .CodeMirror pre {
16
+ padding: 0 4px; /* Horizontal padding of content */
17
+ }
18
+
19
+ .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
20
+ background-color: white; /* The little square between H and V scrollbars */
21
+ }
22
+
23
+ /* GUTTER */
24
+
25
+ .CodeMirror-gutters {
26
+ border-right: 1px solid #ddd;
27
+ background-color: #f7f7f7;
28
+ white-space: nowrap;
29
+ }
30
+ .CodeMirror-linenumbers {}
31
+ .CodeMirror-linenumber {
32
+ padding: 0 3px 0 5px;
33
+ min-width: 20px;
34
+ text-align: right;
35
+ color: #999;
36
+ white-space: nowrap;
37
+ }
38
+
39
+ .CodeMirror-guttermarker { color: black; }
40
+ .CodeMirror-guttermarker-subtle { color: #999; }
41
+
42
+ /* CURSOR */
43
+
44
+ .CodeMirror-cursor {
45
+ border-left: 1px solid black;
46
+ border-right: none;
47
+ width: 0;
48
+ }
49
+ /* Shown when moving in bi-directional text */
50
+ .CodeMirror div.CodeMirror-secondarycursor {
51
+ border-left: 1px solid silver;
52
+ }
53
+ .cm-fat-cursor .CodeMirror-cursor {
54
+ width: auto;
55
+ border: 0 !important;
56
+ background: #7e7;
57
+ }
58
+ .cm-fat-cursor div.CodeMirror-cursors {
59
+ z-index: 1;
60
+ }
61
+
62
+ .cm-animate-fat-cursor {
63
+ width: auto;
64
+ border: 0;
65
+ -webkit-animation: blink 1.06s steps(1) infinite;
66
+ -moz-animation: blink 1.06s steps(1) infinite;
67
+ animation: blink 1.06s steps(1) infinite;
68
+ background-color: #7e7;
69
+ }
70
+ @-moz-keyframes blink {
71
+ 0% {}
72
+ 50% { background-color: transparent; }
73
+ 100% {}
74
+ }
75
+ @-webkit-keyframes blink {
76
+ 0% {}
77
+ 50% { background-color: transparent; }
78
+ 100% {}
79
+ }
80
+ @keyframes blink {
81
+ 0% {}
82
+ 50% { background-color: transparent; }
83
+ 100% {}
84
+ }
85
+
86
+ /* Can style cursor different in overwrite (non-insert) mode */
87
+ .CodeMirror-overwrite .CodeMirror-cursor {}
88
+
89
+ .cm-tab { display: inline-block; text-decoration: inherit; }
90
+
91
+ .CodeMirror-rulers {
92
+ position: absolute;
93
+ left: 0; right: 0; top: -50px; bottom: -20px;
94
+ overflow: hidden;
95
+ }
96
+ .CodeMirror-ruler {
97
+ border-left: 1px solid #ccc;
98
+ top: 0; bottom: 0;
99
+ position: absolute;
100
+ }
101
+
102
+ /* DEFAULT THEME */
103
+
104
+ .cm-s-default .cm-header {color: blue;}
105
+ .cm-s-default .cm-quote {color: #090;}
106
+ .cm-negative {color: #d44;}
107
+ .cm-positive {color: #292;}
108
+ .cm-header, .cm-strong {font-weight: bold;}
109
+ .cm-em {font-style: italic;}
110
+ .cm-link {text-decoration: underline;}
111
+ .cm-strikethrough {text-decoration: line-through;}
112
+
113
+ .cm-s-default .cm-keyword {color: #708;}
114
+ .cm-s-default .cm-atom {color: #219;}
115
+ .cm-s-default .cm-number {color: #164;}
116
+ .cm-s-default .cm-def {color: #00f;}
117
+ .cm-s-default .cm-variable,
118
+ .cm-s-default .cm-punctuation,
119
+ .cm-s-default .cm-property,
120
+ .cm-s-default .cm-operator {}
121
+ .cm-s-default .cm-variable-2 {color: #05a;}
122
+ .cm-s-default .cm-variable-3 {color: #085;}
123
+ .cm-s-default .cm-comment {color: #a50;}
124
+ .cm-s-default .cm-string {color: #a11;}
125
+ .cm-s-default .cm-string-2 {color: #f50;}
126
+ .cm-s-default .cm-meta {color: #555;}
127
+ .cm-s-default .cm-qualifier {color: #555;}
128
+ .cm-s-default .cm-builtin {color: #30a;}
129
+ .cm-s-default .cm-bracket {color: #997;}
130
+ .cm-s-default .cm-tag {color: #170;}
131
+ .cm-s-default .cm-attribute {color: #00c;}
132
+ .cm-s-default .cm-hr {color: #999;}
133
+ .cm-s-default .cm-link {color: #00c;}
134
+
135
+ .cm-s-default .cm-error {color: #f00;}
136
+ .cm-invalidchar {color: #f00;}
137
+
138
+ .CodeMirror-composing { border-bottom: 2px solid; }
139
+
140
+ /* Default styles for common addons */
141
+
142
+ div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
143
+ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
144
+ .CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); }
145
+ .CodeMirror-activeline-background {background: #e8f2ff;}
146
+
147
+ /* STOP */
148
+
149
+ /* The rest of this file contains styles related to the mechanics of
150
+ the editor. You probably shouldn't touch them. */
151
+
152
+ .CodeMirror {
153
+ position: relative;
154
+ overflow: hidden;
155
+ background: white;
156
+ }
157
+
158
+ .CodeMirror-scroll {
159
+ overflow: scroll !important; /* Things will break if this is overridden */
160
+ /* 30px is the magic margin used to hide the element's real scrollbars */
161
+ /* See overflow: hidden in .CodeMirror */
162
+ margin-bottom: -30px; margin-right: -30px;
163
+ padding-bottom: 30px;
164
+ height: 100%;
165
+ outline: none; /* Prevent dragging from highlighting the element */
166
+ position: relative;
167
+ }
168
+ .CodeMirror-sizer {
169
+ position: relative;
170
+ border-right: 30px solid transparent;
171
+ }
172
+
173
+ /* The fake, visible scrollbars. Used to force redraw during scrolling
174
+ before actual scrolling happens, thus preventing shaking and
175
+ flickering artifacts. */
176
+ .CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
177
+ position: absolute;
178
+ z-index: 6;
179
+ display: none;
180
+ }
181
+ .CodeMirror-vscrollbar {
182
+ right: 0; top: 0;
183
+ overflow-x: hidden;
184
+ overflow-y: scroll;
185
+ }
186
+ .CodeMirror-hscrollbar {
187
+ bottom: 0; left: 0;
188
+ overflow-y: hidden;
189
+ overflow-x: scroll;
190
+ }
191
+ .CodeMirror-scrollbar-filler {
192
+ right: 0; bottom: 0;
193
+ }
194
+ .CodeMirror-gutter-filler {
195
+ left: 0; bottom: 0;
196
+ }
197
+
198
+ .CodeMirror-gutters {
199
+ position: absolute; left: 0; top: 0;
200
+ min-height: 100%;
201
+ z-index: 3;
202
+ }
203
+ .CodeMirror-gutter {
204
+ white-space: normal;
205
+ height: 100%;
206
+ display: inline-block;
207
+ vertical-align: top;
208
+ margin-bottom: -30px;
209
+ }
210
+ .CodeMirror-gutter-wrapper {
211
+ position: absolute;
212
+ z-index: 4;
213
+ background: none !important;
214
+ border: none !important;
215
+ }
216
+ .CodeMirror-gutter-background {
217
+ position: absolute;
218
+ top: 0; bottom: 0;
219
+ z-index: 4;
220
+ }
221
+ .CodeMirror-gutter-elt {
222
+ position: absolute;
223
+ cursor: default;
224
+ z-index: 4;
225
+ }
226
+ .CodeMirror-gutter-wrapper {
227
+ -webkit-user-select: none;
228
+ -moz-user-select: none;
229
+ user-select: none;
230
+ }
231
+
232
+ .CodeMirror-lines {
233
+ cursor: text;
234
+ min-height: 1px; /* prevents collapsing before first draw */
235
+ }
236
+ .CodeMirror pre {
237
+ /* Reset some styles that the rest of the page might have set */
238
+ -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0;
239
+ border-width: 0;
240
+ background: transparent;
241
+ font-family: inherit;
242
+ font-size: inherit;
243
+ margin: 0;
244
+ white-space: pre;
245
+ word-wrap: normal;
246
+ line-height: inherit;
247
+ color: inherit;
248
+ z-index: 2;
249
+ position: relative;
250
+ overflow: visible;
251
+ -webkit-tap-highlight-color: transparent;
252
+ -webkit-font-variant-ligatures: contextual;
253
+ font-variant-ligatures: contextual;
254
+ }
255
+ .CodeMirror-wrap pre {
256
+ word-wrap: break-word;
257
+ white-space: pre-wrap;
258
+ word-break: normal;
259
+ }
260
+
261
+ .CodeMirror-linebackground {
262
+ position: absolute;
263
+ left: 0; right: 0; top: 0; bottom: 0;
264
+ z-index: 0;
265
+ }
266
+
267
+ .CodeMirror-linewidget {
268
+ position: relative;
269
+ z-index: 2;
270
+ overflow: auto;
271
+ }
272
+
273
+ .CodeMirror-widget {}
274
+
275
+ .CodeMirror-code {
276
+ outline: none;
277
+ }
278
+
279
+ /* Force content-box sizing for the elements where we expect it */
280
+ .CodeMirror-scroll,
281
+ .CodeMirror-sizer,
282
+ .CodeMirror-gutter,
283
+ .CodeMirror-gutters,
284
+ .CodeMirror-linenumber {
285
+ -moz-box-sizing: content-box;
286
+ box-sizing: content-box;
287
+ }
288
+
289
+ .CodeMirror-measure {
290
+ position: absolute;
291
+ width: 100%;
292
+ height: 0;
293
+ overflow: hidden;
294
+ visibility: hidden;
295
+ }
296
+
297
+ .CodeMirror-cursor {
298
+ position: absolute;
299
+ pointer-events: none;
300
+ }
301
+ .CodeMirror-measure pre { position: static; }
302
+
303
+ div.CodeMirror-cursors {
304
+ visibility: hidden;
305
+ position: relative;
306
+ z-index: 3;
307
+ }
308
+ div.CodeMirror-dragcursors {
309
+ visibility: visible;
310
+ }
311
+
312
+ .CodeMirror-focused div.CodeMirror-cursors {
313
+ visibility: visible;
314
+ }
315
+
316
+ .CodeMirror-selected { background: #d9d9d9; }
317
+ .CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
318
+ .CodeMirror-crosshair { cursor: crosshair; }
319
+ .CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; }
320
+ .CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; }
321
+
322
+ .cm-searching {
323
+ background: #ffa;
324
+ background: rgba(255, 255, 0, .4);
325
+ }
326
+
327
+ /* Used to force a border model for a node */
328
+ .cm-force-border { padding-right: .1px; }
329
+
330
+ @media print {
331
+ /* Hide the cursor when printing */
332
+ .CodeMirror div.CodeMirror-cursors {
333
+ visibility: hidden;
334
+ }
335
+ }
336
+
337
+ /* See issue #2901 */
338
+ .cm-tab-wrap-hack:after { content: ''; }
339
+
340
+ /* Help users use markselection to safely style text background */
341
+ span.CodeMirror-selectedtext { background: none; }
lib/codemirror.js ADDED
@@ -0,0 +1,9112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // CodeMirror, copyright (c) by Marijn Haverbeke and others
2
+ // Distributed under an MIT license: http://codemirror.net/LICENSE
3
+
4
+ // This is CodeMirror (http://codemirror.net), a code editor
5
+ // implemented in JavaScript on top of the browser's DOM.
6
+ //
7
+ // You can find some technical background for some of the code below
8
+ // at http://marijnhaverbeke.nl/blog/#cm-internals .
9
+
10
+ (function (global, factory) {
11
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
12
+ typeof define === 'function' && define.amd ? define(factory) :
13
+ (global.CodeMirror = factory());
14
+ }(this, (function () { 'use strict';
15
+
16
+ // Kludges for bugs and behavior differences that can't be feature
17
+ // detected are enabled based on userAgent etc sniffing.
18
+ var userAgent = navigator.userAgent
19
+ var platform = navigator.platform
20
+
21
+ var gecko = /gecko\/\d/i.test(userAgent)
22
+ var ie_upto10 = /MSIE \d/.test(userAgent)
23
+ var ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(userAgent)
24
+ var ie = ie_upto10 || ie_11up
25
+ var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : ie_11up[1])
26
+ var webkit = /WebKit\//.test(userAgent)
27
+ var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(userAgent)
28
+ var chrome = /Chrome\//.test(userAgent)
29
+ var presto = /Opera\//.test(userAgent)
30
+ var safari = /Apple Computer/.test(navigator.vendor)
31
+ var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(userAgent)
32
+ var phantom = /PhantomJS/.test(userAgent)
33
+
34
+ var ios = /AppleWebKit/.test(userAgent) && /Mobile\/\w+/.test(userAgent)
35
+ // This is woefully incomplete. Suggestions for alternative methods welcome.
36
+ var mobile = ios || /Android|webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(userAgent)
37
+ var mac = ios || /Mac/.test(platform)
38
+ var chromeOS = /\bCrOS\b/.test(userAgent)
39
+ var windows = /win/i.test(platform)
40
+
41
+ var presto_version = presto && userAgent.match(/Version\/(\d*\.\d*)/)
42
+ if (presto_version) { presto_version = Number(presto_version[1]) }
43
+ if (presto_version && presto_version >= 15) { presto = false; webkit = true }
44
+ // Some browsers use the wrong event properties to signal cmd/ctrl on OS X
45
+ var flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11))
46
+ var captureRightClick = gecko || (ie && ie_version >= 9)
47
+
48
+ function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*") }
49
+
50
+ var rmClass = function(node, cls) {
51
+ var current = node.className
52
+ var match = classTest(cls).exec(current)
53
+ if (match) {
54
+ var after = current.slice(match.index + match[0].length)
55
+ node.className = current.slice(0, match.index) + (after ? match[1] + after : "")
56
+ }
57
+ }
58
+
59
+ function removeChildren(e) {
60
+ for (var count = e.childNodes.length; count > 0; --count)
61
+ { e.removeChild(e.firstChild) }
62
+ return e
63
+ }
64
+
65
+ function removeChildrenAndAdd(parent, e) {
66
+ return removeChildren(parent).appendChild(e)
67
+ }
68
+
69
+ function elt(tag, content, className, style) {
70
+ var e = document.createElement(tag)
71
+ if (className) { e.className = className }
72
+ if (style) { e.style.cssText = style }
73
+ if (typeof content == "string") { e.appendChild(document.createTextNode(content)) }
74
+ else if (content) { for (var i = 0; i < content.length; ++i) { e.appendChild(content[i]) } }
75
+ return e
76
+ }
77
+
78
+ var range
79
+ if (document.createRange) { range = function(node, start, end, endNode) {
80
+ var r = document.createRange()
81
+ r.setEnd(endNode || node, end)
82
+ r.setStart(node, start)
83
+ return r
84
+ } }
85
+ else { range = function(node, start, end) {
86
+ var r = document.body.createTextRange()
87
+ try { r.moveToElementText(node.parentNode) }
88
+ catch(e) { return r }
89
+ r.collapse(true)
90
+ r.moveEnd("character", end)
91
+ r.moveStart("character", start)
92
+ return r
93
+ } }
94
+
95
+ function contains(parent, child) {
96
+ if (child.nodeType == 3) // Android browser always returns false when child is a textnode
97
+ { child = child.parentNode }
98
+ if (parent.contains)
99
+ { return parent.contains(child) }
100
+ do {
101
+ if (child.nodeType == 11) { child = child.host }
102
+ if (child == parent) { return true }
103
+ } while (child = child.parentNode)
104
+ }
105
+
106
+ function activeElt() {
107
+ // IE and Edge may throw an "Unspecified Error" when accessing document.activeElement.
108
+ // IE < 10 will throw when accessed while the page is loading or in an iframe.
109
+ // IE > 9 and Edge will throw when accessed in an iframe if document.body is unavailable.
110
+ var activeElement
111
+ try {
112
+ activeElement = document.activeElement
113
+ } catch(e) {
114
+ activeElement = document.body || null
115
+ }
116
+ while (activeElement && activeElement.root && activeElement.root.activeElement)
117
+ { activeElement = activeElement.root.activeElement }
118
+ return activeElement
119
+ }
120
+
121
+ function addClass(node, cls) {
122
+ var current = node.className
123
+ if (!classTest(cls).test(current)) { node.className += (current ? " " : "") + cls }
124
+ }
125
+ function joinClasses(a, b) {
126
+ var as = a.split(" ")
127
+ for (var i = 0; i < as.length; i++)
128
+ { if (as[i] && !classTest(as[i]).test(b)) { b += " " + as[i] } }
129
+ return b
130
+ }
131
+
132
+ var selectInput = function(node) { node.select() }
133
+ if (ios) // Mobile Safari apparently has a bug where select() is broken.
134
+ { selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length } }
135
+ else if (ie) // Suppress mysterious IE10 errors
136
+ { selectInput = function(node) { try { node.select() } catch(_e) {} } }
137
+
138
+ function bind(f) {
139
+ var args = Array.prototype.slice.call(arguments, 1)
140
+ return function(){return f.apply(null, args)}
141
+ }
142
+
143
+ function copyObj(obj, target, overwrite) {
144
+ if (!target) { target = {} }
145
+ for (var prop in obj)
146
+ { if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop)))
147
+ { target[prop] = obj[prop] } }
148
+ return target
149
+ }
150
+
151
+ // Counts the column offset in a string, taking tabs into account.
152
+ // Used mostly to find indentation.
153
+ function countColumn(string, end, tabSize, startIndex, startValue) {
154
+ if (end == null) {
155
+ end = string.search(/[^\s\u00a0]/)
156
+ if (end == -1) { end = string.length }
157
+ }
158
+ for (var i = startIndex || 0, n = startValue || 0;;) {
159
+ var nextTab = string.indexOf("\t", i)
160
+ if (nextTab < 0 || nextTab >= end)
161
+ { return n + (end - i) }
162
+ n += nextTab - i
163
+ n += tabSize - (n % tabSize)
164
+ i = nextTab + 1
165
+ }
166
+ }
167
+
168
+ function Delayed() {this.id = null}
169
+ Delayed.prototype.set = function(ms, f) {
170
+ clearTimeout(this.id)
171
+ this.id = setTimeout(f, ms)
172
+ }
173
+
174
+ function indexOf(array, elt) {
175
+ for (var i = 0; i < array.length; ++i)
176
+ { if (array[i] == elt) { return i } }
177
+ return -1
178
+ }
179
+
180
+ // Number of pixels added to scroller and sizer to hide scrollbar
181
+ var scrollerGap = 30
182
+
183
+ // Returned or thrown by various protocols to signal 'I'm not
184
+ // handling this'.
185
+ var Pass = {toString: function(){return "CodeMirror.Pass"}}
186
+
187
+ // Reused option objects for setSelection & friends
188
+ var sel_dontScroll = {scroll: false};
189
+ var sel_mouse = {origin: "*mouse"};
190
+ var sel_move = {origin: "+move"}
191
+
192
+ // The inverse of countColumn -- find the offset that corresponds to
193
+ // a particular column.
194
+ function findColumn(string, goal, tabSize) {
195
+ for (var pos = 0, col = 0;;) {
196
+ var nextTab = string.indexOf("\t", pos)
197
+ if (nextTab == -1) { nextTab = string.length }
198
+ var skipped = nextTab - pos
199
+ if (nextTab == string.length || col + skipped >= goal)
200
+ { return pos + Math.min(skipped, goal - col) }
201
+ col += nextTab - pos
202
+ col += tabSize - (col % tabSize)
203
+ pos = nextTab + 1
204
+ if (col >= goal) { return pos }
205
+ }
206
+ }
207
+
208
+ var spaceStrs = [""]
209
+ function spaceStr(n) {
210
+ while (spaceStrs.length <= n)
211
+ { spaceStrs.push(lst(spaceStrs) + " ") }
212
+ return spaceStrs[n]
213
+ }
214
+
215
+ function lst(arr) { return arr[arr.length-1] }
216
+
217
+ function map(array, f) {
218
+ var out = []
219
+ for (var i = 0; i < array.length; i++) { out[i] = f(array[i], i) }
220
+ return out
221
+ }
222
+
223
+ function insertSorted(array, value, score) {
224
+ var pos = 0, priority = score(value)
225
+ while (pos < array.length && score(array[pos]) <= priority) { pos++ }
226
+ array.splice(pos, 0, value)
227
+ }
228
+
229
+ function nothing() {}
230
+
231
+ function createObj(base, props) {
232
+ var inst
233
+ if (Object.create) {
234
+ inst = Object.create(base)
235
+ } else {
236
+ nothing.prototype = base
237
+ inst = new nothing()
238
+ }
239
+ if (props) { copyObj(props, inst) }
240
+ return inst
241
+ }
242
+
243
+ var nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/
244
+ function isWordCharBasic(ch) {
245
+ return /\w/.test(ch) || ch > "\x80" &&
246
+ (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch))
247
+ }
248
+ function isWordChar(ch, helper) {
249
+ if (!helper) { return isWordCharBasic(ch) }
250
+ if (helper.source.indexOf("\\w") > -1 && isWordCharBasic(ch)) { return true }
251
+ return helper.test(ch)
252
+ }
253
+
254
+ function isEmpty(obj) {
255
+ for (var n in obj) { if (obj.hasOwnProperty(n) && obj[n]) { return false } }
256
+ return true
257
+ }
258
+
259
+ // Extending unicode characters. A series of a non-extending char +
260
+ // any number of extending chars is treated as a single unit as far
261
+ // as editing and measuring is concerned. This is not fully correct,
262
+ // since some scripts/fonts/browsers also treat other configurations
263
+ // of code points as a group.
264
+ var extendingChars = /[\u0300-\u036f\u0483-\u0489\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u065e\u0670\u06d6-\u06dc\u06de-\u06e4\u06e7\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0900-\u0902\u093c\u0941-\u0948\u094d\u0951-\u0955\u0962\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2\u09e3\u0a01\u0a02\u0a3c\u0a41\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a70\u0a71\u0a75\u0a81\u0a82\u0abc\u0ac1-\u0ac5\u0ac7\u0ac8\u0acd\u0ae2\u0ae3\u0b01\u0b3c\u0b3e\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b82\u0bbe\u0bc0\u0bcd\u0bd7\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0cbc\u0cbf\u0cc2\u0cc6\u0ccc\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0d3e\u0d41-\u0d44\u0d4d\u0d57\u0d62\u0d63\u0dca\u0dcf\u0dd2-\u0dd4\u0dd6\u0ddf\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86\u0f87\u0f90-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039\u103a\u103d\u103e\u1058\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085\u1086\u108d\u109d\u135f\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193b\u1a17\u1a18\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80\u1b81\u1ba2-\u1ba5\u1ba8\u1ba9\u1c2c-\u1c33\u1c36\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1dc0-\u1de6\u1dfd-\u1dff\u200c\u200d\u20d0-\u20f0\u2cef-\u2cf1\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua66f-\ua672\ua67c\ua67d\ua6f0\ua6f1\ua802\ua806\ua80b\ua825\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\uaa29-\uaa2e\uaa31\uaa32\uaa35\uaa36\uaa43\uaa4c\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uabe5\uabe8\uabed\udc00-\udfff\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\uff9e\uff9f]/
265
+ function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch) }
266
+
267
+ // The display handles the DOM integration, both for input reading
268
+ // and content drawing. It holds references to DOM nodes and
269
+ // display-related state.
270
+
271
+ function Display(place, doc, input) {
272
+ var d = this
273
+ this.input = input
274
+
275
+ // Covers bottom-right square when both scrollbars are present.
276
+ d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler")
277
+ d.scrollbarFiller.setAttribute("cm-not-content", "true")
278
+ // Covers bottom of gutter when coverGutterNextToScrollbar is on
279
+ // and h scrollbar is present.
280
+ d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler")
281
+ d.gutterFiller.setAttribute("cm-not-content", "true")
282
+ // Will contain the actual code, positioned to cover the viewport.
283
+ d.lineDiv = elt("div", null, "CodeMirror-code")
284
+ // Elements are added to these to represent selection and cursors.
285
+ d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1")
286
+ d.cursorDiv = elt("div", null, "CodeMirror-cursors")
287
+ // A visibility: hidden element used to find the size of things.
288
+ d.measure = elt("div", null, "CodeMirror-measure")
289
+ // When lines outside of the viewport are measured, they are drawn in this.
290
+ d.lineMeasure = elt("div", null, "CodeMirror-measure")
291
+ // Wraps everything that needs to exist inside the vertically-padded coordinate system
292
+ d.lineSpace = elt("div", [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv],
293
+ null, "position: relative; outline: none")
294
+ // Moved around its parent to cover visible view.
295
+ d.mover = elt("div", [elt("div", [d.lineSpace], "CodeMirror-lines")], null, "position: relative")
296
+ // Set to the height of the document, allowing scrolling.
297
+ d.sizer = elt("div", [d.mover], "CodeMirror-sizer")
298
+ d.sizerWidth = null
299
+ // Behavior of elts with overflow: auto and padding is
300
+ // inconsistent across browsers. This is used to ensure the
301
+ // scrollable area is big enough.
302
+ d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerGap + "px; width: 1px;")
303
+ // Will contain the gutters, if any.
304
+ d.gutters = elt("div", null, "CodeMirror-gutters")
305
+ d.lineGutter = null
306
+ // Actual scrollable element.
307
+ d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-scroll")
308
+ d.scroller.setAttribute("tabIndex", "-1")
309
+ // The element in which the editor lives.
310
+ d.wrapper = elt("div", [d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror")
311
+
312
+ // Work around IE7 z-index bug (not perfect, hence IE7 not really being supported)
313
+ if (ie && ie_version < 8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0 }
314
+ if (!webkit && !(gecko && mobile)) { d.scroller.draggable = true }
315
+
316
+ if (place) {
317
+ if (place.appendChild) { place.appendChild(d.wrapper) }
318
+ else { place(d.wrapper) }
319
+ }
320
+
321
+ // Current rendered range (may be bigger than the view window).
322
+ d.viewFrom = d.viewTo = doc.first
323
+ d.reportedViewFrom = d.reportedViewTo = doc.first
324
+ // Information about the rendered lines.
325
+ d.view = []
326
+ d.renderedView = null
327
+ // Holds info about a single rendered line when it was rendered
328
+ // for measurement, while not in view.
329
+ d.externalMeasured = null
330
+ // Empty space (in pixels) above the view
331
+ d.viewOffset = 0
332
+ d.lastWrapHeight = d.lastWrapWidth = 0
333
+ d.updateLineNumbers = null
334
+
335
+ d.nativeBarWidth = d.barHeight = d.barWidth = 0
336
+ d.scrollbarsClipped = false
337
+
338
+ // Used to only resize the line number gutter when necessary (when
339
+ // the amount of lines crosses a boundary that makes its width change)
340
+ d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null
341
+ // Set to true when a non-horizontal-scrolling line widget is
342
+ // added. As an optimization, line widget aligning is skipped when
343
+ // this is false.
344
+ d.alignWidgets = false
345
+
346
+ d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null
347
+
348
+ // Tracks the maximum line length so that the horizontal scrollbar
349
+ // can be kept static when scrolling.
350
+ d.maxLine = null
351
+ d.maxLineLength = 0
352
+ d.maxLineChanged = false
353
+
354
+ // Used for measuring wheel scrolling granularity
355
+ d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null
356
+
357
+ // True when shift is held down.
358
+ d.shift = false
359
+
360
+ // Used to track whether anything happened since the context menu
361
+ // was opened.
362
+ d.selForContextMenu = null
363
+
364
+ d.activeTouch = null
365
+
366
+ input.init(d)
367
+ }
368
+
369
+ // Find the line object corresponding to the given line number.
370
+ function getLine(doc, n) {
371
+ n -= doc.first
372
+ if (n < 0 || n >= doc.size) { throw new Error("There is no line " + (n + doc.first) + " in the document.") }
373
+ var chunk = doc
374
+ while (!chunk.lines) {
375
+ for (var i = 0;; ++i) {
376
+ var child = chunk.children[i], sz = child.chunkSize()
377
+ if (n < sz) { chunk = child; break }
378
+ n -= sz
379
+ }
380
+ }
381
+ return chunk.lines[n]
382
+ }
383
+
384
+ // Get the part of a document between two positions, as an array of
385
+ // strings.
386
+ function getBetween(doc, start, end) {
387
+ var out = [], n = start.line
388
+ doc.iter(start.line, end.line + 1, function (line) {
389
+ var text = line.text
390
+ if (n == end.line) { text = text.slice(0, end.ch) }
391
+ if (n == start.line) { text = text.slice(start.ch) }
392
+ out.push(text)
393
+ ++n
394
+ })
395
+ return out
396
+ }
397
+ // Get the lines between from and to, as array of strings.
398
+ function getLines(doc, from, to) {
399
+ var out = []
400
+ doc.iter(from, to, function (line) { out.push(line.text) }) // iter aborts when callback returns truthy value
401
+ return out
402
+ }
403
+
404
+ // Update the height of a line, propagating the height change
405
+ // upwards to parent nodes.
406
+ function updateLineHeight(line, height) {
407
+ var diff = height - line.height
408
+ if (diff) { for (var n = line; n; n = n.parent) { n.height += diff } }
409
+ }
410
+
411
+ // Given a line object, find its line number by walking up through
412
+ // its parent links.
413
+ function lineNo(line) {
414
+ if (line.parent == null) { return null }
415
+ var cur = line.parent, no = indexOf(cur.lines, line)
416
+ for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {
417
+ for (var i = 0;; ++i) {
418
+ if (chunk.children[i] == cur) { break }
419
+ no += chunk.children[i].chunkSize()
420
+ }
421
+ }
422
+ return no + cur.first
423
+ }
424
+
425
+ // Find the line at the given vertical position, using the height
426
+ // information in the document tree.
427
+ function lineAtHeight(chunk, h) {
428
+ var n = chunk.first
429
+ outer: do {
430
+ for (var i$1 = 0; i$1 < chunk.children.length; ++i$1) {
431
+ var child = chunk.children[i$1], ch = child.height
432
+ if (h < ch) { chunk = child; continue outer }
433
+ h -= ch
434
+ n += child.chunkSize()
435
+ }
436
+ return n
437
+ } while (!chunk.lines)
438
+ var i = 0
439
+ for (; i < chunk.lines.length; ++i) {
440
+ var line = chunk.lines[i], lh = line.height
441
+ if (h < lh) { break }
442
+ h -= lh
443
+ }
444
+ return n + i
445
+ }
446
+
447
+ function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size}
448
+
449
+ function lineNumberFor(options, i) {
450
+ return String(options.lineNumberFormatter(i + options.firstLineNumber))
451
+ }
452
+
453
+ // A Pos instance represents a position within the text.
454
+ function Pos (line, ch) {
455
+ if (!(this instanceof Pos)) { return new Pos(line, ch) }
456
+ this.line = line; this.ch = ch
457
+ }
458
+
459
+ // Compare two positions, return 0 if they are the same, a negative
460
+ // number when a is less, and a positive number otherwise.
461
+ function cmp(a, b) { return a.line - b.line || a.ch - b.ch }
462
+
463
+ function copyPos(x) {return Pos(x.line, x.ch)}
464
+ function maxPos(a, b) { return cmp(a, b) < 0 ? b : a }
465
+ function minPos(a, b) { return cmp(a, b) < 0 ? a : b }
466
+
467
+ // Most of the external API clips given positions to make sure they
468
+ // actually exist within the document.
469
+ function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1))}
470
+ function clipPos(doc, pos) {
471
+ if (pos.line < doc.first) { return Pos(doc.first, 0) }
472
+ var last = doc.first + doc.size - 1
473
+ if (pos.line > last) { return Pos(last, getLine(doc, last).text.length) }
474
+ return clipToLen(pos, getLine(doc, pos.line).text.length)
475
+ }
476
+ function clipToLen(pos, linelen) {
477
+ var ch = pos.ch
478
+ if (ch == null || ch > linelen) { return Pos(pos.line, linelen) }
479
+ else if (ch < 0) { return Pos(pos.line, 0) }
480
+ else { return pos }
481
+ }
482
+ function clipPosArray(doc, array) {
483
+ var out = []
484
+ for (var i = 0; i < array.length; i++) { out[i] = clipPos(doc, array[i]) }
485
+ return out
486
+ }
487
+
488
+ // Optimize some code when these features are not used.
489
+ var sawReadOnlySpans = false;
490
+ var sawCollapsedSpans = false
491
+
492
+ function seeReadOnlySpans() {
493
+ sawReadOnlySpans = true
494
+ }
495
+
496
+ function seeCollapsedSpans() {
497
+ sawCollapsedSpans = true
498
+ }
499
+
500
+ // TEXTMARKER SPANS
501
+
502
+ function MarkedSpan(marker, from, to) {
503
+ this.marker = marker
504
+ this.from = from; this.to = to
505
+ }
506
+
507
+ // Search an array of spans for a span matching the given marker.
508
+ function getMarkedSpanFor(spans, marker) {
509
+ if (spans) { for (var i = 0; i < spans.length; ++i) {
510
+ var span = spans[i]
511
+ if (span.marker == marker) { return span }
512
+ } }
513
+ }
514
+ // Remove a span from an array, returning undefined if no spans are
515
+ // left (we don't store arrays for lines without spans).
516
+ function removeMarkedSpan(spans, span) {
517
+ var r
518
+ for (var i = 0; i < spans.length; ++i)
519
+ { if (spans[i] != span) { (r || (r = [])).push(spans[i]) } }
520
+ return r
521
+ }
522
+ // Add a span to a line.
523
+ function addMarkedSpan(line, span) {
524
+ line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span]
525
+ span.marker.attachLine(line)
526
+ }
527
+
528
+ // Used for the algorithm that adjusts markers for a change in the
529
+ // document. These functions cut an array of spans at a given
530
+ // character position, returning an array of remaining chunks (or
531
+ // undefined if nothing remains).
532
+ function markedSpansBefore(old, startCh, isInsert) {
533
+ var nw
534
+ if (old) { for (var i = 0; i < old.length; ++i) {
535
+ var span = old[i], marker = span.marker
536
+ var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh)
537
+ if (startsBefore || span.from == startCh && marker.type == "bookmark" && (!isInsert || !span.marker.insertLeft)) {
538
+ var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh);(nw || (nw = [])).push(new MarkedSpan(marker, span.from, endsAfter ? null : span.to))
539
+ }
540
+ } }
541
+ return nw
542
+ }
543
+ function markedSpansAfter(old, endCh, isInsert) {
544
+ var nw
545
+ if (old) { for (var i = 0; i < old.length; ++i) {
546
+ var span = old[i], marker = span.marker
547
+ var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh)
548
+ if (endsAfter || span.from == endCh && marker.type == "bookmark" && (!isInsert || span.marker.insertLeft)) {
549
+ var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh);(nw || (nw = [])).push(new MarkedSpan(marker, startsBefore ? null : span.from - endCh,
550
+ span.to == null ? null : span.to - endCh))
551
+ }
552
+ } }
553
+ return nw
554
+ }
555
+
556
+ // Given a change object, compute the new set of marker spans that
557
+ // cover the line in which the change took place. Removes spans
558
+ // entirely within the change, reconnects spans belonging to the
559
+ // same marker that appear on both sides of the change, and cuts off
560
+ // spans partially within the change. Returns an array of span
561
+ // arrays with one element for each line in (after) the change.
562
+ function stretchSpansOverChange(doc, change) {
563
+ if (change.full) { return null }
564
+ var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans
565
+ var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans
566
+ if (!oldFirst && !oldLast) { return null }
567
+
568
+ var startCh = change.from.ch, endCh = change.to.ch, isInsert = cmp(change.from, change.to) == 0
569
+ // Get the spans that 'stick out' on both sides
570
+ var first = markedSpansBefore(oldFirst, startCh, isInsert)
571
+ var last = markedSpansAfter(oldLast, endCh, isInsert)
572
+
573
+ // Next, merge those two ends
574
+ var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0)
575
+ if (first) {
576
+ // Fix up .to properties of first
577
+ for (var i = 0; i < first.length; ++i) {
578
+ var span = first[i]
579
+ if (span.to == null) {
580
+ var found = getMarkedSpanFor(last, span.marker)
581
+ if (!found) { span.to = startCh }
582
+ else if (sameLine) { span.to = found.to == null ? null : found.to + offset }
583
+ }
584
+ }
585
+ }
586
+ if (last) {
587
+ // Fix up .from in last (or move them into first in case of sameLine)
588
+ for (var i$1 = 0; i$1 < last.length; ++i$1) {
589
+ var span$1 = last[i$1]
590
+ if (span$1.to != null) { span$1.to += offset }
591
+ if (span$1.from == null) {
592
+ var found$1 = getMarkedSpanFor(first, span$1.marker)
593
+ if (!found$1) {
594
+ span$1.from = offset
595
+ if (sameLine) { (first || (first = [])).push(span$1) }
596
+ }
597
+ } else {
598
+ span$1.from += offset
599
+ if (sameLine) { (first || (first = [])).push(span$1) }
600
+ }
601
+ }
602
+ }
603
+ // Make sure we didn't create any zero-length spans
604
+ if (first) { first = clearEmptySpans(first) }
605
+ if (last && last != first) { last = clearEmptySpans(last) }
606
+
607
+ var newMarkers = [first]
608
+ if (!sameLine) {
609
+ // Fill gap with whole-line-spans
610
+ var gap = change.text.length - 2, gapMarkers
611
+ if (gap > 0 && first)
612
+ { for (var i$2 = 0; i$2 < first.length; ++i$2)
613
+ { if (first[i$2].to == null)
614
+ { (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i$2].marker, null, null)) } } }
615
+ for (var i$3 = 0; i$3 < gap; ++i$3)
616
+ { newMarkers.push(gapMarkers) }
617
+ newMarkers.push(last)
618
+ }
619
+ return newMarkers
620
+ }
621
+
622
+ // Remove spans that are empty and don't have a clearWhenEmpty
623
+ // option of false.
624
+ function clearEmptySpans(spans) {
625
+ for (var i = 0; i < spans.length; ++i) {
626
+ var span = spans[i]
627
+ if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false)
628
+ { spans.splice(i--, 1) }
629
+ }
630
+ if (!spans.length) { return null }
631
+ return spans
632
+ }
633
+
634
+ // Used to 'clip' out readOnly ranges when making a change.
635
+ function removeReadOnlyRanges(doc, from, to) {
636
+ var markers = null
637
+ doc.iter(from.line, to.line + 1, function (line) {
638
+ if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) {
639
+ var mark = line.markedSpans[i].marker
640
+ if (mark.readOnly && (!markers || indexOf(markers, mark) == -1))
641
+ { (markers || (markers = [])).push(mark) }
642
+ } }
643
+ })
644
+ if (!markers) { return null }
645
+ var parts = [{from: from, to: to}]
646
+ for (var i = 0; i < markers.length; ++i) {
647
+ var mk = markers[i], m = mk.find(0)
648
+ for (var j = 0; j < parts.length; ++j) {
649
+ var p = parts[j]
650
+ if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) { continue }
651
+ var newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to)
652
+ if (dfrom < 0 || !mk.inclusiveLeft && !dfrom)
653
+ { newParts.push({from: p.from, to: m.from}) }
654
+ if (dto > 0 || !mk.inclusiveRight && !dto)
655
+ { newParts.push({from: m.to, to: p.to}) }
656
+ parts.splice.apply(parts, newParts)
657
+ j += newParts.length - 1
658
+ }
659
+ }
660
+ return parts
661
+ }
662
+
663
+ // Connect or disconnect spans from a line.
664
+ function detachMarkedSpans(line) {
665
+ var spans = line.markedSpans
666
+ if (!spans) { return }
667
+ for (var i = 0; i < spans.length; ++i)
668
+ { spans[i].marker.detachLine(line) }
669
+ line.markedSpans = null
670
+ }
671
+ function attachMarkedSpans(line, spans) {
672
+ if (!spans) { return }
673
+ for (var i = 0; i < spans.length; ++i)
674
+ { spans[i].marker.attachLine(line) }
675
+ line.markedSpans = spans
676
+ }
677
+
678
+ // Helpers used when computing which overlapping collapsed span
679
+ // counts as the larger one.
680
+ function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0 }
681
+ function extraRight(marker) { return marker.inclusiveRight ? 1 : 0 }
682
+
683
+ // Returns a number indicating which of two overlapping collapsed
684
+ // spans is larger (and thus includes the other). Falls back to
685
+ // comparing ids when the spans cover exactly the same range.
686
+ function compareCollapsedMarkers(a, b) {
687
+ var lenDiff = a.lines.length - b.lines.length
688
+ if (lenDiff != 0) { return lenDiff }
689
+ var aPos = a.find(), bPos = b.find()
690
+ var fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b)
691
+ if (fromCmp) { return -fromCmp }
692
+ var toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b)
693
+ if (toCmp) { return toCmp }
694
+ return b.id - a.id
695
+ }
696
+
697
+ // Find out whether a line ends or starts in a collapsed span. If
698
+ // so, return the marker for that span.
699
+ function collapsedSpanAtSide(line, start) {
700
+ var sps = sawCollapsedSpans && line.markedSpans, found
701
+ if (sps) { for (var sp = void 0, i = 0; i < sps.length; ++i) {
702
+ sp = sps[i]
703
+ if (sp.marker.collapsed && (start ? sp.from : sp.to) == null &&
704
+ (!found || compareCollapsedMarkers(found, sp.marker) < 0))
705
+ { found = sp.marker }
706
+ } }
707
+ return found
708
+ }
709
+ function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true) }
710
+ function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false) }
711
+
712
+ // Test whether there exists a collapsed span that partially
713
+ // overlaps (covers the start or end, but not both) of a new span.
714
+ // Such overlap is not allowed.
715
+ function conflictingCollapsedRange(doc, lineNo$$1, from, to, marker) {
716
+ var line = getLine(doc, lineNo$$1)
717
+ var sps = sawCollapsedSpans && line.markedSpans
718
+ if (sps) { for (var i = 0; i < sps.length; ++i) {
719
+ var sp = sps[i]
720
+ if (!sp.marker.collapsed) { continue }
721
+ var found = sp.marker.find(0)
722
+ var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker)
723
+ var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker)
724
+ if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) { continue }
725
+ if (fromCmp <= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.to, from) >= 0 : cmp(found.to, from) > 0) ||
726
+ fromCmp >= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.from, to) <= 0 : cmp(found.from, to) < 0))
727
+ { return true }
728
+ } }
729
+ }
730
+
731
+ // A visual line is a line as drawn on the screen. Folding, for
732
+ // example, can cause multiple logical lines to appear on the same
733
+ // visual line. This finds the start of the visual line that the
734
+ // given line is part of (usually that is the line itself).
735
+ function visualLine(line) {
736
+ var merged
737
+ while (merged = collapsedSpanAtStart(line))
738
+ { line = merged.find(-1, true).line }
739
+ return line
740
+ }
741
+
742
+ // Returns an array of logical lines that continue the visual line
743
+ // started by the argument, or undefined if there are no such lines.
744
+ function visualLineContinued(line) {
745
+ var merged, lines
746
+ while (merged = collapsedSpanAtEnd(line)) {
747
+ line = merged.find(1, true).line
748
+ ;(lines || (lines = [])).push(line)
749
+ }
750
+ return lines
751
+ }
752
+
753
+ // Get the line number of the start of the visual line that the
754
+ // given line number is part of.
755
+ function visualLineNo(doc, lineN) {
756
+ var line = getLine(doc, lineN), vis = visualLine(line)
757
+ if (line == vis) { return lineN }
758
+ return lineNo(vis)
759
+ }
760
+
761
+ // Get the line number of the start of the next visual line after
762
+ // the given line.
763
+ function visualLineEndNo(doc, lineN) {
764
+ if (lineN > doc.lastLine()) { return lineN }
765
+ var line = getLine(doc, lineN), merged
766
+ if (!lineIsHidden(doc, line)) { return lineN }
767
+ while (merged = collapsedSpanAtEnd(line))
768
+ { line = merged.find(1, true).line }
769
+ return lineNo(line) + 1
770
+ }
771
+
772
+ // Compute whether a line is hidden. Lines count as hidden when they
773
+ // are part of a visual line that starts with another line, or when
774
+ // they are entirely covered by collapsed, non-widget span.
775
+ function lineIsHidden(doc, line) {
776
+ var sps = sawCollapsedSpans && line.markedSpans
777
+ if (sps) { for (var sp = void 0, i = 0; i < sps.length; ++i) {
778
+ sp = sps[i]
779
+ if (!sp.marker.collapsed) { continue }
780
+ if (sp.from == null) { return true }
781
+ if (sp.marker.widgetNode) { continue }
782
+ if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp))
783
+ { return true }
784
+ } }
785
+ }
786
+ function lineIsHiddenInner(doc, line, span) {
787
+ if (span.to == null) {
788
+ var end = span.marker.find(1, true)
789
+ return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpans, span.marker))
790
+ }
791
+ if (span.marker.inclusiveRight && span.to == line.text.length)
792
+ { return true }
793
+ for (var sp = void 0, i = 0; i < line.markedSpans.length; ++i) {
794
+ sp = line.markedSpans[i]
795
+ if (sp.marker.collapsed && !sp.marker.widgetNode && sp.from == span.to &&
796
+ (sp.to == null || sp.to != span.from) &&
797
+ (sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
798
+ lineIsHiddenInner(doc, line, sp)) { return true }
799
+ }
800
+ }
801
+
802
+ // Find the height above the given line.
803
+ function heightAtLine(lineObj) {
804
+ lineObj = visualLine(lineObj)
805
+
806
+ var h = 0, chunk = lineObj.parent
807
+ for (var i = 0; i < chunk.lines.length; ++i) {
808
+ var line = chunk.lines[i]
809
+ if (line == lineObj) { break }
810
+ else { h += line.height }
811
+ }
812
+ for (var p = chunk.parent; p; chunk = p, p = chunk.parent) {
813
+ for (var i$1 = 0; i$1 < p.children.length; ++i$1) {
814
+ var cur = p.children[i$1]
815
+ if (cur == chunk) { break }
816
+ else { h += cur.height }
817
+ }
818
+ }
819
+ return h
820
+ }
821
+
822
+ // Compute the character length of a line, taking into account
823
+ // collapsed ranges (see markText) that might hide parts, and join
824
+ // other lines onto it.
825
+ function lineLength(line) {
826
+ if (line.height == 0) { return 0 }
827
+ var len = line.text.length, merged, cur = line
828
+ while (merged = collapsedSpanAtStart(cur)) {
829
+ var found = merged.find(0, true)
830
+ cur = found.from.line
831
+ len += found.from.ch - found.to.ch
832
+ }
833
+ cur = line
834
+ while (merged = collapsedSpanAtEnd(cur)) {
835
+ var found$1 = merged.find(0, true)
836
+ len -= cur.text.length - found$1.from.ch
837
+ cur = found$1.to.line
838
+ len += cur.text.length - found$1.to.ch
839
+ }
840
+ return len
841
+ }
842
+
843
+ // Find the longest line in the document.
844
+ function findMaxLine(cm) {
845
+ var d = cm.display, doc = cm.doc
846
+ d.maxLine = getLine(doc, doc.first)
847
+ d.maxLineLength = lineLength(d.maxLine)
848
+ d.maxLineChanged = true
849
+ doc.iter(function (line) {
850
+ var len = lineLength(line)
851
+ if (len > d.maxLineLength) {
852
+ d.maxLineLength = len
853
+ d.maxLine = line
854
+ }
855
+ })
856
+ }
857
+
858
+ // BIDI HELPERS
859
+
860
+ function iterateBidiSections(order, from, to, f) {
861
+ if (!order) { return f(from, to, "ltr") }
862
+ var found = false
863
+ for (var i = 0; i < order.length; ++i) {
864
+ var part = order[i]
865
+ if (part.from < to && part.to > from || from == to && part.to == from) {
866
+ f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr")
867
+ found = true
868
+ }
869
+ }
870
+ if (!found) { f(from, to, "ltr") }
871
+ }
872
+
873
+ function bidiLeft(part) { return part.level % 2 ? part.to : part.from }
874
+ function bidiRight(part) { return part.level % 2 ? part.from : part.to }
875
+
876
+ function lineLeft(line) { var order = getOrder(line); return order ? bidiLeft(order[0]) : 0 }
877
+ function lineRight(line) {
878
+ var order = getOrder(line)
879
+ if (!order) { return line.text.length }
880
+ return bidiRight(lst(order))
881
+ }
882
+
883
+ function compareBidiLevel(order, a, b) {
884
+ var linedir = order[0].level
885
+ if (a == linedir) { return true }
886
+ if (b == linedir) { return false }
887
+ return a < b
888
+ }
889
+
890
+ var bidiOther = null
891
+ function getBidiPartAt(order, pos) {
892
+ var found
893
+ bidiOther = null
894
+ for (var i = 0; i < order.length; ++i) {
895
+ var cur = order[i]
896
+ if (cur.from < pos && cur.to > pos) { return i }
897
+ if ((cur.from == pos || cur.to == pos)) {
898
+ if (found == null) {
899
+ found = i
900
+ } else if (compareBidiLevel(order, cur.level, order[found].level)) {
901
+ if (cur.from != cur.to) { bidiOther = found }
902
+ return i
903
+ } else {
904
+ if (cur.from != cur.to) { bidiOther = i }
905
+ return found
906
+ }
907
+ }
908
+ }
909
+ return found
910
+ }
911
+
912
+ function moveInLine(line, pos, dir, byUnit) {
913
+ if (!byUnit) { return pos + dir }
914
+ do { pos += dir }
915
+ while (pos > 0 && isExtendingChar(line.text.charAt(pos)))
916
+ return pos
917
+ }
918
+
919
+ // This is needed in order to move 'visually' through bi-directional
920
+ // text -- i.e., pressing left should make the cursor go left, even
921
+ // when in RTL text. The tricky part is the 'jumps', where RTL and
922
+ // LTR text touch each other. This often requires the cursor offset
923
+ // to move more than one unit, in order to visually move one unit.
924
+ function moveVisually(line, start, dir, byUnit) {
925
+ var bidi = getOrder(line)
926
+ if (!bidi) { return moveLogically(line, start, dir, byUnit) }
927
+ var pos = getBidiPartAt(bidi, start), part = bidi[pos]
928
+ var target = moveInLine(line, start, part.level % 2 ? -dir : dir, byUnit)
929
+
930
+ for (;;) {
931
+ if (target > part.from && target < part.to) { return target }
932
+ if (target == part.from || target == part.to) {
933
+ if (getBidiPartAt(bidi, target) == pos) { return target }
934
+ part = bidi[pos += dir]
935
+ return (dir > 0) == part.level % 2 ? part.to : part.from
936
+ } else {
937
+ part = bidi[pos += dir]
938
+ if (!part) { return null }
939
+ if ((dir > 0) == part.level % 2)
940
+ { target = moveInLine(line, part.to, -1, byUnit) }
941
+ else
942
+ { target = moveInLine(line, part.from, 1, byUnit) }
943
+ }
944
+ }
945
+ }
946
+
947
+ function moveLogically(line, start, dir, byUnit) {
948
+ var target = start + dir
949
+ if (byUnit) { while (target > 0 && isExtendingChar(line.text.charAt(target))) { target += dir } }
950
+ return target < 0 || target > line.text.length ? null : target
951
+ }
952
+
953
+ // Bidirectional ordering algorithm
954
+ // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm
955
+ // that this (partially) implements.
956
+
957
+ // One-char codes used for character types:
958
+ // L (L): Left-to-Right
959
+ // R (R): Right-to-Left
960
+ // r (AL): Right-to-Left Arabic
961
+ // 1 (EN): European Number
962
+ // + (ES): European Number Separator
963
+ // % (ET): European Number Terminator
964
+ // n (AN): Arabic Number
965
+ // , (CS): Common Number Separator
966
+ // m (NSM): Non-Spacing Mark
967
+ // b (BN): Boundary Neutral
968
+ // s (B): Paragraph Separator
969
+ // t (S): Segment Separator
970
+ // w (WS): Whitespace
971
+ // N (ON): Other Neutrals
972
+
973
+ // Returns null if characters are ordered as they appear
974
+ // (left-to-right), or an array of sections ({from, to, level}
975
+ // objects) in the order in which they occur visually.
976
+ var bidiOrdering = (function() {
977
+ // Character types for codepoints 0 to 0xff
978
+ var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN"
979
+ // Character types for codepoints 0x600 to 0x6f9
980
+ var arabicTypes = "nnnnnnNNr%%r,rNNmmmmmmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmnNmmmmmmrrmmNmmmmrr1111111111"
981
+ function charType(code) {
982
+ if (code <= 0xf7) { return lowTypes.charAt(code) }
983
+ else if (0x590 <= code && code <= 0x5f4) { return "R" }
984
+ else if (0x600 <= code && code <= 0x6f9) { return arabicTypes.charAt(code - 0x600) }
985
+ else if (0x6ee <= code && code <= 0x8ac) { return "r" }
986
+ else if (0x2000 <= code && code <= 0x200b) { return "w" }
987
+ else if (code == 0x200c) { return "b" }
988
+ else { return "L" }
989
+ }
990
+
991
+ var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/
992
+ var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/
993
+ // Browsers seem to always treat the boundaries of block elements as being L.
994
+ var outerType = "L"
995
+
996
+ function BidiSpan(level, from, to) {
997
+ this.level = level
998
+ this.from = from; this.to = to
999
+ }
1000
+
1001
+ return function(str) {
1002
+ if (!bidiRE.test(str)) { return false }
1003
+ var len = str.length, types = []
1004
+ for (var i = 0; i < len; ++i)
1005
+ { types.push(charType(str.charCodeAt(i))) }
1006
+
1007
+ // W1. Examine each non-spacing mark (NSM) in the level run, and
1008
+ // change the type of the NSM to the type of the previous
1009
+ // character. If the NSM is at the start of the level run, it will
1010
+ // get the type of sor.
1011
+ for (var i$1 = 0, prev = outerType; i$1 < len; ++i$1) {
1012
+ var type = types[i$1]
1013
+ if (type == "m") { types[i$1] = prev }
1014
+ else { prev = type }
1015
+ }
1016
+
1017
+ // W2. Search backwards from each instance of a European number
1018
+ // until the first strong type (R, L, AL, or sor) is found. If an
1019
+ // AL is found, change the type of the European number to Arabic
1020
+ // number.
1021
+ // W3. Change all ALs to R.
1022
+ for (var i$2 = 0, cur = outerType; i$2 < len; ++i$2) {
1023
+ var type$1 = types[i$2]
1024
+ if (type$1 == "1" && cur == "r") { types[i$2] = "n" }
1025
+ else if (isStrong.test(type$1)) { cur = type$1; if (type$1 == "r") { types[i$2] = "R" } }
1026
+ }
1027
+
1028
+ // W4. A single European separator between two European numbers
1029
+ // changes to a European number. A single common separator between
1030
+ // two numbers of the same type changes to that type.
1031
+ for (var i$3 = 1, prev$1 = types[0]; i$3 < len - 1; ++i$3) {
1032
+ var type$2 = types[i$3]
1033
+ if (type$2 == "+" && prev$1 == "1" && types[i$3+1] == "1") { types[i$3] = "1" }
1034
+ else if (type$2 == "," && prev$1 == types[i$3+1] &&
1035
+ (prev$1 == "1" || prev$1 == "n")) { types[i$3] = prev$1 }
1036
+ prev$1 = type$2
1037
+ }
1038
+
1039
+ // W5. A sequence of European terminators adjacent to European
1040
+ // numbers changes to all European numbers.
1041
+ // W6. Otherwise, separators and terminators change to Other
1042
+ // Neutral.
1043
+ for (var i$4 = 0; i$4 < len; ++i$4) {
1044
+ var type$3 = types[i$4]
1045
+ if (type$3 == ",") { types[i$4] = "N" }
1046
+ else if (type$3 == "%") {
1047
+ var end = void 0
1048
+ for (end = i$4 + 1; end < len && types[end] == "%"; ++end) {}
1049
+ var replace = (i$4 && types[i$4-1] == "!") || (end < len && types[end] == "1") ? "1" : "N"
1050
+ for (var j = i$4; j < end; ++j) { types[j] = replace }
1051
+ i$4 = end - 1
1052
+ }
1053
+ }
1054
+
1055
+ // W7. Search backwards from each instance of a European number
1056
+ // until the first strong type (R, L, or sor) is found. If an L is
1057
+ // found, then change the type of the European number to L.
1058
+ for (var i$5 = 0, cur$1 = outerType; i$5 < len; ++i$5) {
1059
+ var type$4 = types[i$5]
1060
+ if (cur$1 == "L" && type$4 == "1") { types[i$5] = "L" }
1061
+ else if (isStrong.test(type$4)) { cur$1 = type$4 }
1062
+ }
1063
+
1064
+ // N1. A sequence of neutrals takes the direction of the
1065
+ // surrounding strong text if the text on both sides has the same
1066
+ // direction. European and Arabic numbers act as if they were R in
1067
+ // terms of their influence on neutrals. Start-of-level-run (sor)
1068
+ // and end-of-level-run (eor) are used at level run boundaries.
1069
+ // N2. Any remaining neutrals take the embedding direction.
1070
+ for (var i$6 = 0; i$6 < len; ++i$6) {
1071
+ if (isNeutral.test(types[i$6])) {
1072
+ var end$1 = void 0
1073
+ for (end$1 = i$6 + 1; end$1 < len && isNeutral.test(types[end$1]); ++end$1) {}
1074
+ var before = (i$6 ? types[i$6-1] : outerType) == "L"
1075
+ var after = (end$1 < len ? types[end$1] : outerType) == "L"
1076
+ var replace$1 = before || after ? "L" : "R"
1077
+ for (var j$1 = i$6; j$1 < end$1; ++j$1) { types[j$1] = replace$1 }
1078
+ i$6 = end$1 - 1
1079
+ }
1080
+ }
1081
+
1082
+ // Here we depart from the documented algorithm, in order to avoid
1083
+ // building up an actual levels array. Since there are only three
1084
+ // levels (0, 1, 2) in an implementation that doesn't take
1085
+ // explicit embedding into account, we can build up the order on
1086
+ // the fly, without following the level-based algorithm.
1087
+ var order = [], m
1088
+ for (var i$7 = 0; i$7 < len;) {
1089
+ if (countsAsLeft.test(types[i$7])) {
1090
+ var start = i$7
1091
+ for (++i$7; i$7 < len && countsAsLeft.test(types[i$7]); ++i$7) {}
1092
+ order.push(new BidiSpan(0, start, i$7))
1093
+ } else {
1094
+ var pos = i$7, at = order.length
1095
+ for (++i$7; i$7 < len && types[i$7] != "L"; ++i$7) {}
1096
+ for (var j$2 = pos; j$2 < i$7;) {
1097
+ if (countsAsNum.test(types[j$2])) {
1098
+ if (pos < j$2) { order.splice(at, 0, new BidiSpan(1, pos, j$2)) }
1099
+ var nstart = j$2
1100
+ for (++j$2; j$2 < i$7 && countsAsNum.test(types[j$2]); ++j$2) {}
1101
+ order.splice(at, 0, new BidiSpan(2, nstart, j$2))
1102
+ pos = j$2
1103
+ } else { ++j$2 }
1104
+ }
1105
+ if (pos < i$7) { order.splice(at, 0, new BidiSpan(1, pos, i$7)) }
1106
+ }
1107
+ }
1108
+ if (order[0].level == 1 && (m = str.match(/^\s+/))) {
1109
+ order[0].from = m[0].length
1110
+ order.unshift(new BidiSpan(0, 0, m[0].length))
1111
+ }
1112
+ if (lst(order).level == 1 && (m = str.match(/\s+$/))) {
1113
+ lst(order).to -= m[0].length
1114
+ order.push(new BidiSpan(0, len - m[0].length, len))
1115
+ }
1116
+ if (order[0].level == 2)
1117
+ { order.unshift(new BidiSpan(1, order[0].to, order[0].to)) }
1118
+ if (order[0].level != lst(order).level)
1119
+ { order.push(new BidiSpan(order[0].level, len, len)) }
1120
+
1121
+ return order
1122
+ }
1123
+ })()
1124
+
1125
+ // Get the bidi ordering for the given line (and cache it). Returns
1126
+ // false for lines that are fully left-to-right, and an array of
1127
+ // BidiSpan objects otherwise.
1128
+ function getOrder(line) {
1129
+ var order = line.order
1130
+ if (order == null) { order = line.order = bidiOrdering(line.text) }
1131
+ return order
1132
+ }
1133
+
1134
+ // EVENT HANDLING
1135
+
1136
+ // Lightweight event framework. on/off also work on DOM nodes,
1137
+ // registering native DOM handlers.
1138
+
1139
+ var noHandlers = []
1140
+
1141
+ var on = function(emitter, type, f) {
1142
+ if (emitter.addEventListener) {
1143
+ emitter.addEventListener(type, f, false)
1144
+ } else if (emitter.attachEvent) {
1145
+ emitter.attachEvent("on" + type, f)
1146
+ } else {
1147
+ var map$$1 = emitter._handlers || (emitter._handlers = {})
1148
+ map$$1[type] = (map$$1[type] || noHandlers).concat(f)
1149
+ }
1150
+ }
1151
+
1152
+ function getHandlers(emitter, type) {
1153
+ return emitter._handlers && emitter._handlers[type] || noHandlers
1154
+ }
1155
+
1156
+ function off(emitter, type, f) {
1157
+ if (emitter.removeEventListener) {
1158
+ emitter.removeEventListener(type, f, false)
1159
+ } else if (emitter.detachEvent) {
1160
+ emitter.detachEvent("on" + type, f)
1161
+ } else {
1162
+ var map$$1 = emitter._handlers, arr = map$$1 && map$$1[type]
1163
+ if (arr) {
1164
+ var index = indexOf(arr, f)
1165
+ if (index > -1)
1166
+ { map$$1[type] = arr.slice(0, index).concat(arr.slice(index + 1)) }
1167
+ }
1168
+ }
1169
+ }
1170
+
1171
+ function signal(emitter, type /*, values...*/) {
1172
+ var handlers = getHandlers(emitter, type)
1173
+ if (!handlers.length) { return }
1174
+ var args = Array.prototype.slice.call(arguments, 2)
1175
+ for (var i = 0; i < handlers.length; ++i) { handlers[i].apply(null, args) }
1176
+ }
1177
+
1178
+ // The DOM events that CodeMirror handles can be overridden by
1179
+ // registering a (non-DOM) handler on the editor for the event name,
1180
+ // and preventDefault-ing the event in that handler.
1181
+ function signalDOMEvent(cm, e, override) {
1182
+ if (typeof e == "string")
1183
+ { e = {type: e, preventDefault: function() { this.defaultPrevented = true }} }
1184
+ signal(cm, override || e.type, cm, e)
1185
+ return e_defaultPrevented(e) || e.codemirrorIgnore
1186
+ }
1187
+
1188
+ function signalCursorActivity(cm) {
1189
+ var arr = cm._handlers && cm._handlers.cursorActivity
1190
+ if (!arr) { return }
1191
+ var set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = [])
1192
+ for (var i = 0; i < arr.length; ++i) { if (indexOf(set, arr[i]) == -1)
1193
+ { set.push(arr[i]) } }
1194
+ }
1195
+
1196
+ function hasHandler(emitter, type) {
1197
+ return getHandlers(emitter, type).length > 0
1198
+ }
1199
+
1200
+ // Add on and off methods to a constructor's prototype, to make
1201
+ // registering events on such objects more convenient.
1202
+ function eventMixin(ctor) {
1203
+ ctor.prototype.on = function(type, f) {on(this, type, f)}
1204
+ ctor.prototype.off = function(type, f) {off(this, type, f)}
1205
+ }
1206
+
1207
+ // Due to the fact that we still support jurassic IE versions, some
1208
+ // compatibility wrappers are needed.
1209
+
1210
+ function e_preventDefault(e) {
1211
+ if (e.preventDefault) { e.preventDefault() }
1212
+ else { e.returnValue = false }
1213
+ }
1214
+ function e_stopPropagation(e) {
1215
+ if (e.stopPropagation) { e.stopPropagation() }
1216
+ else { e.cancelBubble = true }
1217
+ }
1218
+ function e_defaultPrevented(e) {
1219
+ return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false
1220
+ }
1221
+ function e_stop(e) {e_preventDefault(e); e_stopPropagation(e)}
1222
+
1223
+ function e_target(e) {return e.target || e.srcElement}
1224
+ function e_button(e) {
1225
+ var b = e.which
1226
+ if (b == null) {
1227
+ if (e.button & 1) { b = 1 }
1228
+ else if (e.button & 2) { b = 3 }
1229
+ else if (e.button & 4) { b = 2 }
1230
+ }
1231
+ if (mac && e.ctrlKey && b == 1) { b = 3 }
1232
+ return b
1233
+ }
1234
+
1235
+ // Detect drag-and-drop
1236
+ var dragAndDrop = function() {
1237
+ // There is *some* kind of drag-and-drop support in IE6-8, but I
1238
+ // couldn't get it to work yet.
1239
+ if (ie && ie_version < 9) { return false }
1240
+ var div = elt('div')
1241
+ return "draggable" in div || "dragDrop" in div
1242
+ }()
1243
+
1244
+ var zwspSupported
1245
+ function zeroWidthElement(measure) {
1246
+ if (zwspSupported == null) {
1247
+ var test = elt("span", "\u200b")
1248
+ removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")]))
1249
+ if (measure.firstChild.offsetHeight != 0)
1250
+ { zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie && ie_version < 8) }
1251
+ }
1252
+ var node = zwspSupported ? elt("span", "\u200b") :
1253
+ elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px")
1254
+ node.setAttribute("cm-text", "")
1255
+ return node
1256
+ }
1257
+
1258
+ // Feature-detect IE's crummy client rect reporting for bidi text
1259
+ var badBidiRects
1260
+ function hasBadBidiRects(measure) {
1261
+ if (badBidiRects != null) { return badBidiRects }
1262
+ var txt = removeChildrenAndAdd(measure, document.createTextNode("A\u062eA"))
1263
+ var r0 = range(txt, 0, 1).getBoundingClientRect()
1264
+ var r1 = range(txt, 1, 2).getBoundingClientRect()
1265
+ removeChildren(measure)
1266
+ if (!r0 || r0.left == r0.right) { return false } // Safari returns null in some cases (#2780)
1267
+ return badBidiRects = (r1.right - r0.right < 3)
1268
+ }
1269
+
1270
+ // See if "".split is the broken IE version, if so, provide an
1271
+ // alternative way to split lines.
1272
+ var splitLinesAuto = "\n\nb".split(/\n/).length != 3 ? function (string) {
1273
+ var pos = 0, result = [], l = string.length
1274
+ while (pos <= l) {
1275
+ var nl = string.indexOf("\n", pos)
1276
+ if (nl == -1) { nl = string.length }
1277
+ var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl)
1278
+ var rt = line.indexOf("\r")
1279
+ if (rt != -1) {
1280
+ result.push(line.slice(0, rt))
1281
+ pos += rt + 1
1282
+ } else {
1283
+ result.push(line)
1284
+ pos = nl + 1
1285
+ }
1286
+ }
1287
+ return result
1288
+ } : function (string) { return string.split(/\r\n?|\n/); }
1289
+
1290
+ var hasSelection = window.getSelection ? function (te) {
1291
+ try { return te.selectionStart != te.selectionEnd }
1292
+ catch(e) { return false }
1293
+ } : function (te) {
1294
+ var range$$1
1295
+ try {range$$1 = te.ownerDocument.selection.createRange()}
1296
+ catch(e) {}
1297
+ if (!range$$1 || range$$1.parentElement() != te) { return false }
1298
+ return range$$1.compareEndPoints("StartToEnd", range$$1) != 0
1299
+ }
1300
+
1301
+ var hasCopyEvent = (function () {
1302
+ var e = elt("div")
1303
+ if ("oncopy" in e) { return true }
1304
+ e.setAttribute("oncopy", "return;")
1305
+ return typeof e.oncopy == "function"
1306
+ })()
1307
+
1308
+ var badZoomedRects = null
1309
+ function hasBadZoomedRects(measure) {
1310
+ if (badZoomedRects != null) { return badZoomedRects }
1311
+ var node = removeChildrenAndAdd(measure, elt("span", "x"))
1312
+ var normal = node.getBoundingClientRect()
1313
+ var fromRange = range(node, 0, 1).getBoundingClientRect()
1314
+ return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1
1315
+ }
1316
+
1317
+ // Known modes, by name and by MIME
1318
+ var modes = {};
1319
+ var mimeModes = {}
1320
+
1321
+ // Extra arguments are stored as the mode's dependencies, which is
1322
+ // used by (legacy) mechanisms like loadmode.js to automatically
1323
+ // load a mode. (Preferred mechanism is the require/define calls.)
1324
+ function defineMode(name, mode) {
1325
+ if (arguments.length > 2)
1326
+ { mode.dependencies = Array.prototype.slice.call(arguments, 2) }
1327
+ modes[name] = mode
1328
+ }
1329
+
1330
+ function defineMIME(mime, spec) {
1331
+ mimeModes[mime] = spec
1332
+ }
1333
+
1334
+ // Given a MIME type, a {name, ...options} config object, or a name
1335
+ // string, return a mode config object.
1336
+ function resolveMode(spec) {
1337
+ if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) {
1338
+ spec = mimeModes[spec]
1339
+ } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) {
1340
+ var found = mimeModes[spec.name]
1341
+ if (typeof found == "string") { found = {name: found} }
1342
+ spec = createObj(found, spec)
1343
+ spec.name = found.name
1344
+ } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) {
1345
+ return resolveMode("application/xml")
1346
+ } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+json$/.test(spec)) {
1347
+ return resolveMode("application/json")
1348
+ }
1349
+ if (typeof spec == "string") { return {name: spec} }
1350
+ else { return spec || {name: "null"} }
1351
+ }
1352
+
1353
+ // Given a mode spec (anything that resolveMode accepts), find and
1354
+ // initialize an actual mode object.
1355
+ function getMode(options, spec) {
1356
+ spec = resolveMode(spec)
1357
+ var mfactory = modes[spec.name]
1358
+ if (!mfactory) { return getMode(options, "text/plain") }
1359
+ var modeObj = mfactory(options, spec)
1360
+ if (modeExtensions.hasOwnProperty(spec.name)) {
1361
+ var exts = modeExtensions[spec.name]
1362
+ for (var prop in exts) {
1363
+ if (!exts.hasOwnProperty(prop)) { continue }
1364
+ if (modeObj.hasOwnProperty(prop)) { modeObj["_" + prop] = modeObj[prop] }
1365
+ modeObj[prop] = exts[prop]
1366
+ }
1367
+ }
1368
+ modeObj.name = spec.name
1369
+ if (spec.helperType) { modeObj.helperType = spec.helperType }
1370
+ if (spec.modeProps) { for (var prop$1 in spec.modeProps)
1371
+ { modeObj[prop$1] = spec.modeProps[prop$1] } }
1372
+
1373
+ return modeObj
1374
+ }
1375
+
1376
+ // This can be used to attach properties to mode objects from
1377
+ // outside the actual mode definition.
1378
+ var modeExtensions = {}
1379
+ function extendMode(mode, properties) {
1380
+ var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {})
1381
+ copyObj(properties, exts)
1382
+ }
1383
+
1384
+ function copyState(mode, state) {
1385
+ if (state === true) { return state }
1386
+ if (mode.copyState) { return mode.copyState(state) }
1387
+ var nstate = {}
1388
+ for (var n in state) {
1389
+ var val = state[n]
1390
+ if (val instanceof Array) { val = val.concat([]) }
1391
+ nstate[n] = val
1392
+ }
1393
+ return nstate
1394
+ }
1395
+
1396
+ // Given a mode and a state (for that mode), find the inner mode and
1397
+ // state at the position that the state refers to.
1398
+ function innerMode(mode, state) {
1399
+ var info
1400
+ while (mode.innerMode) {
1401
+ info = mode.innerMode(state)
1402
+ if (!info || info.mode == mode) { break }
1403
+ state = info.state
1404
+ mode = info.mode
1405
+ }
1406
+ return info || {mode: mode, state: state}
1407
+ }
1408
+
1409
+ function startState(mode, a1, a2) {
1410
+ return mode.startState ? mode.startState(a1, a2) : true
1411
+ }
1412
+
1413
+ // STRING STREAM
1414
+
1415
+ // Fed to the mode parsers, provides helper functions to make
1416
+ // parsers more succinct.
1417
+
1418
+ var StringStream = function(string, tabSize) {
1419
+ this.pos = this.start = 0
1420
+ this.string = string
1421
+ this.tabSize = tabSize || 8
1422
+ this.lastColumnPos = this.lastColumnValue = 0
1423
+ this.lineStart = 0
1424
+ }
1425
+
1426
+ StringStream.prototype = {
1427
+ eol: function() {return this.pos >= this.string.length},
1428
+ sol: function() {return this.pos == this.lineStart},
1429
+ peek: function() {return this.string.charAt(this.pos) || undefined},
1430
+ next: function() {
1431
+ if (this.pos < this.string.length)
1432
+ { return this.string.charAt(this.pos++) }
1433
+ },
1434
+ eat: function(match) {
1435
+ var ch = this.string.charAt(this.pos)
1436
+ var ok
1437
+ if (typeof match == "string") { ok = ch == match }
1438
+ else { ok = ch && (match.test ? match.test(ch) : match(ch)) }
1439
+ if (ok) {++this.pos; return ch}
1440
+ },
1441
+ eatWhile: function(match) {
1442
+ var start = this.pos
1443
+ while (this.eat(match)){}
1444
+ return this.pos > start
1445
+ },
1446
+ eatSpace: function() {
1447
+ var this$1 = this;
1448
+
1449
+ var start = this.pos
1450
+ while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) { ++this$1.pos }
1451
+ return this.pos > start
1452
+ },
1453
+ skipToEnd: function() {this.pos = this.string.length},
1454
+ skipTo: function(ch) {
1455
+ var found = this.string.indexOf(ch, this.pos)
1456
+ if (found > -1) {this.pos = found; return true}
1457
+ },
1458
+ backUp: function(n) {this.pos -= n},
1459
+ column: function() {
1460
+ if (this.lastColumnPos < this.start) {
1461
+ this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue)
1462
+ this.lastColumnPos = this.start
1463
+ }
1464
+ return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
1465
+ },
1466
+ indentation: function() {
1467
+ return countColumn(this.string, null, this.tabSize) -
1468
+ (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
1469
+ },
1470
+ match: function(pattern, consume, caseInsensitive) {
1471
+ if (typeof pattern == "string") {
1472
+ var cased = function (str) { return caseInsensitive ? str.toLowerCase() : str; }
1473
+ var substr = this.string.substr(this.pos, pattern.length)
1474
+ if (cased(substr) == cased(pattern)) {
1475
+ if (consume !== false) { this.pos += pattern.length }
1476
+ return true
1477
+ }
1478
+ } else {
1479
+ var match = this.string.slice(this.pos).match(pattern)
1480
+ if (match && match.index > 0) { return null }
1481
+ if (match && consume !== false) { this.pos += match[0].length }
1482
+ return match
1483
+ }
1484
+ },
1485
+ current: function(){return this.string.slice(this.start, this.pos)},
1486
+ hideFirstChars: function(n, inner) {
1487
+ this.lineStart += n
1488
+ try { return inner() }
1489
+ finally { this.lineStart -= n }
1490
+ }
1491
+ }
1492
+
1493
+ // Compute a style array (an array starting with a mode generation
1494
+ // -- for invalidation -- followed by pairs of end positions and
1495
+ // style strings), which is used to highlight the tokens on the
1496
+ // line.
1497
+ function highlightLine(cm, line, state, forceToEnd) {
1498
+ // A styles array always starts with a number identifying the
1499
+ // mode/overlays that it is based on (for easy invalidation).
1500
+ var st = [cm.state.modeGen], lineClasses = {}
1501
+ // Compute the base array of styles
1502
+ runMode(cm, line.text, cm.doc.mode, state, function (end, style) { return st.push(end, style); },
1503
+ lineClasses, forceToEnd)
1504
+
1505
+ // Run overlays, adjust style array.
1506
+ var loop = function ( o ) {
1507
+ var overlay = cm.state.overlays[o], i = 1, at = 0
1508
+ runMode(cm, line.text, overlay.mode, true, function (end, style) {
1509
+ var start = i
1510
+ // Ensure there's a token end at the current position, and that i points at it
1511
+ while (at < end) {
1512
+ var i_end = st[i]
1513
+ if (i_end > end)
1514
+ { st.splice(i, 1, end, st[i+1], i_end) }
1515
+ i += 2
1516
+ at = Math.min(end, i_end)
1517
+ }
1518
+ if (!style) { return }
1519
+ if (overlay.opaque) {
1520
+ st.splice(start, i - start, end, "overlay " + style)
1521
+ i = start + 2
1522
+ } else {
1523
+ for (; start < i; start += 2) {
1524
+ var cur = st[start+1]
1525
+ st[start+1] = (cur ? cur + " " : "") + "overlay " + style
1526
+ }
1527
+ }
1528
+ }, lineClasses)
1529
+ };
1530
+
1531
+ for (var o = 0; o < cm.state.overlays.length; ++o) loop( o );
1532
+
1533
+ return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null}
1534
+ }
1535
+
1536
+ function getLineStyles(cm, line, updateFrontier) {
1537
+ if (!line.styles || line.styles[0] != cm.state.modeGen) {
1538
+ var state = getStateBefore(cm, lineNo(line))
1539
+ var result = highlightLine(cm, line, line.text.length > cm.options.maxHighlightLength ? copyState(cm.doc.mode, state) : state)
1540
+ line.stateAfter = state
1541
+ line.styles = result.styles
1542
+ if (result.classes) { line.styleClasses = result.classes }
1543
+ else if (line.styleClasses) { line.styleClasses = null }
1544
+ if (updateFrontier === cm.doc.frontier) { cm.doc.frontier++ }
1545
+ }
1546
+ return line.styles
1547
+ }
1548
+
1549
+ function getStateBefore(cm, n, precise) {
1550
+ var doc = cm.doc, display = cm.display
1551
+ if (!doc.mode.startState) { return true }
1552
+ var pos = findStartLine(cm, n, precise), state = pos > doc.first && getLine(doc, pos-1).stateAfter
1553
+ if (!state) { state = startState(doc.mode) }
1554
+ else { state = copyState(doc.mode, state) }
1555
+ doc.iter(pos, n, function (line) {
1556
+ processLine(cm, line.text, state)
1557
+ var save = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo
1558
+ line.stateAfter = save ? copyState(doc.mode, state) : null
1559
+ ++pos
1560
+ })
1561
+ if (precise) { doc.frontier = pos }
1562
+ return state
1563
+ }
1564
+
1565
+ // Lightweight form of highlight -- proceed over this line and
1566
+ // update state, but don't save a style array. Used for lines that
1567
+ // aren't currently visible.
1568
+ function processLine(cm, text, state, startAt) {
1569
+ var mode = cm.doc.mode
1570
+ var stream = new StringStream(text, cm.options.tabSize)
1571
+ stream.start = stream.pos = startAt || 0
1572
+ if (text == "") { callBlankLine(mode, state) }
1573
+ while (!stream.eol()) {
1574
+ readToken(mode, stream, state)
1575
+ stream.start = stream.pos
1576
+ }
1577
+ }
1578
+
1579
+ function callBlankLine(mode, state) {
1580
+ if (mode.blankLine) { return mode.blankLine(state) }
1581
+ if (!mode.innerMode) { return }
1582
+ var inner = innerMode(mode, state)
1583
+ if (inner.mode.blankLine) { return inner.mode.blankLine(inner.state) }
1584
+ }
1585
+
1586
+ function readToken(mode, stream, state, inner) {
1587
+ for (var i = 0; i < 10; i++) {
1588
+ if (inner) { inner[0] = innerMode(mode, state).mode }
1589
+ var style = mode.token(stream, state)
1590
+ if (stream.pos > stream.start) { return style }
1591
+ }
1592
+ throw new Error("Mode " + mode.name + " failed to advance stream.")
1593
+ }
1594
+
1595
+ // Utility for getTokenAt and getLineTokens
1596
+ function takeToken(cm, pos, precise, asArray) {
1597
+ var getObj = function (copy) { return ({
1598
+ start: stream.start, end: stream.pos,
1599
+ string: stream.current(),
1600
+ type: style || null,
1601
+ state: copy ? copyState(doc.mode, state) : state
1602
+ }); }
1603
+
1604
+ var doc = cm.doc, mode = doc.mode, style
1605
+ pos = clipPos(doc, pos)
1606
+ var line = getLine(doc, pos.line), state = getStateBefore(cm, pos.line, precise)
1607
+ var stream = new StringStream(line.text, cm.options.tabSize), tokens
1608
+ if (asArray) { tokens = [] }
1609
+ while ((asArray || stream.pos < pos.ch) && !stream.eol()) {
1610
+ stream.start = stream.pos
1611
+ style = readToken(mode, stream, state)
1612
+ if (asArray) { tokens.push(getObj(true)) }
1613
+ }
1614
+ return asArray ? tokens : getObj()
1615
+ }
1616
+
1617
+ function extractLineClasses(type, output) {
1618
+ if (type) { for (;;) {
1619
+ var lineClass = type.match(/(?:^|\s+)line-(background-)?(\S+)/)
1620
+ if (!lineClass) { break }
1621
+ type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length)
1622
+ var prop = lineClass[1] ? "bgClass" : "textClass"
1623
+ if (output[prop] == null)
1624
+ { output[prop] = lineClass[2] }
1625
+ else if (!(new RegExp("(?:^|\s)" + lineClass[2] + "(?:$|\s)")).test(output[prop]))
1626
+ { output[prop] += " " + lineClass[2] }
1627
+ } }
1628
+ return type
1629
+ }
1630
+
1631
+ // Run the given mode's parser over a line, calling f for each token.
1632
+ function runMode(cm, text, mode, state, f, lineClasses, forceToEnd) {
1633
+ var flattenSpans = mode.flattenSpans
1634
+ if (flattenSpans == null) { flattenSpans = cm.options.flattenSpans }
1635
+ var curStart = 0, curStyle = null
1636
+ var stream = new StringStream(text, cm.options.tabSize), style
1637
+ var inner = cm.options.addModeClass && [null]
1638
+ if (text == "") { extractLineClasses(callBlankLine(mode, state), lineClasses) }
1639
+ while (!stream.eol()) {
1640
+ if (stream.pos > cm.options.maxHighlightLength) {
1641
+ flattenSpans = false
1642
+ if (forceToEnd) { processLine(cm, text, state, stream.pos) }
1643
+ stream.pos = text.length
1644
+ style = null
1645
+ } else {
1646
+ style = extractLineClasses(readToken(mode, stream, state, inner), lineClasses)
1647
+ }
1648
+ if (inner) {
1649
+ var mName = inner[0].name
1650
+ if (mName) { style = "m-" + (style ? mName + " " + style : mName) }
1651
+ }
1652
+ if (!flattenSpans || curStyle != style) {
1653
+ while (curStart < stream.start) {
1654
+ curStart = Math.min(stream.start, curStart + 5000)
1655
+ f(curStart, curStyle)
1656
+ }
1657
+ curStyle = style
1658
+ }
1659
+ stream.start = stream.pos
1660
+ }
1661
+ while (curStart < stream.pos) {
1662
+ // Webkit seems to refuse to render text nodes longer than 57444
1663
+ // characters, and returns inaccurate measurements in nodes
1664
+ // starting around 5000 chars.
1665
+ var pos = Math.min(stream.pos, curStart + 5000)
1666
+ f(pos, curStyle)
1667
+ curStart = pos
1668
+ }
1669
+ }
1670
+
1671
+ // Finds the line to start with when starting a parse. Tries to
1672
+ // find a line with a stateAfter, so that it can start with a
1673
+ // valid state. If that fails, it returns the line with the
1674
+ // smallest indentation, which tends to need the least context to
1675
+ // parse correctly.
1676
+ function findStartLine(cm, n, precise) {
1677
+ var minindent, minline, doc = cm.doc
1678
+ var lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100)
1679
+ for (var search = n; search > lim; --search) {
1680
+ if (search <= doc.first) { return doc.first }
1681
+ var line = getLine(doc, search - 1)
1682
+ if (line.stateAfter && (!precise || search <= doc.frontier)) { return search }
1683
+ var indented = countColumn(line.text, null, cm.options.tabSize)
1684
+ if (minline == null || minindent > indented) {
1685
+ minline = search - 1
1686
+ minindent = indented
1687
+ }
1688
+ }
1689
+ return minline
1690
+ }
1691
+
1692
+ // LINE DATA STRUCTURE
1693
+
1694
+ // Line objects. These hold state related to a line, including
1695
+ // highlighting info (the styles array).
1696
+ function Line(text, markedSpans, estimateHeight) {
1697
+ this.text = text
1698
+ attachMarkedSpans(this, markedSpans)
1699
+ this.height = estimateHeight ? estimateHeight(this) : 1
1700
+ }
1701
+ eventMixin(Line)
1702
+ Line.prototype.lineNo = function() { return lineNo(this) }
1703
+
1704
+ // Change the content (text, markers) of a line. Automatically
1705
+ // invalidates cached information and tries to re-estimate the
1706
+ // line's height.
1707
+ function updateLine(line, text, markedSpans, estimateHeight) {
1708
+ line.text = text
1709
+ if (line.stateAfter) { line.stateAfter = null }
1710
+ if (line.styles) { line.styles = null }
1711
+ if (line.order != null) { line.order = null }
1712
+ detachMarkedSpans(line)
1713
+ attachMarkedSpans(line, markedSpans)
1714
+ var estHeight = estimateHeight ? estimateHeight(line) : 1
1715
+ if (estHeight != line.height) { updateLineHeight(line, estHeight) }
1716
+ }
1717
+
1718
+ // Detach a line from the document tree and its markers.
1719
+ function cleanUpLine(line) {
1720
+ line.parent = null
1721
+ detachMarkedSpans(line)
1722
+ }
1723
+
1724
+ // Convert a style as returned by a mode (either null, or a string
1725
+ // containing one or more styles) to a CSS style. This is cached,
1726
+ // and also looks for line-wide styles.
1727
+ var styleToClassCache = {};
1728
+ var styleToClassCacheWithMode = {}
1729
+ function interpretTokenStyle(style, options) {
1730
+ if (!style || /^\s*$/.test(style)) { return null }
1731
+ var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache
1732
+ return cache[style] ||
1733
+ (cache[style] = style.replace(/\S+/g, "cm-$&"))
1734
+ }
1735
+
1736
+ // Render the DOM representation of the text of a line. Also builds
1737
+ // up a 'line map', which points at the DOM nodes that represent
1738
+ // specific stretches of text, and is used by the measuring code.
1739
+ // The returned object contains the DOM node, this map, and
1740
+ // information about line-wide styles that were set by the mode.
1741
+ function buildLineContent(cm, lineView) {
1742
+ // The padding-right forces the element to have a 'border', which
1743
+ // is needed on Webkit to be able to get line-level bounding
1744
+ // rectangles for it (in measureChar).
1745
+ var content = elt("span", null, null, webkit ? "padding-right: .1px" : null)
1746
+ var builder = {pre: elt("pre", [content], "CodeMirror-line"), content: content,
1747
+ col: 0, pos: 0, cm: cm,
1748
+ trailingSpace: false,
1749
+ splitSpaces: (ie || webkit) && cm.getOption("lineWrapping")}
1750
+ lineView.measure = {}
1751
+
1752
+ // Iterate over the logical lines that make up this visual line.
1753
+ for (var i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) {
1754
+ var line = i ? lineView.rest[i - 1] : lineView.line, order = void 0
1755
+ builder.pos = 0
1756
+ builder.addToken = buildToken
1757
+ // Optionally wire in some hacks into the token-rendering
1758
+ // algorithm, to deal with browser quirks.
1759
+ if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line)))
1760
+ { builder.addToken = buildTokenBadBidi(builder.addToken, order) }
1761
+ builder.map = []
1762
+ var allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line)
1763
+ insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate))
1764
+ if (line.styleClasses) {
1765
+ if (line.styleClasses.bgClass)
1766
+ { builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || "") }
1767
+ if (line.styleClasses.textClass)
1768
+ { builder.textClass = joinClasses(line.styleClasses.textClass, builder.textClass || "") }
1769
+ }
1770
+
1771
+ // Ensure at least a single node is present, for measuring.
1772
+ if (builder.map.length == 0)
1773
+ { builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.display.measure))) }
1774
+
1775
+ // Store the map and a cache object for the current logical line
1776
+ if (i == 0) {
1777
+ lineView.measure.map = builder.map
1778
+ lineView.measure.cache = {}
1779
+ } else {
1780
+ (lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map)
1781
+ ;(lineView.measure.caches || (lineView.measure.caches = [])).push({})
1782
+ }
1783
+ }
1784
+
1785
+ // See issue #2901
1786
+ if (webkit) {
1787
+ var last = builder.content.lastChild
1788
+ if (/\bcm-tab\b/.test(last.className) || (last.querySelector && last.querySelector(".cm-tab")))
1789
+ { builder.content.className = "cm-tab-wrap-hack" }
1790
+ }
1791
+
1792
+ signal(cm, "renderLine", cm, lineView.line, builder.pre)
1793
+ if (builder.pre.className)
1794
+ { builder.textClass = joinClasses(builder.pre.className, builder.textClass || "") }
1795
+
1796
+ return builder
1797
+ }
1798
+
1799
+ function defaultSpecialCharPlaceholder(ch) {
1800
+ var token = elt("span", "\u2022", "cm-invalidchar")
1801
+ token.title = "\\u" + ch.charCodeAt(0).toString(16)
1802
+ token.setAttribute("aria-label", token.title)
1803
+ return token
1804
+ }
1805
+
1806
+ // Build up the DOM representation for a single token, and add it to
1807
+ // the line map. Takes care to render special characters separately.
1808
+ function buildToken(builder, text, style, startStyle, endStyle, title, css) {
1809
+ if (!text) { return }
1810
+ var displayText = builder.splitSpaces ? splitSpaces(text, builder.trailingSpace) : text
1811
+ var special = builder.cm.state.specialChars, mustWrap = false
1812
+ var content
1813
+ if (!special.test(text)) {
1814
+ builder.col += text.length
1815
+ content = document.createTextNode(displayText)
1816
+ builder.map.push(builder.pos, builder.pos + text.length, content)
1817
+ if (ie && ie_version < 9) { mustWrap = true }
1818
+ builder.pos += text.length
1819
+ } else {
1820
+ content = document.createDocumentFragment()
1821
+ var pos = 0
1822
+ while (true) {
1823
+ special.lastIndex = pos
1824
+ var m = special.exec(text)
1825
+ var skipped = m ? m.index - pos : text.length - pos
1826
+ if (skipped) {
1827
+ var txt = document.createTextNode(displayText.slice(pos, pos + skipped))
1828
+ if (ie && ie_version < 9) { content.appendChild(elt("span", [txt])) }
1829
+ else { content.appendChild(txt) }
1830
+ builder.map.push(builder.pos, builder.pos + skipped, txt)
1831
+ builder.col += skipped
1832
+ builder.pos += skipped
1833
+ }
1834
+ if (!m) { break }
1835
+ pos += skipped + 1
1836
+ var txt$1 = void 0
1837
+ if (m[0] == "\t") {
1838
+ var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize
1839
+ txt$1 = content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab"))
1840
+ txt$1.setAttribute("role", "presentation")
1841
+ txt$1.setAttribute("cm-text", "\t")
1842
+ builder.col += tabWidth
1843
+ } else if (m[0] == "\r" || m[0] == "\n") {
1844
+ txt$1 = content.appendChild(elt("span", m[0] == "\r" ? "\u240d" : "\u2424", "cm-invalidchar"))
1845
+ txt$1.setAttribute("cm-text", m[0])
1846
+ builder.col += 1
1847
+ } else {
1848
+ txt$1 = builder.cm.options.specialCharPlaceholder(m[0])
1849
+ txt$1.setAttribute("cm-text", m[0])
1850
+ if (ie && ie_version < 9) { content.appendChild(elt("span", [txt$1])) }
1851
+ else { content.appendChild(txt$1) }
1852
+ builder.col += 1
1853
+ }
1854
+ builder.map.push(builder.pos, builder.pos + 1, txt$1)
1855
+ builder.pos++
1856
+ }
1857
+ }
1858
+ builder.trailingSpace = displayText.charCodeAt(text.length - 1) == 32
1859
+ if (style || startStyle || endStyle || mustWrap || css) {
1860
+ var fullStyle = style || ""
1861
+ if (startStyle) { fullStyle += startStyle }
1862
+ if (endStyle) { fullStyle += endStyle }
1863
+ var token = elt("span", [content], fullStyle, css)
1864
+ if (title) { token.title = title }
1865
+ return builder.content.appendChild(token)
1866
+ }
1867
+ builder.content.appendChild(content)
1868
+ }
1869
+
1870
+ function splitSpaces(text, trailingBefore) {
1871
+ if (text.length > 1 && !/ /.test(text)) { return text }
1872
+ var spaceBefore = trailingBefore, result = ""
1873
+ for (var i = 0; i < text.length; i++) {
1874
+ var ch = text.charAt(i)
1875
+ if (ch == " " && spaceBefore && (i == text.length - 1 || text.charCodeAt(i + 1) == 32))
1876
+ { ch = "\u00a0" }
1877
+ result += ch
1878
+ spaceBefore = ch == " "
1879
+ }
1880
+ return result
1881
+ }
1882
+
1883
+ // Work around nonsense dimensions being reported for stretches of
1884
+ // right-to-left text.
1885
+ function buildTokenBadBidi(inner, order) {
1886
+ return function (builder, text, style, startStyle, endStyle, title, css) {
1887
+ style = style ? style + " cm-force-border" : "cm-force-border"
1888
+ var start = builder.pos, end = start + text.length
1889
+ for (;;) {
1890
+ // Find the part that overlaps with the start of this text
1891
+ var part = void 0
1892
+ for (var i = 0; i < order.length; i++) {
1893
+ part = order[i]
1894
+ if (part.to > start && part.from <= start) { break }
1895
+ }
1896
+ if (part.to >= end) { return inner(builder, text, style, startStyle, endStyle, title, css) }
1897
+ inner(builder, text.slice(0, part.to - start), style, startStyle, null, title, css)
1898
+ startStyle = null
1899
+ text = text.slice(part.to - start)
1900
+ start = part.to
1901
+ }
1902
+ }
1903
+ }
1904
+
1905
+ function buildCollapsedSpan(builder, size, marker, ignoreWidget) {
1906
+ var widget = !ignoreWidget && marker.widgetNode
1907
+ if (widget) { builder.map.push(builder.pos, builder.pos + size, widget) }
1908
+ if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) {
1909
+ if (!widget)
1910
+ { widget = builder.content.appendChild(document.createElement("span")) }
1911
+ widget.setAttribute("cm-marker", marker.id)
1912
+ }
1913
+ if (widget) {
1914
+ builder.cm.display.input.setUneditable(widget)
1915
+ builder.content.appendChild(widget)
1916
+ }
1917
+ builder.pos += size
1918
+ builder.trailingSpace = false
1919
+ }
1920
+
1921
+ // Outputs a number of spans to make up a line, taking highlighting
1922
+ // and marked text into account.
1923
+ function insertLineContent(line, builder, styles) {
1924
+ var spans = line.markedSpans, allText = line.text, at = 0
1925
+ if (!spans) {
1926
+ for (var i$1 = 1; i$1 < styles.length; i$1+=2)
1927
+ { builder.addToken(builder, allText.slice(at, at = styles[i$1]), interpretTokenStyle(styles[i$1+1], builder.cm.options)) }
1928
+ return
1929
+ }
1930
+
1931
+ var len = allText.length, pos = 0, i = 1, text = "", style, css
1932
+ var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, title, collapsed
1933
+ for (;;) {
1934
+ if (nextChange == pos) { // Update current marker set
1935
+ spanStyle = spanEndStyle = spanStartStyle = title = css = ""
1936
+ collapsed = null; nextChange = Infinity
1937
+ var foundBookmarks = [], endStyles = void 0
1938
+ for (var j = 0; j < spans.length; ++j) {
1939
+ var sp = spans[j], m = sp.marker
1940
+ if (m.type == "bookmark" && sp.from == pos && m.widgetNode) {
1941
+ foundBookmarks.push(m)
1942
+ } else if (sp.from <= pos && (sp.to == null || sp.to > pos || m.collapsed && sp.to == pos && sp.from == pos)) {
1943
+ if (sp.to != null && sp.to != pos && nextChange > sp.to) {
1944
+ nextChange = sp.to
1945
+ spanEndStyle = ""
1946
+ }
1947
+ if (m.className) { spanStyle += " " + m.className }
1948
+ if (m.css) { css = (css ? css + ";" : "") + m.css }
1949
+ if (m.startStyle && sp.from == pos) { spanStartStyle += " " + m.startStyle }
1950
+ if (m.endStyle && sp.to == nextChange) { (endStyles || (endStyles = [])).push(m.endStyle, sp.to) }
1951
+ if (m.title && !title) { title = m.title }
1952
+ if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0))
1953
+ { collapsed = sp }
1954
+ } else if (sp.from > pos && nextChange > sp.from) {
1955
+ nextChange = sp.from
1956
+ }
1957
+ }
1958
+ if (endStyles) { for (var j$1 = 0; j$1 < endStyles.length; j$1 += 2)
1959
+ { if (endStyles[j$1 + 1] == nextChange) { spanEndStyle += " " + endStyles[j$1] } } }
1960
+
1961
+ if (!collapsed || collapsed.from == pos) { for (var j$2 = 0; j$2 < foundBookmarks.length; ++j$2)
1962
+ { buildCollapsedSpan(builder, 0, foundBookmarks[j$2]) } }
1963
+ if (collapsed && (collapsed.from || 0) == pos) {
1964
+ buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos,
1965
+ collapsed.marker, collapsed.from == null)
1966
+ if (collapsed.to == null) { return }
1967
+ if (collapsed.to == pos) { collapsed = false }
1968
+ }
1969
+ }
1970
+ if (pos >= len) { break }
1971
+
1972
+ var upto = Math.min(len, nextChange)
1973
+ while (true) {
1974
+ if (text) {
1975
+ var end = pos + text.length
1976
+ if (!collapsed) {
1977
+ var tokenText = end > upto ? text.slice(0, upto - pos) : text
1978
+ builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle,
1979
+ spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", title, css)
1980
+ }
1981
+ if (end >= upto) {text = text.slice(upto - pos); pos = upto; break}
1982
+ pos = end
1983
+ spanStartStyle = ""
1984
+ }
1985
+ text = allText.slice(at, at = styles[i++])
1986
+ style = interpretTokenStyle(styles[i++], builder.cm.options)
1987
+ }
1988
+ }
1989
+ }
1990
+
1991
+
1992
+ // These objects are used to represent the visible (currently drawn)
1993
+ // part of the document. A LineView may correspond to multiple
1994
+ // logical lines, if those are connected by collapsed ranges.
1995
+ function LineView(doc, line, lineN) {
1996
+ // The starting line
1997
+ this.line = line
1998
+ // Continuing lines, if any
1999
+ this.rest = visualLineContinued(line)
2000
+ // Number of logical lines in this visual line
2001
+ this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1
2002
+ this.node = this.text = null
2003
+ this.hidden = lineIsHidden(doc, line)
2004
+ }
2005
+
2006
+ // Create a range of LineView objects for the given lines.
2007
+ function buildViewArray(cm, from, to) {
2008
+ var array = [], nextPos
2009
+ for (var pos = from; pos < to; pos = nextPos) {
2010
+ var view = new LineView(cm.doc, getLine(cm.doc, pos), pos)
2011
+ nextPos = pos + view.size
2012
+ array.push(view)
2013
+ }
2014
+ return array
2015
+ }
2016
+
2017
+ var operationGroup = null
2018
+
2019
+ function pushOperation(op) {
2020
+ if (operationGroup) {
2021
+ operationGroup.ops.push(op)
2022
+ } else {
2023
+ op.ownsGroup = operationGroup = {
2024
+ ops: [op],
2025
+ delayedCallbacks: []
2026
+ }
2027
+ }
2028
+ }
2029
+
2030
+ function fireCallbacksForOps(group) {
2031
+ // Calls delayed callbacks and cursorActivity handlers until no
2032
+ // new ones appear
2033
+ var callbacks = group.delayedCallbacks, i = 0
2034
+ do {
2035
+ for (; i < callbacks.length; i++)
2036
+ { callbacks[i].call(null) }
2037
+ for (var j = 0; j < group.ops.length; j++) {
2038
+ var op = group.ops[j]
2039
+ if (op.cursorActivityHandlers)
2040
+ { while (op.cursorActivityCalled < op.cursorActivityHandlers.length)
2041
+ { op.cursorActivityHandlers[op.cursorActivityCalled++].call(null, op.cm) } }
2042
+ }
2043
+ } while (i < callbacks.length)
2044
+ }
2045
+
2046
+ function finishOperation(op, endCb) {
2047
+ var group = op.ownsGroup
2048
+ if (!group) { return }
2049
+
2050
+ try { fireCallbacksForOps(group) }
2051
+ finally {
2052
+ operationGroup = null
2053
+ endCb(group)
2054
+ }
2055
+ }
2056
+
2057
+ var orphanDelayedCallbacks = null
2058
+
2059
+ // Often, we want to signal events at a point where we are in the
2060
+ // middle of some work, but don't want the handler to start calling
2061
+ // other methods on the editor, which might be in an inconsistent
2062
+ // state or simply not expect any other events to happen.
2063
+ // signalLater looks whether there are any handlers, and schedules
2064
+ // them to be executed when the last operation ends, or, if no
2065
+ // operation is active, when a timeout fires.
2066
+ function signalLater(emitter, type /*, values...*/) {
2067
+ var arr = getHandlers(emitter, type)
2068
+ if (!arr.length) { return }
2069
+ var args = Array.prototype.slice.call(arguments, 2), list
2070
+ if (operationGroup) {
2071
+ list = operationGroup.delayedCallbacks
2072
+ } else if (orphanDelayedCallbacks) {
2073
+ list = orphanDelayedCallbacks
2074
+ } else {
2075
+ list = orphanDelayedCallbacks = []
2076
+ setTimeout(fireOrphanDelayed, 0)
2077
+ }
2078
+ var loop = function ( i ) {
2079
+ list.push(function () { return arr[i].apply(null, args); })
2080
+ };
2081
+
2082
+ for (var i = 0; i < arr.length; ++i)
2083
+ loop( i );
2084
+ }
2085
+
2086
+ function fireOrphanDelayed() {
2087
+ var delayed = orphanDelayedCallbacks
2088
+ orphanDelayedCallbacks = null
2089
+ for (var i = 0; i < delayed.length; ++i) { delayed[i]() }
2090
+ }
2091
+
2092
+ // When an aspect of a line changes, a string is added to
2093
+ // lineView.changes. This updates the relevant part of the line's
2094
+ // DOM structure.
2095
+ function updateLineForChanges(cm, lineView, lineN, dims) {
2096
+ for (var j = 0; j < lineView.changes.length; j++) {
2097
+ var type = lineView.changes[j]
2098
+ if (type == "text") { updateLineText(cm, lineView) }
2099
+ else if (type == "gutter") { updateLineGutter(cm, lineView, lineN, dims) }
2100
+ else if (type == "class") { updateLineClasses(lineView) }
2101
+ else if (type == "widget") { updateLineWidgets(cm, lineView, dims) }
2102
+ }
2103
+ lineView.changes = null
2104
+ }
2105
+
2106
+ // Lines with gutter elements, widgets or a background class need to
2107
+ // be wrapped, and have the extra elements added to the wrapper div
2108
+ function ensureLineWrapped(lineView) {
2109
+ if (lineView.node == lineView.text) {
2110
+ lineView.node = elt("div", null, null, "position: relative")
2111
+ if (lineView.text.parentNode)
2112
+ { lineView.text.parentNode.replaceChild(lineView.node, lineView.text) }
2113
+ lineView.node.appendChild(lineView.text)
2114
+ if (ie && ie_version < 8) { lineView.node.style.zIndex = 2 }
2115
+ }
2116
+ return lineView.node
2117
+ }
2118
+
2119
+ function updateLineBackground(lineView) {
2120
+ var cls = lineView.bgClass ? lineView.bgClass + " " + (lineView.line.bgClass || "") : lineView.line.bgClass
2121
+ if (cls) { cls += " CodeMirror-linebackground" }
2122
+ if (lineView.background) {
2123
+ if (cls) { lineView.background.className = cls }
2124
+ else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null }
2125
+ } else if (cls) {
2126
+ var wrap = ensureLineWrapped(lineView)
2127
+ lineView.background = wrap.insertBefore(elt("div", null, cls), wrap.firstChild)
2128
+ }
2129
+ }
2130
+
2131
+ // Wrapper around buildLineContent which will reuse the structure
2132
+ // in display.externalMeasured when possible.
2133
+ function getLineContent(cm, lineView) {
2134
+ var ext = cm.display.externalMeasured
2135
+ if (ext && ext.line == lineView.line) {
2136
+ cm.display.externalMeasured = null
2137
+ lineView.measure = ext.measure
2138
+ return ext.built
2139
+ }
2140
+ return buildLineContent(cm, lineView)
2141
+ }
2142
+
2143
+ // Redraw the line's text. Interacts with the background and text
2144
+ // classes because the mode may output tokens that influence these
2145
+ // classes.
2146
+ function updateLineText(cm, lineView) {
2147
+ var cls = lineView.text.className
2148
+ var built = getLineContent(cm, lineView)
2149
+ if (lineView.text == lineView.node) { lineView.node = built.pre }
2150
+ lineView.text.parentNode.replaceChild(built.pre, lineView.text)
2151
+ lineView.text = built.pre
2152
+ if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) {
2153
+ lineView.bgClass = built.bgClass
2154
+ lineView.textClass = built.textClass
2155
+ updateLineClasses(lineView)
2156
+ } else if (cls) {
2157
+ lineView.text.className = cls
2158
+ }
2159
+ }
2160
+
2161
+ function updateLineClasses(lineView) {
2162
+ updateLineBackground(lineView)
2163
+ if (lineView.line.wrapClass)
2164
+ { ensureLineWrapped(lineView).className = lineView.line.wrapClass }
2165
+ else if (lineView.node != lineView.text)
2166
+ { lineView.node.className = "" }
2167
+ var textClass = lineView.textClass ? lineView.textClass + " " + (lineView.line.textClass || "") : lineView.line.textClass
2168
+ lineView.text.className = textClass || ""
2169
+ }
2170
+
2171
+ function updateLineGutter(cm, lineView, lineN, dims) {
2172
+ if (lineView.gutter) {
2173
+ lineView.node.removeChild(lineView.gutter)
2174
+ lineView.gutter = null
2175
+ }
2176
+ if (lineView.gutterBackground) {
2177
+ lineView.node.removeChild(lineView.gutterBackground)
2178
+ lineView.gutterBackground = null
2179
+ }
2180
+ if (lineView.line.gutterClass) {
2181
+ var wrap = ensureLineWrapped(lineView)
2182
+ lineView.gutterBackground = elt("div", null, "CodeMirror-gutter-background " + lineView.line.gutterClass,
2183
+ ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px; width: " + (dims.gutterTotalWidth) + "px"))
2184
+ wrap.insertBefore(lineView.gutterBackground, lineView.text)
2185
+ }
2186
+ var markers = lineView.line.gutterMarkers
2187
+ if (cm.options.lineNumbers || markers) {
2188
+ var wrap$1 = ensureLineWrapped(lineView)
2189
+ var gutterWrap = lineView.gutter = elt("div", null, "CodeMirror-gutter-wrapper", ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px"))
2190
+ cm.display.input.setUneditable(gutterWrap)
2191
+ wrap$1.insertBefore(gutterWrap, lineView.text)
2192
+ if (lineView.line.gutterClass)
2193
+ { gutterWrap.className += " " + lineView.line.gutterClass }
2194
+ if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"]))
2195
+ { lineView.lineNumber = gutterWrap.appendChild(
2196
+ elt("div", lineNumberFor(cm.options, lineN),
2197
+ "CodeMirror-linenumber CodeMirror-gutter-elt",
2198
+ ("left: " + (dims.gutterLeft["CodeMirror-linenumbers"]) + "px; width: " + (cm.display.lineNumInnerWidth) + "px"))) }
2199
+ if (markers) { for (var k = 0; k < cm.options.gutters.length; ++k) {
2200
+ var id = cm.options.gutters[k], found = markers.hasOwnProperty(id) && markers[id]
2201
+ if (found)
2202
+ { gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt",
2203
+ ("left: " + (dims.gutterLeft[id]) + "px; width: " + (dims.gutterWidth[id]) + "px"))) }
2204
+ } }
2205
+ }
2206
+ }
2207
+
2208
+ function updateLineWidgets(cm, lineView, dims) {
2209
+ if (lineView.alignable) { lineView.alignable = null }
2210
+ for (var node = lineView.node.firstChild, next = void 0; node; node = next) {
2211
+ next = node.nextSibling
2212
+ if (node.className == "CodeMirror-linewidget")
2213
+ { lineView.node.removeChild(node) }
2214
+ }
2215
+ insertLineWidgets(cm, lineView, dims)
2216
+ }
2217
+
2218
+ // Build a line's DOM representation from scratch
2219
+ function buildLineElement(cm, lineView, lineN, dims) {
2220
+ var built = getLineContent(cm, lineView)
2221
+ lineView.text = lineView.node = built.pre
2222
+ if (built.bgClass) { lineView.bgClass = built.bgClass }
2223
+ if (built.textClass) { lineView.textClass = built.textClass }
2224
+
2225
+ updateLineClasses(lineView)
2226
+ updateLineGutter(cm, lineView, lineN, dims)
2227
+ insertLineWidgets(cm, lineView, dims)
2228
+ return lineView.node
2229
+ }
2230
+
2231
+ // A lineView may contain multiple logical lines (when merged by
2232
+ // collapsed spans). The widgets for all of them need to be drawn.
2233
+ function insertLineWidgets(cm, lineView, dims) {
2234
+ insertLineWidgetsFor(cm, lineView.line, lineView, dims, true)
2235
+ if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++)
2236
+ { insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false) } }
2237
+ }
2238
+
2239
+ function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) {
2240
+ if (!line.widgets) { return }
2241
+ var wrap = ensureLineWrapped(lineView)
2242
+ for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
2243
+ var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget")
2244
+ if (!widget.handleMouseEvents) { node.setAttribute("cm-ignore-events", "true") }
2245
+ positionLineWidget(widget, node, lineView, dims)
2246
+ cm.display.input.setUneditable(node)
2247
+ if (allowAbove && widget.above)
2248
+ { wrap.insertBefore(node, lineView.gutter || lineView.text) }
2249
+ else
2250
+ { wrap.appendChild(node) }
2251
+ signalLater(widget, "redraw")
2252
+ }
2253
+ }
2254
+
2255
+ function positionLineWidget(widget, node, lineView, dims) {
2256
+ if (widget.noHScroll) {
2257
+ (lineView.alignable || (lineView.alignable = [])).push(node)
2258
+ var width = dims.wrapperWidth
2259
+ node.style.left = dims.fixedPos + "px"
2260
+ if (!widget.coverGutter) {
2261
+ width -= dims.gutterTotalWidth
2262
+ node.style.paddingLeft = dims.gutterTotalWidth + "px"
2263
+ }
2264
+ node.style.width = width + "px"
2265
+ }
2266
+ if (widget.coverGutter) {
2267
+ node.style.zIndex = 5
2268
+ node.style.position = "relative"
2269
+ if (!widget.noHScroll) { node.style.marginLeft = -dims.gutterTotalWidth + "px" }
2270
+ }
2271
+ }
2272
+
2273
+ function widgetHeight(widget) {
2274
+ if (widget.height != null) { return widget.height }
2275
+ var cm = widget.doc.cm
2276
+ if (!cm) { return 0 }
2277
+ if (!contains(document.body, widget.node)) {
2278
+ var parentStyle = "position: relative;"
2279
+ if (widget.coverGutter)
2280
+ { parentStyle += "margin-left: -" + cm.display.gutters.offsetWidth + "px;" }
2281
+ if (widget.noHScroll)
2282
+ { parentStyle += "width: " + cm.display.wrapper.clientWidth + "px;" }
2283
+ removeChildrenAndAdd(cm.display.measure, elt("div", [widget.node], null, parentStyle))
2284
+ }
2285
+ return widget.height = widget.node.parentNode.offsetHeight
2286
+ }
2287
+
2288
+ // Return true when the given mouse event happened in a widget
2289
+ function eventInWidget(display, e) {
2290
+ for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {
2291
+ if (!n || (n.nodeType == 1 && n.getAttribute("cm-ignore-events") == "true") ||
2292
+ (n.parentNode == display.sizer && n != display.mover))
2293
+ { return true }
2294
+ }
2295
+ }
2296
+
2297
+ // POSITION MEASUREMENT
2298
+
2299
+ function paddingTop(display) {return display.lineSpace.offsetTop}
2300
+ function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight}
2301
+ function paddingH(display) {
2302
+ if (display.cachedPaddingH) { return display.cachedPaddingH }
2303
+ var e = removeChildrenAndAdd(display.measure, elt("pre", "x"))
2304
+ var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle
2305
+ var data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)}
2306
+ if (!isNaN(data.left) && !isNaN(data.right)) { display.cachedPaddingH = data }
2307
+ return data
2308
+ }
2309
+
2310
+ function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth }
2311
+ function displayWidth(cm) {
2312
+ return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth
2313
+ }
2314
+ function displayHeight(cm) {
2315
+ return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeight
2316
+ }
2317
+
2318
+ // Ensure the lineView.wrapping.heights array is populated. This is
2319
+ // an array of bottom offsets for the lines that make up a drawn
2320
+ // line. When lineWrapping is on, there might be more than one
2321
+ // height.
2322
+ function ensureLineHeights(cm, lineView, rect) {
2323
+ var wrapping = cm.options.lineWrapping
2324
+ var curWidth = wrapping && displayWidth(cm)
2325
+ if (!lineView.measure.heights || wrapping && lineView.measure.width != curWidth) {
2326
+ var heights = lineView.measure.heights = []
2327
+ if (wrapping) {
2328
+ lineView.measure.width = curWidth
2329
+ var rects = lineView.text.firstChild.getClientRects()
2330
+ for (var i = 0; i < rects.length - 1; i++) {
2331
+ var cur = rects[i], next = rects[i + 1]
2332
+ if (Math.abs(cur.bottom - next.bottom) > 2)
2333
+ { heights.push((cur.bottom + next.top) / 2 - rect.top) }
2334
+ }
2335
+ }
2336
+ heights.push(rect.bottom - rect.top)
2337
+ }
2338
+ }
2339
+
2340
+ // Find a line map (mapping character offsets to text nodes) and a
2341
+ // measurement cache for the given line number. (A line view might
2342
+ // contain multiple lines when collapsed ranges are present.)
2343
+ function mapFromLineView(lineView, line, lineN) {
2344
+ if (lineView.line == line)
2345
+ { return {map: lineView.measure.map, cache: lineView.measure.cache} }
2346
+ for (var i = 0; i < lineView.rest.length; i++)
2347
+ { if (lineView.rest[i] == line)
2348
+ { return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]} } }
2349
+ for (var i$1 = 0; i$1 < lineView.rest.length; i$1++)
2350
+ { if (lineNo(lineView.rest[i$1]) > lineN)
2351
+ { return {map: lineView.measure.maps[i$1], cache: lineView.measure.caches[i$1], before: true} } }
2352
+ }
2353
+
2354
+ // Render a line into the hidden node display.externalMeasured. Used
2355
+ // when measurement is needed for a line that's not in the viewport.
2356
+ function updateExternalMeasurement(cm, line) {
2357
+ line = visualLine(line)
2358
+ var lineN = lineNo(line)
2359
+ var view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN)
2360
+ view.lineN = lineN
2361
+ var built = view.built = buildLineContent(cm, view)
2362
+ view.text = built.pre
2363
+ removeChildrenAndAdd(cm.display.lineMeasure, built.pre)
2364
+ return view
2365
+ }
2366
+
2367
+ // Get a {top, bottom, left, right} box (in line-local coordinates)
2368
+ // for a given character.
2369
+ function measureChar(cm, line, ch, bias) {
2370
+ return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias)
2371
+ }
2372
+
2373
+ // Find a line view that corresponds to the given line number.
2374
+ function findViewForLine(cm, lineN) {
2375
+ if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo)
2376
+ { return cm.display.view[findViewIndex(cm, lineN)] }
2377
+ var ext = cm.display.externalMeasured
2378
+ if (ext && lineN >= ext.lineN && lineN < ext.lineN + ext.size)
2379
+ { return ext }
2380
+ }
2381
+
2382
+ // Measurement can be split in two steps, the set-up work that
2383
+ // applies to the whole line, and the measurement of the actual
2384
+ // character. Functions like coordsChar, that need to do a lot of
2385
+ // measurements in a row, can thus ensure that the set-up work is
2386
+ // only done once.
2387
+ function prepareMeasureForLine(cm, line) {
2388
+ var lineN = lineNo(line)
2389
+ var view = findViewForLine(cm, lineN)
2390
+ if (view && !view.text) {
2391
+ view = null
2392
+ } else if (view && view.changes) {
2393
+ updateLineForChanges(cm, view, lineN, getDimensions(cm))
2394
+ cm.curOp.forceUpdate = true
2395
+ }
2396
+ if (!view)
2397
+ { view = updateExternalMeasurement(cm, line) }
2398
+
2399
+ var info = mapFromLineView(view, line, lineN)
2400
+ return {
2401
+ line: line, view: view, rect: null,
2402
+ map: info.map, cache: info.cache, before: info.before,
2403
+ hasHeights: false
2404
+ }
2405
+ }
2406
+
2407
+ // Given a prepared measurement object, measures the position of an
2408
+ // actual character (or fetches it from the cache).
2409
+ function measureCharPrepared(cm, prepared, ch, bias, varHeight) {
2410
+ if (prepared.before) { ch = -1 }
2411
+ var key = ch + (bias || ""), found
2412
+ if (prepared.cache.hasOwnProperty(key)) {
2413
+ found = prepared.cache[key]
2414
+ } else {
2415
+ if (!prepared.rect)
2416
+ { prepared.rect = prepared.view.text.getBoundingClientRect() }
2417
+ if (!prepared.hasHeights) {
2418
+ ensureLineHeights(cm, prepared.view, prepared.rect)
2419
+ prepared.hasHeights = true
2420
+ }
2421
+ found = measureCharInner(cm, prepared, ch, bias)
2422
+ if (!found.bogus) { prepared.cache[key] = found }
2423
+ }
2424
+ return {left: found.left, right: found.right,
2425
+ top: varHeight ? found.rtop : found.top,
2426
+ bottom: varHeight ? found.rbottom : found.bottom}
2427
+ }
2428
+
2429
+ var nullRect = {left: 0, right: 0, top: 0, bottom: 0}
2430
+
2431
+ function nodeAndOffsetInLineMap(map$$1, ch, bias) {
2432
+ var node, start, end, collapse, mStart, mEnd
2433
+ // First, search the line map for the text node corresponding to,
2434
+ // or closest to, the target character.
2435
+ for (var i = 0; i < map$$1.length; i += 3) {
2436
+ mStart = map$$1[i]
2437
+ mEnd = map$$1[i + 1]
2438
+ if (ch < mStart) {
2439
+ start = 0; end = 1
2440
+ collapse = "left"
2441
+ } else if (ch < mEnd) {
2442
+ start = ch - mStart
2443
+ end = start + 1
2444
+ } else if (i == map$$1.length - 3 || ch == mEnd && map$$1[i + 3] > ch) {
2445
+ end = mEnd - mStart
2446
+ start = end - 1
2447
+ if (ch >= mEnd) { collapse = "right" }
2448
+ }
2449
+ if (start != null) {
2450
+ node = map$$1[i + 2]
2451
+ if (mStart == mEnd && bias == (node.insertLeft ? "left" : "right"))
2452
+ { collapse = bias }
2453
+ if (bias == "left" && start == 0)
2454
+ { while (i && map$$1[i - 2] == map$$1[i - 3] && map$$1[i - 1].insertLeft) {
2455
+ node = map$$1[(i -= 3) + 2]
2456
+ collapse = "left"
2457
+ } }
2458
+ if (bias == "right" && start == mEnd - mStart)
2459
+ { while (i < map$$1.length - 3 && map$$1[i + 3] == map$$1[i + 4] && !map$$1[i + 5].insertLeft) {
2460
+ node = map$$1[(i += 3) + 2]
2461
+ collapse = "right"
2462
+ } }
2463
+ break
2464
+ }
2465
+ }
2466
+ return {node: node, start: start, end: end, collapse: collapse, coverStart: mStart, coverEnd: mEnd}
2467
+ }
2468
+
2469
+ function getUsefulRect(rects, bias) {
2470
+ var rect = nullRect
2471
+ if (bias == "left") { for (var i = 0; i < rects.length; i++) {
2472
+ if ((rect = rects[i]).left != rect.right) { break }
2473
+ } } else { for (var i$1 = rects.length - 1; i$1 >= 0; i$1--) {
2474
+ if ((rect = rects[i$1]).left != rect.right) { break }
2475
+ } }
2476
+ return rect
2477
+ }
2478
+
2479
+ function measureCharInner(cm, prepared, ch, bias) {
2480
+ var place = nodeAndOffsetInLineMap(prepared.map, ch, bias)
2481
+ var node = place.node, start = place.start, end = place.end, collapse = place.collapse
2482
+
2483
+ var rect
2484
+ if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates.
2485
+ for (var i$1 = 0; i$1 < 4; i$1++) { // Retry a maximum of 4 times when nonsense rectangles are returned
2486
+ while (start && isExtendingChar(prepared.line.text.charAt(place.coverStart + start))) { --start }
2487
+ while (place.coverStart + end < place.coverEnd && isExtendingChar(prepared.line.text.charAt(place.coverStart + end))) { ++end }
2488
+ if (ie && ie_version < 9 && start == 0 && end == place.coverEnd - place.coverStart)
2489
+ { rect = node.parentNode.getBoundingClientRect() }
2490
+ else
2491
+ { rect = getUsefulRect(range(node, start, end).getClientRects(), bias) }
2492
+ if (rect.left || rect.right || start == 0) { break }
2493
+ end = start
2494
+ start = start - 1
2495
+ collapse = "right"
2496
+ }
2497
+ if (ie && ie_version < 11) { rect = maybeUpdateRectForZooming(cm.display.measure, rect) }
2498
+ } else { // If it is a widget, simply get the box for the whole widget.
2499
+ if (start > 0) { collapse = bias = "right" }
2500
+ var rects
2501
+ if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1)
2502
+ { rect = rects[bias == "right" ? rects.length - 1 : 0] }
2503
+ else
2504
+ { rect = node.getBoundingClientRect() }
2505
+ }
2506
+ if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) {
2507
+ var rSpan = node.parentNode.getClientRects()[0]
2508
+ if (rSpan)
2509
+ { rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top: rSpan.top, bottom: rSpan.bottom} }
2510
+ else
2511
+ { rect = nullRect }
2512
+ }
2513
+
2514
+ var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top
2515
+ var mid = (rtop + rbot) / 2
2516
+ var heights = prepared.view.measure.heights
2517
+ var i = 0
2518
+ for (; i < heights.length - 1; i++)
2519
+ { if (mid < heights[i]) { break } }
2520
+ var top = i ? heights[i - 1] : 0, bot = heights[i]
2521
+ var result = {left: (collapse == "right" ? rect.right : rect.left) - prepared.rect.left,
2522
+ right: (collapse == "left" ? rect.left : rect.right) - prepared.rect.left,
2523
+ top: top, bottom: bot}
2524
+ if (!rect.left && !rect.right) { result.bogus = true }
2525
+ if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbottom = rbot }
2526
+
2527
+ return result
2528
+ }
2529
+
2530
+ // Work around problem with bounding client rects on ranges being
2531
+ // returned incorrectly when zoomed on IE10 and below.
2532
+ function maybeUpdateRectForZooming(measure, rect) {
2533
+ if (!window.screen || screen.logicalXDPI == null ||
2534
+ screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure))
2535
+ { return rect }
2536
+ var scaleX = screen.logicalXDPI / screen.deviceXDPI
2537
+ var scaleY = screen.logicalYDPI / screen.deviceYDPI
2538
+ return {left: rect.left * scaleX, right: rect.right * scaleX,
2539
+ top: rect.top * scaleY, bottom: rect.bottom * scaleY}
2540
+ }
2541
+
2542
+ function clearLineMeasurementCacheFor(lineView) {
2543
+ if (lineView.measure) {
2544
+ lineView.measure.cache = {}
2545
+ lineView.measure.heights = null
2546
+ if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++)
2547
+ { lineView.measure.caches[i] = {} } }
2548
+ }
2549
+ }
2550
+
2551
+ function clearLineMeasurementCache(cm) {
2552
+ cm.display.externalMeasure = null
2553
+ removeChildren(cm.display.lineMeasure)
2554
+ for (var i = 0; i < cm.display.view.length; i++)
2555
+ { clearLineMeasurementCacheFor(cm.display.view[i]) }
2556
+ }
2557
+
2558
+ function clearCaches(cm) {
2559
+ clearLineMeasurementCache(cm)
2560
+ cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cachedPaddingH = null
2561
+ if (!cm.options.lineWrapping) { cm.display.maxLineChanged = true }
2562
+ cm.display.lineNumChars = null
2563
+ }
2564
+
2565
+ function pageScrollX() { return window.pageXOffset || (document.documentElement || document.body).scrollLeft }
2566
+ function pageScrollY() { return window.pageYOffset || (document.documentElement || document.body).scrollTop }
2567
+
2568
+ // Converts a {top, bottom, left, right} box from line-local
2569
+ // coordinates into another coordinate system. Context may be one of
2570
+ // "line", "div" (display.lineDiv), "local"./null (editor), "window",
2571
+ // or "page".
2572
+ function intoCoordSystem(cm, lineObj, rect, context, includeWidgets) {
2573
+ if (!includeWidgets && lineObj.widgets) { for (var i = 0; i < lineObj.widgets.length; ++i) { if (lineObj.widgets[i].above) {
2574
+ var size = widgetHeight(lineObj.widgets[i])
2575
+ rect.top += size; rect.bottom += size
2576
+ } } }
2577
+ if (context == "line") { return rect }
2578
+ if (!context) { context = "local" }
2579
+ var yOff = heightAtLine(lineObj)
2580
+ if (context == "local") { yOff += paddingTop(cm.display) }
2581
+ else { yOff -= cm.display.viewOffset }
2582
+ if (context == "page" || context == "window") {
2583
+ var lOff = cm.display.lineSpace.getBoundingClientRect()
2584
+ yOff += lOff.top + (context == "window" ? 0 : pageScrollY())
2585
+ var xOff = lOff.left + (context == "window" ? 0 : pageScrollX())
2586
+ rect.left += xOff; rect.right += xOff
2587
+ }
2588
+ rect.top += yOff; rect.bottom += yOff
2589
+ return rect
2590
+ }
2591
+
2592
+ // Coverts a box from "div" coords to another coordinate system.
2593
+ // Context may be "window", "page", "div", or "local"./null.
2594
+ function fromCoordSystem(cm, coords, context) {
2595
+ if (context == "div") { return coords }
2596
+ var left = coords.left, top = coords.top
2597
+ // First move into "page" coordinate system
2598
+ if (context == "page") {
2599
+ left -= pageScrollX()
2600
+ top -= pageScrollY()
2601
+ } else if (context == "local" || !context) {
2602
+ var localBox = cm.display.sizer.getBoundingClientRect()
2603
+ left += localBox.left
2604
+ top += localBox.top
2605
+ }
2606
+
2607
+ var lineSpaceBox = cm.display.lineSpace.getBoundingClientRect()
2608
+ return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top}
2609
+ }
2610
+
2611
+ function charCoords(cm, pos, context, lineObj, bias) {
2612
+ if (!lineObj) { lineObj = getLine(cm.doc, pos.line) }
2613
+ return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, bias), context)
2614
+ }
2615
+
2616
+ // Returns a box for a given cursor position, which may have an
2617
+ // 'other' property containing the position of the secondary cursor
2618
+ // on a bidi boundary.
2619
+ function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) {
2620
+ lineObj = lineObj || getLine(cm.doc, pos.line)
2621
+ if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj) }
2622
+ function get(ch, right) {
2623
+ var m = measureCharPrepared(cm, preparedMeasure, ch, right ? "right" : "left", varHeight)
2624
+ if (right) { m.left = m.right; } else { m.right = m.left }
2625
+ return intoCoordSystem(cm, lineObj, m, context)
2626
+ }
2627
+ function getBidi(ch, partPos) {
2628
+ var part = order[partPos], right = part.level % 2
2629
+ if (ch == bidiLeft(part) && partPos && part.level < order[partPos - 1].level) {
2630
+ part = order[--partPos]
2631
+ ch = bidiRight(part) - (part.level % 2 ? 0 : 1)
2632
+ right = true
2633
+ } else if (ch == bidiRight(part) && partPos < order.length - 1 && part.level < order[partPos + 1].level) {
2634
+ part = order[++partPos]
2635
+ ch = bidiLeft(part) - part.level % 2
2636
+ right = false
2637
+ }
2638
+ if (right && ch == part.to && ch > part.from) { return get(ch - 1) }
2639
+ return get(ch, right)
2640
+ }
2641
+ var order = getOrder(lineObj), ch = pos.ch
2642
+ if (!order) { return get(ch) }
2643
+ var partPos = getBidiPartAt(order, ch)
2644
+ var val = getBidi(ch, partPos)
2645
+ if (bidiOther != null) { val.other = getBidi(ch, bidiOther) }
2646
+ return val
2647
+ }
2648
+
2649
+ // Used to cheaply estimate the coordinates for a position. Used for
2650
+ // intermediate scroll updates.
2651
+ function estimateCoords(cm, pos) {
2652
+ var left = 0
2653
+ pos = clipPos(cm.doc, pos)
2654
+ if (!cm.options.lineWrapping) { left = charWidth(cm.display) * pos.ch }
2655
+ var lineObj = getLine(cm.doc, pos.line)
2656
+ var top = heightAtLine(lineObj) + paddingTop(cm.display)
2657
+ return {left: left, right: left, top: top, bottom: top + lineObj.height}
2658
+ }
2659
+
2660
+ // Positions returned by coordsChar contain some extra information.
2661
+ // xRel is the relative x position of the input coordinates compared
2662
+ // to the found position (so xRel > 0 means the coordinates are to
2663
+ // the right of the character position, for example). When outside
2664
+ // is true, that means the coordinates lie outside the line's
2665
+ // vertical range.
2666
+ function PosWithInfo(line, ch, outside, xRel) {
2667
+ var pos = Pos(line, ch)
2668
+ pos.xRel = xRel
2669
+ if (outside) { pos.outside = true }
2670
+ return pos
2671
+ }
2672
+
2673
+ // Compute the character position closest to the given coordinates.
2674
+ // Input must be lineSpace-local ("div" coordinate system).
2675
+ function coordsChar(cm, x, y) {
2676
+ var doc = cm.doc
2677
+ y += cm.display.viewOffset
2678
+ if (y < 0) { return PosWithInfo(doc.first, 0, true, -1) }
2679
+ var lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1
2680
+ if (lineN > last)
2681
+ { return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, true, 1) }
2682
+ if (x < 0) { x = 0 }
2683
+
2684
+ var lineObj = getLine(doc, lineN)
2685
+ for (;;) {
2686
+ var found = coordsCharInner(cm, lineObj, lineN, x, y)
2687
+ var merged = collapsedSpanAtEnd(lineObj)
2688
+ var mergedPos = merged && merged.find(0, true)
2689
+ if (merged && (found.ch > mergedPos.from.ch || found.ch == mergedPos.from.ch && found.xRel > 0))
2690
+ { lineN = lineNo(lineObj = mergedPos.to.line) }
2691
+ else
2692
+ { return found }
2693
+ }
2694
+ }
2695
+
2696
+ function coordsCharInner(cm, lineObj, lineNo$$1, x, y) {
2697
+ var innerOff = y - heightAtLine(lineObj)
2698
+ var wrongLine = false, adjust = 2 * cm.display.wrapper.clientWidth
2699
+ var preparedMeasure = prepareMeasureForLine(cm, lineObj)
2700
+
2701
+ function getX(ch) {
2702
+ var sp = cursorCoords(cm, Pos(lineNo$$1, ch), "line", lineObj, preparedMeasure)
2703
+ wrongLine = true
2704
+ if (innerOff > sp.bottom) { return sp.left - adjust }
2705
+ else if (innerOff < sp.top) { return sp.left + adjust }
2706
+ else { wrongLine = false }
2707
+ return sp.left
2708
+ }
2709
+
2710
+ var bidi = getOrder(lineObj), dist = lineObj.text.length
2711
+ var from = lineLeft(lineObj), to = lineRight(lineObj)
2712
+ var fromX = getX(from), fromOutside = wrongLine, toX = getX(to), toOutside = wrongLine
2713
+
2714
+ if (x > toX) { return PosWithInfo(lineNo$$1, to, toOutside, 1) }
2715
+ // Do a binary search between these bounds.
2716
+ for (;;) {
2717
+ if (bidi ? to == from || to == moveVisually(lineObj, from, 1) : to - from <= 1) {
2718
+ var ch = x < fromX || x - fromX <= toX - x ? from : to
2719
+ var outside = ch == from ? fromOutside : toOutside
2720
+ var xDiff = x - (ch == from ? fromX : toX)
2721
+ // This is a kludge to handle the case where the coordinates
2722
+ // are after a line-wrapped line. We should replace it with a
2723
+ // more general handling of cursor positions around line
2724
+ // breaks. (Issue #4078)
2725
+ if (toOutside && !bidi && !/\s/.test(lineObj.text.charAt(ch)) && xDiff > 0 &&
2726
+ ch < lineObj.text.length && preparedMeasure.view.measure.heights.length > 1) {
2727
+ var charSize = measureCharPrepared(cm, preparedMeasure, ch, "right")
2728
+ if (innerOff <= charSize.bottom && innerOff >= charSize.top && Math.abs(x - charSize.right) < xDiff) {
2729
+ outside = false
2730
+ ch++
2731
+ xDiff = x - charSize.right
2732
+ }
2733
+ }
2734
+ while (isExtendingChar(lineObj.text.charAt(ch))) { ++ch }
2735
+ var pos = PosWithInfo(lineNo$$1, ch, outside, xDiff < -1 ? -1 : xDiff > 1 ? 1 : 0)
2736
+ return pos
2737
+ }
2738
+ var step = Math.ceil(dist / 2), middle = from + step
2739
+ if (bidi) {
2740
+ middle = from
2741
+ for (var i = 0; i < step; ++i) { middle = moveVisually(lineObj, middle, 1) }
2742
+ }
2743
+ var middleX = getX(middle)
2744
+ if (middleX > x) {to = middle; toX = middleX; if (toOutside = wrongLine) { toX += 1000; } dist = step}
2745
+ else {from = middle; fromX = middleX; fromOutside = wrongLine; dist -= step}
2746
+ }
2747
+ }
2748
+
2749
+ var measureText
2750
+ // Compute the default text height.
2751
+ function textHeight(display) {
2752
+ if (display.cachedTextHeight != null) { return display.cachedTextHeight }
2753
+ if (measureText == null) {
2754
+ measureText = elt("pre")
2755
+ // Measure a bunch of lines, for browsers that compute
2756
+ // fractional heights.
2757
+ for (var i = 0; i < 49; ++i) {
2758
+ measureText.appendChild(document.createTextNode("x"))
2759
+ measureText.appendChild(elt("br"))
2760
+ }
2761
+ measureText.appendChild(document.createTextNode("x"))
2762
+ }
2763
+ removeChildrenAndAdd(display.measure, measureText)
2764
+ var height = measureText.offsetHeight / 50
2765
+ if (height > 3) { display.cachedTextHeight = height }
2766
+ removeChildren(display.measure)
2767
+ return height || 1
2768
+ }
2769
+
2770
+ // Compute the default character width.
2771
+ function charWidth(display) {
2772
+ if (display.cachedCharWidth != null) { return display.cachedCharWidth }
2773
+ var anchor = elt("span", "xxxxxxxxxx")
2774
+ var pre = elt("pre", [anchor])
2775
+ removeChildrenAndAdd(display.measure, pre)
2776
+ var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10
2777
+ if (width > 2) { display.cachedCharWidth = width }
2778
+ return width || 10
2779
+ }
2780
+
2781
+ // Do a bulk-read of the DOM positions and sizes needed to draw the
2782
+ // view, so that we don't interleave reading and writing to the DOM.
2783
+ function getDimensions(cm) {
2784
+ var d = cm.display, left = {}, width = {}
2785
+ var gutterLeft = d.gutters.clientLeft
2786
+ for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) {
2787
+ left[cm.options.gutters[i]] = n.offsetLeft + n.clientLeft + gutterLeft
2788
+ width[cm.options.gutters[i]] = n.clientWidth
2789
+ }
2790
+ return {fixedPos: compensateForHScroll(d),
2791
+ gutterTotalWidth: d.gutters.offsetWidth,
2792
+ gutterLeft: left,
2793
+ gutterWidth: width,
2794
+ wrapperWidth: d.wrapper.clientWidth}
2795
+ }
2796
+
2797
+ // Computes display.scroller.scrollLeft + display.gutters.offsetWidth,
2798
+ // but using getBoundingClientRect to get a sub-pixel-accurate
2799
+ // result.
2800
+ function compensateForHScroll(display) {
2801
+ return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left
2802
+ }
2803
+
2804
+ // Returns a function that estimates the height of a line, to use as
2805
+ // first approximation until the line becomes visible (and is thus
2806
+ // properly measurable).
2807
+ function estimateHeight(cm) {
2808
+ var th = textHeight(cm.display), wrapping = cm.options.lineWrapping
2809
+ var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3)
2810
+ return function (line) {
2811
+ if (lineIsHidden(cm.doc, line)) { return 0 }
2812
+
2813
+ var widgetsHeight = 0
2814
+ if (line.widgets) { for (var i = 0; i < line.widgets.length; i++) {
2815
+ if (line.widgets[i].height) { widgetsHeight += line.widgets[i].height }
2816
+ } }
2817
+
2818
+ if (wrapping)
2819
+ { return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th }
2820
+ else
2821
+ { return widgetsHeight + th }
2822
+ }
2823
+ }
2824
+
2825
+ function estimateLineHeights(cm) {
2826
+ var doc = cm.doc, est = estimateHeight(cm)
2827
+ doc.iter(function (line) {
2828
+ var estHeight = est(line)
2829
+ if (estHeight != line.height) { updateLineHeight(line, estHeight) }
2830
+ })
2831
+ }
2832
+
2833
+ // Given a mouse event, find the corresponding position. If liberal
2834
+ // is false, it checks whether a gutter or scrollbar was clicked,
2835
+ // and returns null if it was. forRect is used by rectangular
2836
+ // selections, and tries to estimate a character position even for
2837
+ // coordinates beyond the right of the text.
2838
+ function posFromMouse(cm, e, liberal, forRect) {
2839
+ var display = cm.display
2840
+ if (!liberal && e_target(e).getAttribute("cm-not-content") == "true") { return null }
2841
+
2842
+ var x, y, space = display.lineSpace.getBoundingClientRect()
2843
+ // Fails unpredictably on IE[67] when mouse is dragged around quickly.
2844
+ try { x = e.clientX - space.left; y = e.clientY - space.top }
2845
+ catch (e) { return null }
2846
+ var coords = coordsChar(cm, x, y), line
2847
+ if (forRect && coords.xRel == 1 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) {
2848
+ var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length
2849
+ coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff))
2850
+ }
2851
+ return coords
2852
+ }
2853
+
2854
+ // Find the view element corresponding to a given line. Return null
2855
+ // when the line isn't visible.
2856
+ function findViewIndex(cm, n) {
2857
+ if (n >= cm.display.viewTo) { return null }
2858
+ n -= cm.display.viewFrom
2859
+ if (n < 0) { return null }
2860
+ var view = cm.display.view
2861
+ for (var i = 0; i < view.length; i++) {
2862
+ n -= view[i].size
2863
+ if (n < 0) { return i }
2864
+ }
2865
+ }
2866
+
2867
+ function updateSelection(cm) {
2868
+ cm.display.input.showSelection(cm.display.input.prepareSelection())
2869
+ }
2870
+
2871
+ function prepareSelection(cm, primary) {
2872
+ var doc = cm.doc, result = {}
2873
+ var curFragment = result.cursors = document.createDocumentFragment()
2874
+ var selFragment = result.selection = document.createDocumentFragment()
2875
+
2876
+ for (var i = 0; i < doc.sel.ranges.length; i++) {
2877
+ if (primary === false && i == doc.sel.primIndex) { continue }
2878
+ var range$$1 = doc.sel.ranges[i]
2879
+ if (range$$1.from().line >= cm.display.viewTo || range$$1.to().line < cm.display.viewFrom) { continue }
2880
+ var collapsed = range$$1.empty()
2881
+ if (collapsed || cm.options.showCursorWhenSelecting)
2882
+ { drawSelectionCursor(cm, range$$1.head, curFragment) }
2883
+ if (!collapsed)
2884
+ { drawSelectionRange(cm, range$$1, selFragment) }
2885
+ }
2886
+ return result
2887
+ }
2888
+
2889
+ // Draws a cursor for the given range
2890
+ function drawSelectionCursor(cm, head, output) {
2891
+ var pos = cursorCoords(cm, head, "div", null, null, !cm.options.singleCursorHeightPerLine)
2892
+
2893
+ var cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor"))
2894
+ cursor.style.left = pos.left + "px"
2895
+ cursor.style.top = pos.top + "px"
2896
+ cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px"
2897
+
2898
+ if (pos.other) {
2899
+ // Secondary cursor, shown when on a 'jump' in bi-directional text
2900
+ var otherCursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor"))
2901
+ otherCursor.style.display = ""
2902
+ otherCursor.style.left = pos.other.left + "px"
2903
+ otherCursor.style.top = pos.other.top + "px"
2904
+ otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px"
2905
+ }
2906
+ }
2907
+
2908
+ // Draws the given range as a highlighted selection
2909
+ function drawSelectionRange(cm, range$$1, output) {
2910
+ var display = cm.display, doc = cm.doc
2911
+ var fragment = document.createDocumentFragment()
2912
+ var padding = paddingH(cm.display), leftSide = padding.left
2913
+ var rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right
2914
+
2915
+ function add(left, top, width, bottom) {
2916
+ if (top < 0) { top = 0 }
2917
+ top = Math.round(top)
2918
+ bottom = Math.round(bottom)
2919
+ fragment.appendChild(elt("div", null, "CodeMirror-selected", ("position: absolute; left: " + left + "px;\n top: " + top + "px; width: " + (width == null ? rightSide - left : width) + "px;\n height: " + (bottom - top) + "px")))
2920
+ }
2921
+
2922
+ function drawForLine(line, fromArg, toArg) {
2923
+ var lineObj = getLine(doc, line)
2924
+ var lineLen = lineObj.text.length
2925
+ var start, end
2926
+ function coords(ch, bias) {
2927
+ return charCoords(cm, Pos(line, ch), "div", lineObj, bias)
2928
+ }
2929
+
2930
+ iterateBidiSections(getOrder(lineObj), fromArg || 0, toArg == null ? lineLen : toArg, function (from, to, dir) {
2931
+ var leftPos = coords(from, "left"), rightPos, left, right
2932
+ if (from == to) {
2933
+ rightPos = leftPos
2934
+ left = right = leftPos.left
2935
+ } else {
2936
+ rightPos = coords(to - 1, "right")
2937
+ if (dir == "rtl") { var tmp = leftPos; leftPos = rightPos; rightPos = tmp }
2938
+ left = leftPos.left
2939
+ right = rightPos.right
2940
+ }
2941
+ if (fromArg == null && from == 0) { left = leftSide }
2942
+ if (rightPos.top - leftPos.top > 3) { // Different lines, draw top part
2943
+ add(left, leftPos.top, null, leftPos.bottom)
2944
+ left = leftSide
2945
+ if (leftPos.bottom < rightPos.top) { add(left, leftPos.bottom, null, rightPos.top) }
2946
+ }
2947
+ if (toArg == null && to == lineLen) { right = rightSide }
2948
+ if (!start || leftPos.top < start.top || leftPos.top == start.top && leftPos.left < start.left)
2949
+ { start = leftPos }
2950
+ if (!end || rightPos.bottom > end.bottom || rightPos.bottom == end.bottom && rightPos.right > end.right)
2951
+ { end = rightPos }
2952
+ if (left < leftSide + 1) { left = leftSide }
2953
+ add(left, rightPos.top, right - left, rightPos.bottom)
2954
+ })
2955
+ return {start: start, end: end}
2956
+ }
2957
+
2958
+ var sFrom = range$$1.from(), sTo = range$$1.to()
2959
+ if (sFrom.line == sTo.line) {
2960
+ drawForLine(sFrom.line, sFrom.ch, sTo.ch)
2961
+ } else {
2962
+ var fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line)
2963
+ var singleVLine = visualLine(fromLine) == visualLine(toLine)
2964
+ var leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end
2965
+ var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start
2966
+ if (singleVLine) {
2967
+ if (leftEnd.top < rightStart.top - 2) {
2968
+ add(leftEnd.right, leftEnd.top, null, leftEnd.bottom)
2969
+ add(leftSide, rightStart.top, rightStart.left, rightStart.bottom)
2970
+ } else {
2971
+ add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom)
2972
+ }
2973
+ }
2974
+ if (leftEnd.bottom < rightStart.top)
2975
+ { add(leftSide, leftEnd.bottom, null, rightStart.top) }
2976
+ }
2977
+
2978
+ output.appendChild(fragment)
2979
+ }
2980
+
2981
+ // Cursor-blinking
2982
+ function restartBlink(cm) {
2983
+ if (!cm.state.focused) { return }
2984
+ var display = cm.display
2985
+ clearInterval(display.blinker)
2986
+ var on = true
2987
+ display.cursorDiv.style.visibility = ""
2988
+ if (cm.options.cursorBlinkRate > 0)
2989
+ { display.blinker = setInterval(function () { return display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden"; },
2990
+ cm.options.cursorBlinkRate) }
2991
+ else if (cm.options.cursorBlinkRate < 0)
2992
+ { display.cursorDiv.style.visibility = "hidden" }
2993
+ }
2994
+
2995
+ function ensureFocus(cm) {
2996
+ if (!cm.state.focused) { cm.display.input.focus(); onFocus(cm) }
2997
+ }
2998
+
2999
+ function delayBlurEvent(cm) {
3000
+ cm.state.delayingBlurEvent = true
3001
+ setTimeout(function () { if (cm.state.delayingBlurEvent) {
3002
+ cm.state.delayingBlurEvent = false
3003
+ onBlur(cm)
3004
+ } }, 100)
3005
+ }
3006
+
3007
+ function onFocus(cm, e) {
3008
+ if (cm.state.delayingBlurEvent) { cm.state.delayingBlurEvent = false }
3009
+
3010
+ if (cm.options.readOnly == "nocursor") { return }
3011
+ if (!cm.state.focused) {
3012
+ signal(cm, "focus", cm, e)
3013
+ cm.state.focused = true
3014
+ addClass(cm.display.wrapper, "CodeMirror-focused")
3015
+ // This test prevents this from firing when a context
3016
+ // menu is closed (since the input reset would kill the
3017
+ // select-all detection hack)
3018
+ if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) {
3019
+ cm.display.input.reset()
3020
+ if (webkit) { setTimeout(function () { return cm.display.input.reset(true); }, 20) } // Issue #1730
3021
+ }
3022
+ cm.display.input.receivedFocus()
3023
+ }
3024
+ restartBlink(cm)
3025
+ }
3026
+ function onBlur(cm, e) {
3027
+ if (cm.state.delayingBlurEvent) { return }
3028
+
3029
+ if (cm.state.focused) {
3030
+ signal(cm, "blur", cm, e)
3031
+ cm.state.focused = false
3032
+ rmClass(cm.display.wrapper, "CodeMirror-focused")
3033
+ }
3034
+ clearInterval(cm.display.blinker)
3035
+ setTimeout(function () { if (!cm.state.focused) { cm.display.shift = false } }, 150)
3036
+ }
3037
+
3038
+ // Re-align line numbers and gutter marks to compensate for
3039
+ // horizontal scrolling.
3040
+ function alignHorizontally(cm) {
3041
+ var display = cm.display, view = display.view
3042
+ if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) { return }
3043
+ var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft
3044
+ var gutterW = display.gutters.offsetWidth, left = comp + "px"
3045
+ for (var i = 0; i < view.length; i++) { if (!view[i].hidden) {
3046
+ if (cm.options.fixedGutter) {
3047
+ if (view[i].gutter)
3048
+ { view[i].gutter.style.left = left }
3049
+ if (view[i].gutterBackground)
3050
+ { view[i].gutterBackground.style.left = left }
3051
+ }
3052
+ var align = view[i].alignable
3053
+ if (align) { for (var j = 0; j < align.length; j++)
3054
+ { align[j].style.left = left } }
3055
+ } }
3056
+ if (cm.options.fixedGutter)
3057
+ { display.gutters.style.left = (comp + gutterW) + "px" }
3058
+ }
3059
+
3060
+ // Used to ensure that the line number gutter is still the right
3061
+ // size for the current document size. Returns true when an update
3062
+ // is needed.
3063
+ function maybeUpdateLineNumberWidth(cm) {
3064
+ if (!cm.options.lineNumbers) { return false }
3065
+ var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display
3066
+ if (last.length != display.lineNumChars) {
3067
+ var test = display.measure.appendChild(elt("div", [elt("div", last)],
3068
+ "CodeMirror-linenumber CodeMirror-gutter-elt"))
3069
+ var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW
3070
+ display.lineGutter.style.width = ""
3071
+ display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding) + 1
3072
+ display.lineNumWidth = display.lineNumInnerWidth + padding
3073
+ display.lineNumChars = display.lineNumInnerWidth ? last.length : -1
3074
+ display.lineGutter.style.width = display.lineNumWidth + "px"
3075
+ updateGutterSpace(cm)
3076
+ return true
3077
+ }
3078
+ return false
3079
+ }
3080
+
3081
+ // Read the actual heights of the rendered lines, and update their
3082
+ // stored heights to match.
3083
+ function updateHeightsInViewport(cm) {
3084
+ var display = cm.display
3085
+ var prevBottom = display.lineDiv.offsetTop
3086
+ for (var i = 0; i < display.view.length; i++) {
3087
+ var cur = display.view[i], height = void 0
3088
+ if (cur.hidden) { continue }
3089
+ if (ie && ie_version < 8) {
3090
+ var bot = cur.node.offsetTop + cur.node.offsetHeight
3091
+ height = bot - prevBottom
3092
+ prevBottom = bot
3093
+ } else {
3094
+ var box = cur.node.getBoundingClientRect()
3095
+ height = box.bottom - box.top
3096
+ }
3097
+ var diff = cur.line.height - height
3098
+ if (height < 2) { height = textHeight(display) }
3099
+ if (diff > .001 || diff < -.001) {
3100
+ updateLineHeight(cur.line, height)
3101
+ updateWidgetHeight(cur.line)
3102
+ if (cur.rest) { for (var j = 0; j < cur.rest.length; j++)
3103
+ { updateWidgetHeight(cur.rest[j]) } }
3104
+ }
3105
+ }
3106
+ }
3107
+
3108
+ // Read and store the height of line widgets associated with the
3109
+ // given line.
3110
+ function updateWidgetHeight(line) {
3111
+ if (line.widgets) { for (var i = 0; i < line.widgets.length; ++i)
3112
+ { line.widgets[i].height = line.widgets[i].node.parentNode.offsetHeight } }
3113
+ }
3114
+
3115
+ // Compute the lines that are visible in a given viewport (defaults
3116
+ // the the current scroll position). viewport may contain top,
3117
+ // height, and ensure (see op.scrollToPos) properties.
3118
+ function visibleLines(display, doc, viewport) {
3119
+ var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop
3120
+ top = Math.floor(top - paddingTop(display))
3121
+ var bottom = viewport && viewport.bottom != null ? viewport.bottom : top + display.wrapper.clientHeight
3122
+
3123
+ var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom)
3124
+ // Ensure is a {from: {line, ch}, to: {line, ch}} object, and
3125
+ // forces those lines into the viewport (if possible).
3126
+ if (viewport && viewport.ensure) {
3127
+ var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line
3128
+ if (ensureFrom < from) {
3129
+ from = ensureFrom
3130
+ to = lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight)
3131
+ } else if (Math.min(ensureTo, doc.lastLine()) >= to) {
3132
+ from = lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight)
3133
+ to = ensureTo
3134
+ }
3135
+ }
3136
+ return {from: from, to: Math.max(to, from + 1)}
3137
+ }
3138
+
3139
+ // Sync the scrollable area and scrollbars, ensure the viewport
3140
+ // covers the visible area.
3141
+ function setScrollTop(cm, val) {
3142
+ if (Math.abs(cm.doc.scrollTop - val) < 2) { return }
3143
+ cm.doc.scrollTop = val
3144
+ if (!gecko) { updateDisplaySimple(cm, {top: val}) }
3145
+ if (cm.display.scroller.scrollTop != val) { cm.display.scroller.scrollTop = val }
3146
+ cm.display.scrollbars.setScrollTop(val)
3147
+ if (gecko) { updateDisplaySimple(cm) }
3148
+ startWorker(cm, 100)
3149
+ }
3150
+ // Sync scroller and scrollbar, ensure the gutter elements are
3151
+ // aligned.
3152
+ function setScrollLeft(cm, val, isScroller) {
3153
+ if (isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) { return }
3154
+ val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth)
3155
+ cm.doc.scrollLeft = val
3156
+ alignHorizontally(cm)
3157
+ if (cm.display.scroller.scrollLeft != val) { cm.display.scroller.scrollLeft = val }
3158
+ cm.display.scrollbars.setScrollLeft(val)
3159
+ }
3160
+
3161
+ // Since the delta values reported on mouse wheel events are
3162
+ // unstandardized between browsers and even browser versions, and
3163
+ // generally horribly unpredictable, this code starts by measuring
3164
+ // the scroll effect that the first few mouse wheel events have,
3165
+ // and, from that, detects the way it can convert deltas to pixel
3166
+ // offsets afterwards.
3167
+ //
3168
+ // The reason we want to know the amount a wheel event will scroll
3169
+ // is that it gives us a chance to update the display before the
3170
+ // actual scrolling happens, reducing flickering.
3171
+
3172
+ var wheelSamples = 0;
3173
+ var wheelPixelsPerUnit = null
3174
+ // Fill in a browser-detected starting value on browsers where we
3175
+ // know one. These don't have to be accurate -- the result of them
3176
+ // being wrong would just be a slight flicker on the first wheel
3177
+ // scroll (if it is large enough).
3178
+ if (ie) { wheelPixelsPerUnit = -.53 }
3179
+ else if (gecko) { wheelPixelsPerUnit = 15 }
3180
+ else if (chrome) { wheelPixelsPerUnit = -.7 }
3181
+ else if (safari) { wheelPixelsPerUnit = -1/3 }
3182
+
3183
+ function wheelEventDelta(e) {
3184
+ var dx = e.wheelDeltaX, dy = e.wheelDeltaY
3185
+ if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) { dx = e.detail }
3186
+ if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) { dy = e.detail }
3187
+ else if (dy == null) { dy = e.wheelDelta }
3188
+ return {x: dx, y: dy}
3189
+ }
3190
+ function wheelEventPixels(e) {
3191
+ var delta = wheelEventDelta(e)
3192
+ delta.x *= wheelPixelsPerUnit
3193
+ delta.y *= wheelPixelsPerUnit
3194
+ return delta
3195
+ }
3196
+
3197
+ function onScrollWheel(cm, e) {
3198
+ var delta = wheelEventDelta(e), dx = delta.x, dy = delta.y
3199
+
3200
+ var display = cm.display, scroll = display.scroller
3201
+ // Quit if there's nothing to scroll here
3202
+ var canScrollX = scroll.scrollWidth > scroll.clientWidth
3203
+ var canScrollY = scroll.scrollHeight > scroll.clientHeight
3204
+ if (!(dx && canScrollX || dy && canScrollY)) { return }
3205
+
3206
+ // Webkit browsers on OS X abort momentum scrolls when the target
3207
+ // of the scroll event is removed from the scrollable element.
3208
+ // This hack (see related code in patchDisplay) makes sure the
3209
+ // element is kept around.
3210
+ if (dy && mac && webkit) {
3211
+ outer: for (var cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) {
3212
+ for (var i = 0; i < view.length; i++) {
3213
+ if (view[i].node == cur) {
3214
+ cm.display.currentWheelTarget = cur
3215
+ break outer
3216
+ }
3217
+ }
3218
+ }
3219
+ }
3220
+
3221
+ // On some browsers, horizontal scrolling will cause redraws to
3222
+ // happen before the gutter has been realigned, causing it to
3223
+ // wriggle around in a most unseemly way. When we have an
3224
+ // estimated pixels/delta value, we just handle horizontal
3225
+ // scrolling entirely here. It'll be slightly off from native, but
3226
+ // better than glitching out.
3227
+ if (dx && !gecko && !presto && wheelPixelsPerUnit != null) {
3228
+ if (dy && canScrollY)
3229
+ { setScrollTop(cm, Math.max(0, Math.min(scroll.scrollTop + dy * wheelPixelsPerUnit, scroll.scrollHeight - scroll.clientHeight))) }
3230
+ setScrollLeft(cm, Math.max(0, Math.min(scroll.scrollLeft + dx * wheelPixelsPerUnit, scroll.scrollWidth - scroll.clientWidth)))
3231
+ // Only prevent default scrolling if vertical scrolling is
3232
+ // actually possible. Otherwise, it causes vertical scroll
3233
+ // jitter on OSX trackpads when deltaX is small and deltaY
3234
+ // is large (issue #3579)
3235
+ if (!dy || (dy && canScrollY))
3236
+ { e_preventDefault(e) }
3237
+ display.wheelStartX = null // Abort measurement, if in progress
3238
+ return
3239
+ }
3240
+
3241
+ // 'Project' the visible viewport to cover the area that is being
3242
+ // scrolled into view (if we know enough to estimate it).
3243
+ if (dy && wheelPixelsPerUnit != null) {
3244
+ var pixels = dy * wheelPixelsPerUnit
3245
+ var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight
3246
+ if (pixels < 0) { top = Math.max(0, top + pixels - 50) }
3247
+ else { bot = Math.min(cm.doc.height, bot + pixels + 50) }
3248
+ updateDisplaySimple(cm, {top: top, bottom: bot})
3249
+ }
3250
+
3251
+ if (wheelSamples < 20) {
3252
+ if (display.wheelStartX == null) {
3253
+ display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop
3254
+ display.wheelDX = dx; display.wheelDY = dy
3255
+ setTimeout(function () {
3256
+ if (display.wheelStartX == null) { return }
3257
+ var movedX = scroll.scrollLeft - display.wheelStartX
3258
+ var movedY = scroll.scrollTop - display.wheelStartY
3259
+ var sample = (movedY && display.wheelDY && movedY / display.wheelDY) ||
3260
+ (movedX && display.wheelDX && movedX / display.wheelDX)
3261
+ display.wheelStartX = display.wheelStartY = null
3262
+ if (!sample) { return }
3263
+ wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1)
3264
+ ++wheelSamples
3265
+ }, 200)
3266
+ } else {
3267
+ display.wheelDX += dx; display.wheelDY += dy
3268
+ }
3269
+ }
3270
+ }
3271
+
3272
+ // SCROLLBARS
3273
+
3274
+ // Prepare DOM reads needed to update the scrollbars. Done in one
3275
+ // shot to minimize update/measure roundtrips.
3276
+ function measureForScrollbars(cm) {
3277
+ var d = cm.display, gutterW = d.gutters.offsetWidth
3278
+ var docH = Math.round(cm.doc.height + paddingVert(cm.display))
3279
+ return {
3280
+ clientHeight: d.scroller.clientHeight,
3281
+ viewHeight: d.wrapper.clientHeight,
3282
+ scrollWidth: d.scroller.scrollWidth, clientWidth: d.scroller.clientWidth,
3283
+ viewWidth: d.wrapper.clientWidth,
3284
+ barLeft: cm.options.fixedGutter ? gutterW : 0,
3285
+ docHeight: docH,
3286
+ scrollHeight: docH + scrollGap(cm) + d.barHeight,
3287
+ nativeBarWidth: d.nativeBarWidth,
3288
+ gutterWidth: gutterW
3289
+ }
3290
+ }
3291
+
3292
+ var NativeScrollbars = function NativeScrollbars(place, scroll, cm) {
3293
+ this.cm = cm
3294
+ var vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar")
3295
+ var horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar")
3296
+ place(vert); place(horiz)
3297
+
3298
+ on(vert, "scroll", function () {
3299
+ if (vert.clientHeight) { scroll(vert.scrollTop, "vertical") }
3300
+ })
3301
+ on(horiz, "scroll", function () {
3302
+ if (horiz.clientWidth) { scroll(horiz.scrollLeft, "horizontal") }
3303
+ })
3304
+
3305
+ this.checkedZeroWidth = false
3306
+ // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
3307
+ if (ie && ie_version < 8) { this.horiz.style.minHeight = this.vert.style.minWidth = "18px" }
3308
+ };
3309
+
3310
+ NativeScrollbars.prototype.update = function update (measure) {
3311
+ var needsH = measure.scrollWidth > measure.clientWidth + 1
3312
+ var needsV = measure.scrollHeight > measure.clientHeight + 1
3313
+ var sWidth = measure.nativeBarWidth
3314
+
3315
+ if (needsV) {
3316
+ this.vert.style.display = "block"
3317
+ this.vert.style.bottom = needsH ? sWidth + "px" : "0"
3318
+ var totalHeight = measure.viewHeight - (needsH ? sWidth : 0)
3319
+ // A bug in IE8 can cause this value to be negative, so guard it.
3320
+ this.vert.firstChild.style.height =
3321
+ Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + "px"
3322
+ } else {
3323
+ this.vert.style.display = ""
3324
+ this.vert.firstChild.style.height = "0"
3325
+ }
3326
+
3327
+ if (needsH) {
3328
+ this.horiz.style.display = "block"
3329
+ this.horiz.style.right = needsV ? sWidth + "px" : "0"
3330
+ this.horiz.style.left = measure.barLeft + "px"
3331
+ var totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0)
3332
+ this.horiz.firstChild.style.width =
3333
+ (measure.scrollWidth - measure.clientWidth + totalWidth) + "px"
3334
+ } else {
3335
+ this.horiz.style.display = ""
3336
+ this.horiz.firstChild.style.width = "0"
3337
+ }
3338
+
3339
+ if (!this.checkedZeroWidth && measure.clientHeight > 0) {
3340
+ if (sWidth == 0) { this.zeroWidthHack() }
3341
+ this.checkedZeroWidth = true
3342
+ }
3343
+
3344
+ return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0}
3345
+ };
3346
+
3347
+ NativeScrollbars.prototype.setScrollLeft = function setScrollLeft$1 (pos) {
3348
+ if (this.horiz.scrollLeft != pos) { this.horiz.scrollLeft = pos }
3349
+ if (this.disableHoriz) { this.enableZeroWidthBar(this.horiz, this.disableHoriz) }
3350
+ };
3351
+
3352
+ NativeScrollbars.prototype.setScrollTop = function setScrollTop$1 (pos) {
3353
+ if (this.vert.scrollTop != pos) { this.vert.scrollTop = pos }
3354
+ if (this.disableVert) { this.enableZeroWidthBar(this.vert, this.disableVert) }
3355
+ };
3356
+
3357
+ NativeScrollbars.prototype.zeroWidthHack = function zeroWidthHack () {
3358
+ var w = mac && !mac_geMountainLion ? "12px" : "18px"
3359
+ this.horiz.style.height = this.vert.style.width = w
3360
+ this.horiz.style.pointerEvents = this.vert.style.pointerEvents = "none"
3361
+ this.disableHoriz = new Delayed
3362
+ this.disableVert = new Delayed
3363
+ };
3364
+
3365
+ NativeScrollbars.prototype.enableZeroWidthBar = function enableZeroWidthBar (bar, delay) {
3366
+ bar.style.pointerEvents = "auto"
3367
+ function maybeDisable() {
3368
+ // To find out whether the scrollbar is still visible, we
3369
+ // check whether the element under the pixel in the bottom
3370
+ // left corner of the scrollbar box is the scrollbar box
3371
+ // itself (when the bar is still visible) or its filler child
3372
+ // (when the bar is hidden). If it is still visible, we keep
3373
+ // it enabled, if it's hidden, we disable pointer events.
3374
+ var box = bar.getBoundingClientRect()
3375
+ var elt$$1 = document.elementFromPoint(box.left + 1, box.bottom - 1)
3376
+ if (elt$$1 != bar) { bar.style.pointerEvents = "none" }
3377
+ else { delay.set(1000, maybeDisable) }
3378
+ }
3379
+ delay.set(1000, maybeDisable)
3380
+ };
3381
+
3382
+ NativeScrollbars.prototype.clear = function clear () {
3383
+ var parent = this.horiz.parentNode
3384
+ parent.removeChild(this.horiz)
3385
+ parent.removeChild(this.vert)
3386
+ };
3387
+
3388
+ var NullScrollbars = function NullScrollbars () {};
3389
+
3390
+ NullScrollbars.prototype.update = function update () { return {bottom: 0, right: 0} };
3391
+ NullScrollbars.prototype.setScrollLeft = function setScrollLeft$2 () {};
3392
+ NullScrollbars.prototype.setScrollTop = function setScrollTop$2 () {};
3393
+ NullScrollbars.prototype.clear = function clear () {};
3394
+
3395
+ function updateScrollbars(cm, measure) {
3396
+ if (!measure) { measure = measureForScrollbars(cm) }
3397
+ var startWidth = cm.display.barWidth, startHeight = cm.display.barHeight
3398
+ updateScrollbarsInner(cm, measure)
3399
+ for (var i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != cm.display.barHeight; i++) {
3400
+ if (startWidth != cm.display.barWidth && cm.options.lineWrapping)
3401
+ { updateHeightsInViewport(cm) }
3402
+ updateScrollbarsInner(cm, measureForScrollbars(cm))
3403
+ startWidth = cm.display.barWidth; startHeight = cm.display.barHeight
3404
+ }
3405
+ }
3406
+
3407
+ // Re-synchronize the fake scrollbars with the actual size of the
3408
+ // content.
3409
+ function updateScrollbarsInner(cm, measure) {
3410
+ var d = cm.display
3411
+ var sizes = d.scrollbars.update(measure)
3412
+
3413
+ d.sizer.style.paddingRight = (d.barWidth = sizes.right) + "px"
3414
+ d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + "px"
3415
+ d.heightForcer.style.borderBottom = sizes.bottom + "px solid transparent"
3416
+
3417
+ if (sizes.right && sizes.bottom) {
3418
+ d.scrollbarFiller.style.display = "block"
3419
+ d.scrollbarFiller.style.height = sizes.bottom + "px"
3420
+ d.scrollbarFiller.style.width = sizes.right + "px"
3421
+ } else { d.scrollbarFiller.style.display = "" }
3422
+ if (sizes.bottom && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) {
3423
+ d.gutterFiller.style.display = "block"
3424
+ d.gutterFiller.style.height = sizes.bottom + "px"
3425
+ d.gutterFiller.style.width = measure.gutterWidth + "px"
3426
+ } else { d.gutterFiller.style.display = "" }
3427
+ }
3428
+
3429
+ var scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbars}
3430
+
3431
+ function initScrollbars(cm) {
3432
+ if (cm.display.scrollbars) {
3433
+ cm.display.scrollbars.clear()
3434
+ if (cm.display.scrollbars.addClass)
3435
+ { rmClass(cm.display.wrapper, cm.display.scrollbars.addClass) }
3436
+ }
3437
+
3438
+ cm.display.scrollbars = new scrollbarModel[cm.options.scrollbarStyle](function (node) {
3439
+ cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller)
3440
+ // Prevent clicks in the scrollbars from killing focus
3441
+ on(node, "mousedown", function () {
3442
+ if (cm.state.focused) { setTimeout(function () { return cm.display.input.focus(); }, 0) }
3443
+ })
3444
+ node.setAttribute("cm-not-content", "true")
3445
+ }, function (pos, axis) {
3446
+ if (axis == "horizontal") { setScrollLeft(cm, pos) }
3447
+ else { setScrollTop(cm, pos) }
3448
+ }, cm)
3449
+ if (cm.display.scrollbars.addClass)
3450
+ { addClass(cm.display.wrapper, cm.display.scrollbars.addClass) }
3451
+ }
3452
+
3453
+ // SCROLLING THINGS INTO VIEW
3454
+
3455
+ // If an editor sits on the top or bottom of the window, partially
3456
+ // scrolled out of view, this ensures that the cursor is visible.
3457
+ function maybeScrollWindow(cm, coords) {
3458
+ if (signalDOMEvent(cm, "scrollCursorIntoView")) { return }
3459
+
3460
+ var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null
3461
+ if (coords.top + box.top < 0) { doScroll = true }
3462
+ else if (coords.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) { doScroll = false }
3463
+ if (doScroll != null && !phantom) {
3464
+ var scrollNode = elt("div", "\u200b", null, ("position: absolute;\n top: " + (coords.top - display.viewOffset - paddingTop(cm.display)) + "px;\n height: " + (coords.bottom - coords.top + scrollGap(cm) + display.barHeight) + "px;\n left: " + (coords.left) + "px; width: 2px;"))
3465
+ cm.display.lineSpace.appendChild(scrollNode)
3466
+ scrollNode.scrollIntoView(doScroll)
3467
+ cm.display.lineSpace.removeChild(scrollNode)
3468
+ }
3469
+ }
3470
+
3471
+ // Scroll a given position into view (immediately), verifying that
3472
+ // it actually became visible (as line heights are accurately
3473
+ // measured, the position of something may 'drift' during drawing).
3474
+ function scrollPosIntoView(cm, pos, end, margin) {
3475
+ if (margin == null) { margin = 0 }
3476
+ var coords
3477
+ for (var limit = 0; limit < 5; limit++) {
3478
+ var changed = false
3479
+ coords = cursorCoords(cm, pos)
3480
+ var endCoords = !end || end == pos ? coords : cursorCoords(cm, end)
3481
+ var scrollPos = calculateScrollPos(cm, Math.min(coords.left, endCoords.left),
3482
+ Math.min(coords.top, endCoords.top) - margin,
3483
+ Math.max(coords.left, endCoords.left),
3484
+ Math.max(coords.bottom, endCoords.bottom) + margin)
3485
+ var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft
3486
+ if (scrollPos.scrollTop != null) {
3487
+ setScrollTop(cm, scrollPos.scrollTop)
3488
+ if (Math.abs(cm.doc.scrollTop - startTop) > 1) { changed = true }
3489
+ }
3490
+ if (scrollPos.scrollLeft != null) {
3491
+ setScrollLeft(cm, scrollPos.scrollLeft)
3492
+ if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) { changed = true }
3493
+ }
3494
+ if (!changed) { break }
3495
+ }
3496
+ return coords
3497
+ }
3498
+
3499
+ // Scroll a given set of coordinates into view (immediately).
3500
+ function scrollIntoView(cm, x1, y1, x2, y2) {
3501
+ var scrollPos = calculateScrollPos(cm, x1, y1, x2, y2)
3502
+ if (scrollPos.scrollTop != null) { setScrollTop(cm, scrollPos.scrollTop) }
3503
+ if (scrollPos.scrollLeft != null) { setScrollLeft(cm, scrollPos.scrollLeft) }
3504
+ }
3505
+
3506
+ // Calculate a new scroll position needed to scroll the given
3507
+ // rectangle into view. Returns an object with scrollTop and
3508
+ // scrollLeft properties. When these are undefined, the
3509
+ // vertical/horizontal position does not need to be adjusted.
3510
+ function calculateScrollPos(cm, x1, y1, x2, y2) {
3511
+ var display = cm.display, snapMargin = textHeight(cm.display)
3512
+ if (y1 < 0) { y1 = 0 }
3513
+ var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop
3514
+ var screen = displayHeight(cm), result = {}
3515
+ if (y2 - y1 > screen) { y2 = y1 + screen }
3516
+ var docBottom = cm.doc.height + paddingVert(display)
3517
+ var atTop = y1 < snapMargin, atBottom = y2 > docBottom - snapMargin
3518
+ if (y1 < screentop) {
3519
+ result.scrollTop = atTop ? 0 : y1
3520
+ } else if (y2 > screentop + screen) {
3521
+ var newTop = Math.min(y1, (atBottom ? docBottom : y2) - screen)
3522
+ if (newTop != screentop) { result.scrollTop = newTop }
3523
+ }
3524
+
3525
+ var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft
3526
+ var screenw = displayWidth(cm) - (cm.options.fixedGutter ? display.gutters.offsetWidth : 0)
3527
+ var tooWide = x2 - x1 > screenw
3528
+ if (tooWide) { x2 = x1 + screenw }
3529
+ if (x1 < 10)
3530
+ { result.scrollLeft = 0 }
3531
+ else if (x1 < screenleft)
3532
+ { result.scrollLeft = Math.max(0, x1 - (tooWide ? 0 : 10)) }
3533
+ else if (x2 > screenw + screenleft - 3)
3534
+ { result.scrollLeft = x2 + (tooWide ? 0 : 10) - screenw }
3535
+ return result
3536
+ }
3537
+
3538
+ // Store a relative adjustment to the scroll position in the current
3539
+ // operation (to be applied when the operation finishes).
3540
+ function addToScrollPos(cm, left, top) {
3541
+ if (left != null || top != null) { resolveScrollToPos(cm) }
3542
+ if (left != null)
3543
+ { cm.curOp.scrollLeft = (cm.curOp.scrollLeft == null ? cm.doc.scrollLeft : cm.curOp.scrollLeft) + left }
3544
+ if (top != null)
3545
+ { cm.curOp.scrollTop = (cm.curOp.scrollTop == null ? cm.doc.scrollTop : cm.curOp.scrollTop) + top }
3546
+ }
3547
+
3548
+ // Make sure that at the end of the operation the current cursor is
3549
+ // shown.
3550
+ function ensureCursorVisible(cm) {
3551
+ resolveScrollToPos(cm)
3552
+ var cur = cm.getCursor(), from = cur, to = cur
3553
+ if (!cm.options.lineWrapping) {
3554
+ from = cur.ch ? Pos(cur.line, cur.ch - 1) : cur
3555
+ to = Pos(cur.line, cur.ch + 1)
3556
+ }
3557
+ cm.curOp.scrollToPos = {from: from, to: to, margin: cm.options.cursorScrollMargin, isCursor: true}
3558
+ }
3559
+
3560
+ // When an operation has its scrollToPos property set, and another
3561
+ // scroll action is applied before the end of the operation, this
3562
+ // 'simulates' scrolling that position into view in a cheap way, so
3563
+ // that the effect of intermediate scroll commands is not ignored.
3564
+ function resolveScrollToPos(cm) {
3565
+ var range$$1 = cm.curOp.scrollToPos
3566
+ if (range$$1) {
3567
+ cm.curOp.scrollToPos = null
3568
+ var from = estimateCoords(cm, range$$1.from), to = estimateCoords(cm, range$$1.to)
3569
+ var sPos = calculateScrollPos(cm, Math.min(from.left, to.left),
3570
+ Math.min(from.top, to.top) - range$$1.margin,
3571
+ Math.max(from.right, to.right),
3572
+ Math.max(from.bottom, to.bottom) + range$$1.margin)
3573
+ cm.scrollTo(sPos.scrollLeft, sPos.scrollTop)
3574
+ }
3575
+ }
3576
+
3577
+ // Operations are used to wrap a series of changes to the editor
3578
+ // state in such a way that each change won't have to update the
3579
+ // cursor and display (which would be awkward, slow, and
3580
+ // error-prone). Instead, display updates are batched and then all
3581
+ // combined and executed at once.
3582
+
3583
+ var nextOpId = 0
3584
+ // Start a new operation.
3585
+ function startOperation(cm) {
3586
+ cm.curOp = {
3587
+ cm: cm,
3588
+ viewChanged: false, // Flag that indicates that lines might need to be redrawn
3589
+ startHeight: cm.doc.height, // Used to detect need to update scrollbar
3590
+ forceUpdate: false, // Used to force a redraw
3591
+ updateInput: null, // Whether to reset the input textarea
3592
+ typing: false, // Whether this reset should be careful to leave existing text (for compositing)
3593
+ changeObjs: null, // Accumulated changes, for firing change events
3594
+ cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on
3595
+ cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already
3596
+ selectionChanged: false, // Whether the selection needs to be redrawn
3597
+ updateMaxLine: false, // Set when the widest line needs to be determined anew
3598
+ scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet
3599
+ scrollToPos: null, // Used to scroll to a specific position
3600
+ focus: false,
3601
+ id: ++nextOpId // Unique ID
3602
+ }
3603
+ pushOperation(cm.curOp)
3604
+ }
3605
+
3606
+ // Finish an operation, updating the display and signalling delayed events
3607
+ function endOperation(cm) {
3608
+ var op = cm.curOp
3609
+ finishOperation(op, function (group) {
3610
+ for (var i = 0; i < group.ops.length; i++)
3611
+ { group.ops[i].cm.curOp = null }
3612
+ endOperations(group)
3613
+ })
3614
+ }
3615
+
3616
+ // The DOM updates done when an operation finishes are batched so
3617
+ // that the minimum number of relayouts are required.
3618
+ function endOperations(group) {
3619
+ var ops = group.ops
3620
+ for (var i = 0; i < ops.length; i++) // Read DOM
3621
+ { endOperation_R1(ops[i]) }
3622
+ for (var i$1 = 0; i$1 < ops.length; i$1++) // Write DOM (maybe)
3623
+ { endOperation_W1(ops[i$1]) }
3624
+ for (var i$2 = 0; i$2 < ops.length; i$2++) // Read DOM
3625
+ { endOperation_R2(ops[i$2]) }
3626
+ for (var i$3 = 0; i$3 < ops.length; i$3++) // Write DOM (maybe)
3627
+ { endOperation_W2(ops[i$3]) }
3628
+ for (var i$4 = 0; i$4 < ops.length; i$4++) // Read DOM
3629
+ { endOperation_finish(ops[i$4]) }
3630
+ }
3631
+
3632
+ function endOperation_R1(op) {
3633
+ var cm = op.cm, display = cm.display
3634
+ maybeClipScrollbars(cm)
3635
+ if (op.updateMaxLine) { findMaxLine(cm) }
3636
+
3637
+ op.mustUpdate = op.viewChanged || op.forceUpdate || op.scrollTop != null ||
3638
+ op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom ||
3639
+ op.scrollToPos.to.line >= display.viewTo) ||
3640
+ display.maxLineChanged && cm.options.lineWrapping
3641
+ op.update = op.mustUpdate &&
3642
+ new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate)
3643
+ }
3644
+
3645
+ function endOperation_W1(op) {
3646
+ op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update)
3647
+ }
3648
+
3649
+ function endOperation_R2(op) {
3650
+ var cm = op.cm, display = cm.display
3651
+ if (op.updatedDisplay) { updateHeightsInViewport(cm) }
3652
+
3653
+ op.barMeasure = measureForScrollbars(cm)
3654
+
3655
+ // If the max line changed since it was last measured, measure it,
3656
+ // and ensure the document's width matches it.
3657
+ // updateDisplay_W2 will use these properties to do the actual resizing
3658
+ if (display.maxLineChanged && !cm.options.lineWrapping) {
3659
+ op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.length).left + 3
3660
+ cm.display.sizerWidth = op.adjustWidthTo
3661
+ op.barMeasure.scrollWidth =
3662
+ Math.max(display.scroller.clientWidth, display.sizer.offsetLeft + op.adjustWidthTo + scrollGap(cm) + cm.display.barWidth)
3663
+ op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm))
3664
+ }
3665
+
3666
+ if (op.updatedDisplay || op.selectionChanged)
3667
+ { op.preparedSelection = display.input.prepareSelection(op.focus) }
3668
+ }
3669
+
3670
+ function endOperation_W2(op) {
3671
+ var cm = op.cm
3672
+
3673
+ if (op.adjustWidthTo != null) {
3674
+ cm.display.sizer.style.minWidth = op.adjustWidthTo + "px"
3675
+ if (op.maxScrollLeft < cm.doc.scrollLeft)
3676
+ { setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true) }
3677
+ cm.display.maxLineChanged = false
3678
+ }
3679
+
3680
+ var takeFocus = op.focus && op.focus == activeElt() && (!document.hasFocus || document.hasFocus())
3681
+ if (op.preparedSelection)
3682
+ { cm.display.input.showSelection(op.preparedSelection, takeFocus) }
3683
+ if (op.updatedDisplay || op.startHeight != cm.doc.height)
3684
+ { updateScrollbars(cm, op.barMeasure) }
3685
+ if (op.updatedDisplay)
3686
+ { setDocumentHeight(cm, op.barMeasure) }
3687
+
3688
+ if (op.selectionChanged) { restartBlink(cm) }
3689
+
3690
+ if (cm.state.focused && op.updateInput)
3691
+ { cm.display.input.reset(op.typing) }
3692
+ if (takeFocus) { ensureFocus(op.cm) }
3693
+ }
3694
+
3695
+ function endOperation_finish(op) {
3696
+ var cm = op.cm, display = cm.display, doc = cm.doc
3697
+
3698
+ if (op.updatedDisplay) { postUpdateDisplay(cm, op.update) }
3699
+
3700
+ // Abort mouse wheel delta measurement, when scrolling explicitly
3701
+ if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos))
3702
+ { display.wheelStartX = display.wheelStartY = null }
3703
+
3704
+ // Propagate the scroll position to the actual DOM scroller
3705
+ if (op.scrollTop != null && (display.scroller.scrollTop != op.scrollTop || op.forceScroll)) {
3706
+ doc.scrollTop = Math.max(0, Math.min(display.scroller.scrollHeight - display.scroller.clientHeight, op.scrollTop))
3707
+ display.scrollbars.setScrollTop(doc.scrollTop)
3708
+ display.scroller.scrollTop = doc.scrollTop
3709
+ }
3710
+ if (op.scrollLeft != null && (display.scroller.scrollLeft != op.scrollLeft || op.forceScroll)) {
3711
+ doc.scrollLeft = Math.max(0, Math.min(display.scroller.scrollWidth - display.scroller.clientWidth, op.scrollLeft))
3712
+ display.scrollbars.setScrollLeft(doc.scrollLeft)
3713
+ display.scroller.scrollLeft = doc.scrollLeft
3714
+ alignHorizontally(cm)
3715
+ }
3716
+ // If we need to scroll a specific position into view, do so.
3717
+ if (op.scrollToPos) {
3718
+ var coords = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from),
3719
+ clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin)
3720
+ if (op.scrollToPos.isCursor && cm.state.focused) { maybeScrollWindow(cm, coords) }
3721
+ }
3722
+
3723
+ // Fire events for markers that are hidden/unidden by editing or
3724
+ // undoing
3725
+ var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers
3726
+ if (hidden) { for (var i = 0; i < hidden.length; ++i)
3727
+ { if (!hidden[i].lines.length) { signal(hidden[i], "hide") } } }
3728
+ if (unhidden) { for (var i$1 = 0; i$1 < unhidden.length; ++i$1)
3729
+ { if (unhidden[i$1].lines.length) { signal(unhidden[i$1], "unhide") } } }
3730
+
3731
+ if (display.wrapper.offsetHeight)
3732
+ { doc.scrollTop = cm.display.scroller.scrollTop }
3733
+
3734
+ // Fire change events, and delayed event handlers
3735
+ if (op.changeObjs)
3736
+ { signal(cm, "changes", cm, op.changeObjs) }
3737
+ if (op.update)
3738
+ { op.update.finish() }
3739
+ }
3740
+
3741
+ // Run the given function in an operation
3742
+ function runInOp(cm, f) {
3743
+ if (cm.curOp) { return f() }
3744
+ startOperation(cm)
3745
+ try { return f() }
3746
+ finally { endOperation(cm) }
3747
+ }
3748
+ // Wraps a function in an operation. Returns the wrapped function.
3749
+ function operation(cm, f) {
3750
+ return function() {
3751
+ if (cm.curOp) { return f.apply(cm, arguments) }
3752
+ startOperation(cm)
3753
+ try { return f.apply(cm, arguments) }
3754
+ finally { endOperation(cm) }
3755
+ }
3756
+ }
3757
+ // Used to add methods to editor and doc instances, wrapping them in
3758
+ // operations.
3759
+ function methodOp(f) {
3760
+ return function() {
3761
+ if (this.curOp) { return f.apply(this, arguments) }
3762
+ startOperation(this)
3763
+ try { return f.apply(this, arguments) }
3764
+ finally { endOperation(this) }
3765
+ }
3766
+ }
3767
+ function docMethodOp(f) {
3768
+ return function() {
3769
+ var cm = this.cm
3770
+ if (!cm || cm.curOp) { return f.apply(this, arguments) }
3771
+ startOperation(cm)
3772
+ try { return f.apply(this, arguments) }
3773
+ finally { endOperation(cm) }
3774
+ }
3775
+ }
3776
+
3777
+ // Updates the display.view data structure for a given change to the
3778
+ // document. From and to are in pre-change coordinates. Lendiff is
3779
+ // the amount of lines added or subtracted by the change. This is
3780
+ // used for changes that span multiple lines, or change the way
3781
+ // lines are divided into visual lines. regLineChange (below)
3782
+ // registers single-line changes.
3783
+ function regChange(cm, from, to, lendiff) {
3784
+ if (from == null) { from = cm.doc.first }
3785
+ if (to == null) { to = cm.doc.first + cm.doc.size }
3786
+ if (!lendiff) { lendiff = 0 }
3787
+
3788
+ var display = cm.display
3789
+ if (lendiff && to < display.viewTo &&
3790
+ (display.updateLineNumbers == null || display.updateLineNumbers > from))
3791
+ { display.updateLineNumbers = from }
3792
+
3793
+ cm.curOp.viewChanged = true
3794
+
3795
+ if (from >= display.viewTo) { // Change after
3796
+ if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo)
3797
+ { resetView(cm) }
3798
+ } else if (to <= display.viewFrom) { // Change before
3799
+ if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.viewFrom) {
3800
+ resetView(cm)
3801
+ } else {
3802
+ display.viewFrom += lendiff
3803
+ display.viewTo += lendiff
3804
+ }
3805
+ } else if (from <= display.viewFrom && to >= display.viewTo) { // Full overlap
3806
+ resetView(cm)
3807
+ } else if (from <= display.viewFrom) { // Top overlap
3808
+ var cut = viewCuttingPoint(cm, to, to + lendiff, 1)
3809
+ if (cut) {
3810
+ display.view = display.view.slice(cut.index)
3811
+ display.viewFrom = cut.lineN
3812
+ display.viewTo += lendiff
3813
+ } else {
3814
+ resetView(cm)
3815
+ }
3816
+ } else if (to >= display.viewTo) { // Bottom overlap
3817
+ var cut$1 = viewCuttingPoint(cm, from, from, -1)
3818
+ if (cut$1) {
3819
+ display.view = display.view.slice(0, cut$1.index)
3820
+ display.viewTo = cut$1.lineN
3821
+ } else {
3822
+ resetView(cm)
3823
+ }
3824
+ } else { // Gap in the middle
3825
+ var cutTop = viewCuttingPoint(cm, from, from, -1)
3826
+ var cutBot = viewCuttingPoint(cm, to, to + lendiff, 1)
3827
+ if (cutTop && cutBot) {
3828
+ display.view = display.view.slice(0, cutTop.index)
3829
+ .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN))
3830
+ .concat(display.view.slice(cutBot.index))
3831
+ display.viewTo += lendiff
3832
+ } else {
3833
+ resetView(cm)
3834
+ }
3835
+ }
3836
+
3837
+ var ext = display.externalMeasured
3838
+ if (ext) {
3839
+ if (to < ext.lineN)
3840
+ { ext.lineN += lendiff }
3841
+ else if (from < ext.lineN + ext.size)
3842
+ { display.externalMeasured = null }
3843
+ }
3844
+ }
3845
+
3846
+ // Register a change to a single line. Type must be one of "text",
3847
+ // "gutter", "class", "widget"
3848
+ function regLineChange(cm, line, type) {
3849
+ cm.curOp.viewChanged = true
3850
+ var display = cm.display, ext = cm.display.externalMeasured
3851
+ if (ext && line >= ext.lineN && line < ext.lineN + ext.size)
3852
+ { display.externalMeasured = null }
3853
+
3854
+ if (line < display.viewFrom || line >= display.viewTo) { return }
3855
+ var lineView = display.view[findViewIndex(cm, line)]
3856
+ if (lineView.node == null) { return }
3857
+ var arr = lineView.changes || (lineView.changes = [])
3858
+ if (indexOf(arr, type) == -1) { arr.push(type) }
3859
+ }
3860
+
3861
+ // Clear the view.
3862
+ function resetView(cm) {
3863
+ cm.display.viewFrom = cm.display.viewTo = cm.doc.first
3864
+ cm.display.view = []
3865
+ cm.display.viewOffset = 0
3866
+ }
3867
+
3868
+ function viewCuttingPoint(cm, oldN, newN, dir) {
3869
+ var index = findViewIndex(cm, oldN), diff, view = cm.display.view
3870
+ if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size)
3871
+ { return {index: index, lineN: newN} }
3872
+ var n = cm.display.viewFrom
3873
+ for (var i = 0; i < index; i++)
3874
+ { n += view[i].size }
3875
+ if (n != oldN) {
3876
+ if (dir > 0) {
3877
+ if (index == view.length - 1) { return null }
3878
+ diff = (n + view[index].size) - oldN
3879
+ index++
3880
+ } else {
3881
+ diff = n - oldN
3882
+ }
3883
+ oldN += diff; newN += diff
3884
+ }
3885
+ while (visualLineNo(cm.doc, newN) != newN) {
3886
+ if (index == (dir < 0 ? 0 : view.length - 1)) { return null }
3887
+ newN += dir * view[index - (dir < 0 ? 1 : 0)].size
3888
+ index += dir
3889
+ }
3890
+ return {index: index, lineN: newN}
3891
+ }
3892
+
3893
+ // Force the view to cover a given range, adding empty view element
3894
+ // or clipping off existing ones as needed.
3895
+ function adjustView(cm, from, to) {
3896
+ var display = cm.display, view = display.view
3897
+ if (view.length == 0 || from >= display.viewTo || to <= display.viewFrom) {
3898
+ display.view = buildViewArray(cm, from, to)
3899
+ display.viewFrom = from
3900
+ } else {
3901
+ if (display.viewFrom > from)
3902
+ { display.view = buildViewArray(cm, from, display.viewFrom).concat(display.view) }
3903
+ else if (display.viewFrom < from)
3904
+ { display.view = display.view.slice(findViewIndex(cm, from)) }
3905
+ display.viewFrom = from
3906
+ if (display.viewTo < to)
3907
+ { display.view = display.view.concat(buildViewArray(cm, display.viewTo, to)) }
3908
+ else if (display.viewTo > to)
3909
+ { display.view = display.view.slice(0, findViewIndex(cm, to)) }
3910
+ }
3911
+ display.viewTo = to
3912
+ }
3913
+
3914
+ // Count the number of lines in the view whose DOM representation is
3915
+ // out of date (or nonexistent).
3916
+ function countDirtyView(cm) {
3917
+ var view = cm.display.view, dirty = 0
3918
+ for (var i = 0; i < view.length; i++) {
3919
+ var lineView = view[i]
3920
+ if (!lineView.hidden && (!lineView.node || lineView.changes)) { ++dirty }
3921
+ }
3922
+ return dirty
3923
+ }
3924
+
3925
+ // HIGHLIGHT WORKER
3926
+
3927
+ function startWorker(cm, time) {
3928
+ if (cm.doc.mode.startState && cm.doc.frontier < cm.display.viewTo)
3929
+ { cm.state.highlight.set(time, bind(highlightWorker, cm)) }
3930
+ }
3931
+
3932
+ function highlightWorker(cm) {
3933
+ var doc = cm.doc
3934
+ if (doc.frontier < doc.first) { doc.frontier = doc.first }
3935
+ if (doc.frontier >= cm.display.viewTo) { return }
3936
+ var end = +new Date + cm.options.workTime
3937
+ var state = copyState(doc.mode, getStateBefore(cm, doc.frontier))
3938
+ var changedLines = []
3939
+
3940
+ doc.iter(doc.frontier, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function (line) {
3941
+ if (doc.frontier >= cm.display.viewFrom) { // Visible
3942
+ var oldStyles = line.styles, tooLong = line.text.length > cm.options.maxHighlightLength
3943
+ var highlighted = highlightLine(cm, line, tooLong ? copyState(doc.mode, state) : state, true)
3944
+ line.styles = highlighted.styles
3945
+ var oldCls = line.styleClasses, newCls = highlighted.classes
3946
+ if (newCls) { line.styleClasses = newCls }
3947
+ else if (oldCls) { line.styleClasses = null }
3948
+ var ischange = !oldStyles || oldStyles.length != line.styles.length ||
3949
+ oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgClass || oldCls.textClass != newCls.textClass)
3950
+ for (var i = 0; !ischange && i < oldStyles.length; ++i) { ischange = oldStyles[i] != line.styles[i] }
3951
+ if (ischange) { changedLines.push(doc.frontier) }
3952
+ line.stateAfter = tooLong ? state : copyState(doc.mode, state)
3953
+ } else {
3954
+ if (line.text.length <= cm.options.maxHighlightLength)
3955
+ { processLine(cm, line.text, state) }
3956
+ line.stateAfter = doc.frontier % 5 == 0 ? copyState(doc.mode, state) : null
3957
+ }
3958
+ ++doc.frontier
3959
+ if (+new Date > end) {
3960
+ startWorker(cm, cm.options.workDelay)
3961
+ return true
3962
+ }
3963
+ })
3964
+ if (changedLines.length) { runInOp(cm, function () {
3965
+ for (var i = 0; i < changedLines.length; i++)
3966
+ { regLineChange(cm, changedLines[i], "text") }
3967
+ }) }
3968
+ }
3969
+
3970
+ // DISPLAY DRAWING
3971
+
3972
+ var DisplayUpdate = function DisplayUpdate(cm, viewport, force) {
3973
+ var display = cm.display
3974
+
3975
+ this.viewport = viewport
3976
+ // Store some values that we'll need later (but don't want to force a relayout for)
3977
+ this.visible = visibleLines(display, cm.doc, viewport)
3978
+ this.editorIsHidden = !display.wrapper.offsetWidth
3979
+ this.wrapperHeight = display.wrapper.clientHeight
3980
+ this.wrapperWidth = display.wrapper.clientWidth
3981
+ this.oldDisplayWidth = displayWidth(cm)
3982
+ this.force = force
3983
+ this.dims = getDimensions(cm)
3984
+ this.events = []
3985
+ };
3986
+
3987
+ DisplayUpdate.prototype.signal = function signal$1 (emitter, type) {
3988
+ if (hasHandler(emitter, type))
3989
+ { this.events.push(arguments) }
3990
+ };
3991
+ DisplayUpdate.prototype.finish = function finish () {
3992
+ var this$1 = this;
3993
+
3994
+ for (var i = 0; i < this.events.length; i++)
3995
+ { signal.apply(null, this$1.events[i]) }
3996
+ };
3997
+
3998
+ function maybeClipScrollbars(cm) {
3999
+ var display = cm.display
4000
+ if (!display.scrollbarsClipped && display.scroller.offsetWidth) {
4001
+ display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.clientWidth
4002
+ display.heightForcer.style.height = scrollGap(cm) + "px"
4003
+ display.sizer.style.marginBottom = -display.nativeBarWidth + "px"
4004
+ display.sizer.style.borderRightWidth = scrollGap(cm) + "px"
4005
+ display.scrollbarsClipped = true
4006
+ }
4007
+ }
4008
+
4009
+ // Does the actual updating of the line display. Bails out
4010
+ // (returning false) when there is nothing to be done and forced is
4011
+ // false.
4012
+ function updateDisplayIfNeeded(cm, update) {
4013
+ var display = cm.display, doc = cm.doc
4014
+
4015
+ if (update.editorIsHidden) {
4016
+ resetView(cm)
4017
+ return false
4018
+ }
4019
+
4020
+ // Bail out if the visible area is already rendered and nothing changed.
4021
+ if (!update.force &&
4022
+ update.visible.from >= display.viewFrom && update.visible.to <= display.viewTo &&
4023
+ (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo) &&
4024
+ display.renderedView == display.view && countDirtyView(cm) == 0)
4025
+ { return false }
4026
+
4027
+ if (maybeUpdateLineNumberWidth(cm)) {
4028
+ resetView(cm)
4029
+ update.dims = getDimensions(cm)
4030
+ }
4031
+
4032
+ // Compute a suitable new viewport (from & to)
4033
+ var end = doc.first + doc.size
4034
+ var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first)
4035
+ var to = Math.min(end, update.visible.to + cm.options.viewportMargin)
4036
+ if (display.viewFrom < from && from - display.viewFrom < 20) { from = Math.max(doc.first, display.viewFrom) }
4037
+ if (display.viewTo > to && display.viewTo - to < 20) { to = Math.min(end, display.viewTo) }
4038
+ if (sawCollapsedSpans) {
4039
+ from = visualLineNo(cm.doc, from)
4040
+ to = visualLineEndNo(cm.doc, to)
4041
+ }
4042
+
4043
+ var different = from != display.viewFrom || to != display.viewTo ||
4044
+ display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != update.wrapperWidth
4045
+ adjustView(cm, from, to)
4046
+
4047
+ display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom))
4048
+ // Position the mover div to align with the current scroll position
4049
+ cm.display.mover.style.top = display.viewOffset + "px"
4050
+
4051
+ var toUpdate = countDirtyView(cm)
4052
+ if (!different && toUpdate == 0 && !update.force && display.renderedView == display.view &&
4053
+ (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo))
4054
+ { return false }
4055
+
4056
+ // For big changes, we hide the enclosing element during the
4057
+ // update, since that speeds up the operations on most browsers.
4058
+ var focused = activeElt()
4059
+ if (toUpdate > 4) { display.lineDiv.style.display = "none" }
4060
+ patchDisplay(cm, display.updateLineNumbers, update.dims)
4061
+ if (toUpdate > 4) { display.lineDiv.style.display = "" }
4062
+ display.renderedView = display.view
4063
+ // There might have been a widget with a focused element that got
4064
+ // hidden or updated, if so re-focus it.
4065
+ if (focused && activeElt() != focused && focused.offsetHeight) { focused.focus() }
4066
+
4067
+ // Prevent selection and cursors from interfering with the scroll
4068
+ // width and height.
4069
+ removeChildren(display.cursorDiv)
4070
+ removeChildren(display.selectionDiv)
4071
+ display.gutters.style.height = display.sizer.style.minHeight = 0
4072
+
4073
+ if (different) {
4074
+ display.lastWrapHeight = update.wrapperHeight
4075
+ display.lastWrapWidth = update.wrapperWidth
4076
+ startWorker(cm, 400)
4077
+ }
4078
+
4079
+ display.updateLineNumbers = null
4080
+
4081
+ return true
4082
+ }
4083
+
4084
+ function postUpdateDisplay(cm, update) {
4085
+ var viewport = update.viewport
4086
+
4087
+ for (var first = true;; first = false) {
4088
+ if (!first || !cm.options.lineWrapping || update.oldDisplayWidth == displayWidth(cm)) {
4089
+ // Clip forced viewport to actual scrollable area.
4090
+ if (viewport && viewport.top != null)
4091
+ { viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - displayHeight(cm), viewport.top)} }
4092
+ // Updated line heights might result in the drawn area not
4093
+ // actually covering the viewport. Keep looping until it does.
4094
+ update.visible = visibleLines(cm.display, cm.doc, viewport)
4095
+ if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo)
4096
+ { break }
4097
+ }
4098
+ if (!updateDisplayIfNeeded(cm, update)) { break }
4099
+ updateHeightsInViewport(cm)
4100
+ var barMeasure = measureForScrollbars(cm)
4101
+ updateSelection(cm)
4102
+ updateScrollbars(cm, barMeasure)
4103
+ setDocumentHeight(cm, barMeasure)
4104
+ }
4105
+
4106
+ update.signal(cm, "update", cm)
4107
+ if (cm.display.viewFrom != cm.display.reportedViewFrom || cm.display.viewTo != cm.display.reportedViewTo) {
4108
+ update.signal(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo)
4109
+ cm.display.reportedViewFrom = cm.display.viewFrom; cm.display.reportedViewTo = cm.display.viewTo
4110
+ }
4111
+ }
4112
+
4113
+ function updateDisplaySimple(cm, viewport) {
4114
+ var update = new DisplayUpdate(cm, viewport)
4115
+ if (updateDisplayIfNeeded(cm, update)) {
4116
+ updateHeightsInViewport(cm)
4117
+ postUpdateDisplay(cm, update)
4118
+ var barMeasure = measureForScrollbars(cm)
4119
+ updateSelection(cm)
4120
+ updateScrollbars(cm, barMeasure)
4121
+ setDocumentHeight(cm, barMeasure)
4122
+ update.finish()
4123
+ }
4124
+ }
4125
+
4126
+ // Sync the actual display DOM structure with display.view, removing
4127
+ // nodes for lines that are no longer in view, and creating the ones
4128
+ // that are not there yet, and updating the ones that are out of
4129
+ // date.
4130
+ function patchDisplay(cm, updateNumbersFrom, dims) {
4131
+ var display = cm.display, lineNumbers = cm.options.lineNumbers
4132
+ var container = display.lineDiv, cur = container.firstChild
4133
+
4134
+ function rm(node) {
4135
+ var next = node.nextSibling
4136
+ // Works around a throw-scroll bug in OS X Webkit
4137
+ if (webkit && mac && cm.display.currentWheelTarget == node)
4138
+ { node.style.display = "none" }
4139
+ else
4140
+ { node.parentNode.removeChild(node) }
4141
+ return next
4142
+ }
4143
+
4144
+ var view = display.view, lineN = display.viewFrom
4145
+ // Loop over the elements in the view, syncing cur (the DOM nodes
4146
+ // in display.lineDiv) with the view as we go.
4147
+ for (var i = 0; i < view.length; i++) {
4148
+ var lineView = view[i]
4149
+ if (lineView.hidden) {
4150
+ } else if (!lineView.node || lineView.node.parentNode != container) { // Not drawn yet
4151
+ var node = buildLineElement(cm, lineView, lineN, dims)
4152
+ container.insertBefore(node, cur)
4153
+ } else { // Already drawn
4154
+ while (cur != lineView.node) { cur = rm(cur) }
4155
+ var updateNumber = lineNumbers && updateNumbersFrom != null &&
4156
+ updateNumbersFrom <= lineN && lineView.lineNumber
4157
+ if (lineView.changes) {
4158
+ if (indexOf(lineView.changes, "gutter") > -1) { updateNumber = false }
4159
+ updateLineForChanges(cm, lineView, lineN, dims)
4160
+ }
4161
+ if (updateNumber) {
4162
+ removeChildren(lineView.lineNumber)
4163
+ lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm.options, lineN)))
4164
+ }
4165
+ cur = lineView.node.nextSibling
4166
+ }
4167
+ lineN += lineView.size
4168
+ }
4169
+ while (cur) { cur = rm(cur) }
4170
+ }
4171
+
4172
+ function updateGutterSpace(cm) {
4173
+ var width = cm.display.gutters.offsetWidth
4174
+ cm.display.sizer.style.marginLeft = width + "px"
4175
+ }
4176
+
4177
+ function setDocumentHeight(cm, measure) {
4178
+ cm.display.sizer.style.minHeight = measure.docHeight + "px"
4179
+ cm.display.heightForcer.style.top = measure.docHeight + "px"
4180
+ cm.display.gutters.style.height = (measure.docHeight + cm.display.barHeight + scrollGap(cm)) + "px"
4181
+ }
4182
+
4183
+ // Rebuild the gutter elements, ensure the margin to the left of the
4184
+ // code matches their width.
4185
+ function updateGutters(cm) {
4186
+ var gutters = cm.display.gutters, specs = cm.options.gutters
4187
+ removeChildren(gutters)
4188
+ var i = 0
4189
+ for (; i < specs.length; ++i) {
4190
+ var gutterClass = specs[i]
4191
+ var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + gutterClass))
4192
+ if (gutterClass == "CodeMirror-linenumbers") {
4193
+ cm.display.lineGutter = gElt
4194
+ gElt.style.width = (cm.display.lineNumWidth || 1) + "px"
4195
+ }
4196
+ }
4197
+ gutters.style.display = i ? "" : "none"
4198
+ updateGutterSpace(cm)
4199
+ }
4200
+
4201
+ // Make sure the gutters options contains the element
4202
+ // "CodeMirror-linenumbers" when the lineNumbers option is true.
4203
+ function setGuttersForLineNumbers(options) {
4204
+ var found = indexOf(options.gutters, "CodeMirror-linenumbers")
4205
+ if (found == -1 && options.lineNumbers) {
4206
+ options.gutters = options.gutters.concat(["CodeMirror-linenumbers"])
4207
+ } else if (found > -1 && !options.lineNumbers) {
4208
+ options.gutters = options.gutters.slice(0)
4209
+ options.gutters.splice(found, 1)
4210
+ }
4211
+ }
4212
+
4213
+ // Selection objects are immutable. A new one is created every time
4214
+ // the selection changes. A selection is one or more non-overlapping
4215
+ // (and non-touching) ranges, sorted, and an integer that indicates
4216
+ // which one is the primary selection (the one that's scrolled into
4217
+ // view, that getCursor returns, etc).
4218
+ function Selection(ranges, primIndex) {
4219
+ this.ranges = ranges
4220
+ this.primIndex = primIndex
4221
+ }
4222
+
4223
+ Selection.prototype = {
4224
+ primary: function() { return this.ranges[this.primIndex] },
4225
+ equals: function(other) {
4226
+ var this$1 = this;
4227
+
4228
+ if (other == this) { return true }
4229
+ if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) { return false }
4230
+ for (var i = 0; i < this.ranges.length; i++) {
4231
+ var here = this$1.ranges[i], there = other.ranges[i]
4232
+ if (cmp(here.anchor, there.anchor) != 0 || cmp(here.head, there.head) != 0) { return false }
4233
+ }
4234
+ return true
4235
+ },
4236
+ deepCopy: function() {
4237
+ var this$1 = this;
4238
+
4239
+ var out = []
4240
+ for (var i = 0; i < this.ranges.length; i++)
4241
+ { out[i] = new Range(copyPos(this$1.ranges[i].anchor), copyPos(this$1.ranges[i].head)) }
4242
+ return new Selection(out, this.primIndex)
4243
+ },
4244
+ somethingSelected: function() {
4245
+ var this$1 = this;
4246
+
4247
+ for (var i = 0; i < this.ranges.length; i++)
4248
+ { if (!this$1.ranges[i].empty()) { return true } }
4249
+ return false
4250
+ },
4251
+ contains: function(pos, end) {
4252
+ var this$1 = this;
4253
+
4254
+ if (!end) { end = pos }
4255
+ for (var i = 0; i < this.ranges.length; i++) {
4256
+ var range = this$1.ranges[i]
4257
+ if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0)
4258
+ { return i }
4259
+ }
4260
+ return -1
4261
+ }
4262
+ }
4263
+
4264
+ function Range(anchor, head) {
4265
+ this.anchor = anchor; this.head = head
4266
+ }
4267
+
4268
+ Range.prototype = {
4269
+ from: function() { return minPos(this.anchor, this.head) },
4270
+ to: function() { return maxPos(this.anchor, this.head) },
4271
+ empty: function() {
4272
+ return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch
4273
+ }
4274
+ }
4275
+
4276
+ // Take an unsorted, potentially overlapping set of ranges, and
4277
+ // build a selection out of it. 'Consumes' ranges array (modifying
4278
+ // it).
4279
+ function normalizeSelection(ranges, primIndex) {
4280
+ var prim = ranges[primIndex]
4281
+ ranges.sort(function (a, b) { return cmp(a.from(), b.from()); })
4282
+ primIndex = indexOf(ranges, prim)
4283
+ for (var i = 1; i < ranges.length; i++) {
4284
+ var cur = ranges[i], prev = ranges[i - 1]
4285
+ if (cmp(prev.to(), cur.from()) >= 0) {
4286
+ var from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to())
4287
+ var inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head
4288
+ if (i <= primIndex) { --primIndex }
4289
+ ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to))
4290
+ }
4291
+ }
4292
+ return new Selection(ranges, primIndex)
4293
+ }
4294
+
4295
+ function simpleSelection(anchor, head) {
4296
+ return new Selection([new Range(anchor, head || anchor)], 0)
4297
+ }
4298
+
4299
+ // Compute the position of the end of a change (its 'to' property
4300
+ // refers to the pre-change end).
4301
+ function changeEnd(change) {
4302
+ if (!change.text) { return change.to }
4303
+ return Pos(change.from.line + change.text.length - 1,
4304
+ lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0))
4305
+ }
4306
+
4307
+ // Adjust a position to refer to the post-change position of the
4308
+ // same text, or the end of the change if the change covers it.
4309
+ function adjustForChange(pos, change) {
4310
+ if (cmp(pos, change.from) < 0) { return pos }
4311
+ if (cmp(pos, change.to) <= 0) { return changeEnd(change) }
4312
+
4313
+ var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch
4314
+ if (pos.line == change.to.line) { ch += changeEnd(change).ch - change.to.ch }
4315
+ return Pos(line, ch)
4316
+ }
4317
+
4318
+ function computeSelAfterChange(doc, change) {
4319
+ var out = []
4320
+ for (var i = 0; i < doc.sel.ranges.length; i++) {
4321
+ var range = doc.sel.ranges[i]
4322
+ out.push(new Range(adjustForChange(range.anchor, change),
4323
+ adjustForChange(range.head, change)))
4324
+ }
4325
+ return normalizeSelection(out, doc.sel.primIndex)
4326
+ }
4327
+
4328
+ function offsetPos(pos, old, nw) {
4329
+ if (pos.line == old.line)
4330
+ { return Pos(nw.line, pos.ch - old.ch + nw.ch) }
4331
+ else
4332
+ { return Pos(nw.line + (pos.line - old.line), pos.ch) }
4333
+ }
4334
+
4335
+ // Used by replaceSelections to allow moving the selection to the
4336
+ // start or around the replaced test. Hint may be "start" or "around".
4337
+ function computeReplacedSel(doc, changes, hint) {
4338
+ var out = []
4339
+ var oldPrev = Pos(doc.first, 0), newPrev = oldPrev
4340
+ for (var i = 0; i < changes.length; i++) {
4341
+ var change = changes[i]
4342
+ var from = offsetPos(change.from, oldPrev, newPrev)
4343
+ var to = offsetPos(changeEnd(change), oldPrev, newPrev)
4344
+ oldPrev = change.to
4345
+ newPrev = to
4346
+ if (hint == "around") {
4347
+ var range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) < 0
4348
+ out[i] = new Range(inv ? to : from, inv ? from : to)
4349
+ } else {
4350
+ out[i] = new Range(from, from)
4351
+ }
4352
+ }
4353
+ return new Selection(out, doc.sel.primIndex)
4354
+ }
4355
+
4356
+ // Used to get the editor into a consistent state again when options change.
4357
+
4358
+ function loadMode(cm) {
4359
+ cm.doc.mode = getMode(cm.options, cm.doc.modeOption)
4360
+ resetModeState(cm)
4361
+ }
4362
+
4363
+ function resetModeState(cm) {
4364
+ cm.doc.iter(function (line) {
4365
+ if (line.stateAfter) { line.stateAfter = null }
4366
+ if (line.styles) { line.styles = null }
4367
+ })
4368
+ cm.doc.frontier = cm.doc.first
4369
+ startWorker(cm, 100)
4370
+ cm.state.modeGen++
4371
+ if (cm.curOp) { regChange(cm) }
4372
+ }
4373
+
4374
+ // DOCUMENT DATA STRUCTURE
4375
+
4376
+ // By default, updates that start and end at the beginning of a line
4377
+ // are treated specially, in order to make the association of line
4378
+ // widgets and marker elements with the text behave more intuitive.
4379
+ function isWholeLineUpdate(doc, change) {
4380
+ return change.from.ch == 0 && change.to.ch == 0 && lst(change.text) == "" &&
4381
+ (!doc.cm || doc.cm.options.wholeLineUpdateBefore)
4382
+ }
4383
+
4384
+ // Perform a change on the document data structure.
4385
+ function updateDoc(doc, change, markedSpans, estimateHeight$$1) {
4386
+ function spansFor(n) {return markedSpans ? markedSpans[n] : null}
4387
+ function update(line, text, spans) {
4388
+ updateLine(line, text, spans, estimateHeight$$1)
4389
+ signalLater(line, "change", line, change)
4390
+ }
4391
+ function linesFor(start, end) {
4392
+ var result = []
4393
+ for (var i = start; i < end; ++i)
4394
+ { result.push(new Line(text[i], spansFor(i), estimateHeight$$1)) }
4395
+ return result
4396
+ }
4397
+
4398
+ var from = change.from, to = change.to, text = change.text
4399
+ var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line)
4400
+ var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line
4401
+
4402
+ // Adjust the line structure
4403
+ if (change.full) {
4404
+ doc.insert(0, linesFor(0, text.length))
4405
+ doc.remove(text.length, doc.size - text.length)
4406
+ } else if (isWholeLineUpdate(doc, change)) {
4407
+ // This is a whole-line replace. Treated specially to make
4408
+ // sure line objects move the way they are supposed to.
4409
+ var added = linesFor(0, text.length - 1)
4410
+ update(lastLine, lastLine.text, lastSpans)
4411
+ if (nlines) { doc.remove(from.line, nlines) }
4412
+ if (added.length) { doc.insert(from.line, added) }
4413
+ } else if (firstLine == lastLine) {
4414
+ if (text.length == 1) {
4415
+ update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans)
4416
+ } else {
4417
+ var added$1 = linesFor(1, text.length - 1)
4418
+ added$1.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight$$1))
4419
+ update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0))
4420
+ doc.insert(from.line + 1, added$1)
4421
+ }
4422
+ } else if (text.length == 1) {
4423
+ update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0))
4424
+ doc.remove(from.line + 1, nlines)
4425
+ } else {
4426
+ update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0))
4427
+ update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans)
4428
+ var added$2 = linesFor(1, text.length - 1)
4429
+ if (nlines > 1) { doc.remove(from.line + 1, nlines - 1) }
4430
+ doc.insert(from.line + 1, added$2)
4431
+ }
4432
+
4433
+ signalLater(doc, "change", doc, change)
4434
+ }
4435
+
4436
+ // Call f for all linked documents.
4437
+ function linkedDocs(doc, f, sharedHistOnly) {
4438
+ function propagate(doc, skip, sharedHist) {
4439
+ if (doc.linked) { for (var i = 0; i < doc.linked.length; ++i) {
4440
+ var rel = doc.linked[i]
4441
+ if (rel.doc == skip) { continue }
4442
+ var shared = sharedHist && rel.sharedHist
4443
+ if (sharedHistOnly && !shared) { continue }
4444
+ f(rel.doc, shared)
4445
+ propagate(rel.doc, doc, shared)
4446
+ } }
4447
+ }
4448
+ propagate(doc, null, true)
4449
+ }
4450
+
4451
+ // Attach a document to an editor.
4452
+ function attachDoc(cm, doc) {
4453
+ if (doc.cm) { throw new Error("This document is already in use.") }
4454
+ cm.doc = doc
4455
+ doc.cm = cm
4456
+ estimateLineHeights(cm)
4457
+ loadMode(cm)
4458
+ if (!cm.options.lineWrapping) { findMaxLine(cm) }
4459
+ cm.options.mode = doc.modeOption
4460
+ regChange(cm)
4461
+ }
4462
+
4463
+ function History(startGen) {
4464
+ // Arrays of change events and selections. Doing something adds an
4465
+ // event to done and clears undo. Undoing moves events from done
4466
+ // to undone, redoing moves them in the other direction.
4467
+ this.done = []; this.undone = []
4468
+ this.undoDepth = Infinity
4469
+ // Used to track when changes can be merged into a single undo
4470
+ // event
4471
+ this.lastModTime = this.lastSelTime = 0
4472
+ this.lastOp = this.lastSelOp = null
4473
+ this.lastOrigin = this.lastSelOrigin = null
4474
+ // Used by the isClean() method
4475
+ this.generation = this.maxGeneration = startGen || 1
4476
+ }
4477
+
4478
+ // Create a history change event from an updateDoc-style change
4479
+ // object.
4480
+ function historyChangeFromChange(doc, change) {
4481
+ var histChange = {from: copyPos(change.from), to: changeEnd(change), text: getBetween(doc, change.from, change.to)}
4482
+ attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1)
4483
+ linkedDocs(doc, function (doc) { return attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1); }, true)
4484
+ return histChange
4485
+ }
4486
+
4487
+ // Pop all selection events off the end of a history array. Stop at
4488
+ // a change event.
4489
+ function clearSelectionEvents(array) {
4490
+ while (array.length) {
4491
+ var last = lst(array)
4492
+ if (last.ranges) { array.pop() }
4493
+ else { break }
4494
+ }
4495
+ }
4496
+
4497
+ // Find the top change event in the history. Pop off selection
4498
+ // events that are in the way.
4499
+ function lastChangeEvent(hist, force) {
4500
+ if (force) {
4501
+ clearSelectionEvents(hist.done)
4502
+ return lst(hist.done)
4503
+ } else if (hist.done.length && !lst(hist.done).ranges) {
4504
+ return lst(hist.done)
4505
+ } else if (hist.done.length > 1 && !hist.done[hist.done.length - 2].ranges) {
4506
+ hist.done.pop()
4507
+ return lst(hist.done)
4508
+ }
4509
+ }
4510
+
4511
+ // Register a change in the history. Merges changes that are within
4512
+ // a single operation, or are close together with an origin that
4513
+ // allows merging (starting with "+") into a single event.
4514
+ function addChangeToHistory(doc, change, selAfter, opId) {
4515
+ var hist = doc.history
4516
+ hist.undone.length = 0
4517
+ var time = +new Date, cur
4518
+ var last
4519
+
4520
+ if ((hist.lastOp == opId ||
4521
+ hist.lastOrigin == change.origin && change.origin &&
4522
+ ((change.origin.charAt(0) == "+" && doc.cm && hist.lastModTime > time - doc.cm.options.historyEventDelay) ||
4523
+ change.origin.charAt(0) == "*")) &&
4524
+ (cur = lastChangeEvent(hist, hist.lastOp == opId))) {
4525
+ // Merge this change into the last event
4526
+ last = lst(cur.changes)
4527
+ if (cmp(change.from, change.to) == 0 && cmp(change.from, last.to) == 0) {
4528
+ // Optimized case for simple insertion -- don't want to add
4529
+ // new changesets for every character typed
4530
+ last.to = changeEnd(change)
4531
+ } else {
4532
+ // Add new sub-event
4533
+ cur.changes.push(historyChangeFromChange(doc, change))
4534
+ }
4535
+ } else {
4536
+ // Can not be merged, start a new event.
4537
+ var before = lst(hist.done)
4538
+ if (!before || !before.ranges)
4539
+ { pushSelectionToHistory(doc.sel, hist.done) }
4540
+ cur = {changes: [historyChangeFromChange(doc, change)],
4541
+ generation: hist.generation}
4542
+ hist.done.push(cur)
4543
+ while (hist.done.length > hist.undoDepth) {
4544
+ hist.done.shift()
4545
+ if (!hist.done[0].ranges) { hist.done.shift() }
4546
+ }
4547
+ }
4548
+ hist.done.push(selAfter)
4549
+ hist.generation = ++hist.maxGeneration
4550
+ hist.lastModTime = hist.lastSelTime = time
4551
+ hist.lastOp = hist.lastSelOp = opId
4552
+ hist.lastOrigin = hist.lastSelOrigin = change.origin
4553
+
4554
+ if (!last) { signal(doc, "historyAdded") }
4555
+ }
4556
+
4557
+ function selectionEventCanBeMerged(doc, origin, prev, sel) {
4558
+ var ch = origin.charAt(0)
4559
+ return ch == "*" ||
4560
+ ch == "+" &&
4561
+ prev.ranges.length == sel.ranges.length &&
4562
+ prev.somethingSelected() == sel.somethingSelected() &&
4563
+ new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEventDelay : 500)
4564
+ }
4565
+
4566
+ // Called whenever the selection changes, sets the new selection as
4567
+ // the pending selection in the history, and pushes the old pending
4568
+ // selection into the 'done' array when it was significantly
4569
+ // different (in number of selected ranges, emptiness, or time).
4570
+ function addSelectionToHistory(doc, sel, opId, options) {
4571
+ var hist = doc.history, origin = options && options.origin
4572
+
4573
+ // A new event is started when the previous origin does not match
4574
+ // the current, or the origins don't allow matching. Origins
4575
+ // starting with * are always merged, those starting with + are
4576
+ // merged when similar and close together in time.
4577
+ if (opId == hist.lastSelOp ||
4578
+ (origin && hist.lastSelOrigin == origin &&
4579
+ (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin ||
4580
+ selectionEventCanBeMerged(doc, origin, lst(hist.done), sel))))
4581
+ { hist.done[hist.done.length - 1] = sel }
4582
+ else
4583
+ { pushSelectionToHistory(sel, hist.done) }
4584
+
4585
+ hist.lastSelTime = +new Date
4586
+ hist.lastSelOrigin = origin
4587
+ hist.lastSelOp = opId
4588
+ if (options && options.clearRedo !== false)
4589
+ { clearSelectionEvents(hist.undone) }
4590
+ }
4591
+
4592
+ function pushSelectionToHistory(sel, dest) {
4593
+ var top = lst(dest)
4594
+ if (!(top && top.ranges && top.equals(sel)))
4595
+ { dest.push(sel) }
4596
+ }
4597
+
4598
+ // Used to store marked span information in the history.
4599
+ function attachLocalSpans(doc, change, from, to) {
4600
+ var existing = change["spans_" + doc.id], n = 0
4601
+ doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function (line) {
4602
+ if (line.markedSpans)
4603
+ { (existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans }
4604
+ ++n
4605
+ })
4606
+ }
4607
+
4608
+ // When un/re-doing restores text containing marked spans, those
4609
+ // that have been explicitly cleared should not be restored.
4610
+ function removeClearedSpans(spans) {
4611
+ if (!spans) { return null }
4612
+ var out
4613
+ for (var i = 0; i < spans.length; ++i) {
4614
+ if (spans[i].marker.explicitlyCleared) { if (!out) { out = spans.slice(0, i) } }
4615
+ else if (out) { out.push(spans[i]) }
4616
+ }
4617
+ return !out ? spans : out.length ? out : null
4618
+ }
4619
+
4620
+ // Retrieve and filter the old marked spans stored in a change event.
4621
+ function getOldSpans(doc, change) {
4622
+ var found = change["spans_" + doc.id]
4623
+ if (!found) { return null }
4624
+ var nw = []
4625
+ for (var i = 0; i < change.text.length; ++i)
4626
+ { nw.push(removeClearedSpans(found[i])) }
4627
+ return nw
4628
+ }
4629
+
4630
+ // Used for un/re-doing changes from the history. Combines the
4631
+ // result of computing the existing spans with the set of spans that
4632
+ // existed in the history (so that deleting around a span and then
4633
+ // undoing brings back the span).
4634
+ function mergeOldSpans(doc, change) {
4635
+ var old = getOldSpans(doc, change)
4636
+ var stretched = stretchSpansOverChange(doc, change)
4637
+ if (!old) { return stretched }
4638
+ if (!stretched) { return old }
4639
+
4640
+ for (var i = 0; i < old.length; ++i) {
4641
+ var oldCur = old[i], stretchCur = stretched[i]
4642
+ if (oldCur && stretchCur) {
4643
+ spans: for (var j = 0; j < stretchCur.length; ++j) {
4644
+ var span = stretchCur[j]
4645
+ for (var k = 0; k < oldCur.length; ++k)
4646
+ { if (oldCur[k].marker == span.marker) { continue spans } }
4647
+ oldCur.push(span)
4648
+ }
4649
+ } else if (stretchCur) {
4650
+ old[i] = stretchCur
4651
+ }
4652
+ }
4653
+ return old
4654
+ }
4655
+
4656
+ // Used both to provide a JSON-safe object in .getHistory, and, when
4657
+ // detaching a document, to split the history in two
4658
+ function copyHistoryArray(events, newGroup, instantiateSel) {
4659
+ var copy = []
4660
+ for (var i = 0; i < events.length; ++i) {
4661
+ var event = events[i]
4662
+ if (event.ranges) {
4663
+ copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event)
4664
+ continue
4665
+ }
4666
+ var changes = event.changes, newChanges = []
4667
+ copy.push({changes: newChanges})
4668
+ for (var j = 0; j < changes.length; ++j) {
4669
+ var change = changes[j], m = void 0
4670
+ newChanges.push({from: change.from, to: change.to, text: change.text})
4671
+ if (newGroup) { for (var prop in change) { if (m = prop.match(/^spans_(\d+)$/)) {
4672
+ if (indexOf(newGroup, Number(m[1])) > -1) {
4673
+ lst(newChanges)[prop] = change[prop]
4674
+ delete change[prop]
4675
+ }
4676
+ } } }
4677
+ }
4678
+ }
4679
+ return copy
4680
+ }
4681
+
4682
+ // The 'scroll' parameter given to many of these indicated whether
4683
+ // the new cursor position should be scrolled into view after
4684
+ // modifying the selection.
4685
+
4686
+ // If shift is held or the extend flag is set, extends a range to
4687
+ // include a given position (and optionally a second position).
4688
+ // Otherwise, simply returns the range between the given positions.
4689
+ // Used for cursor motion and such.
4690
+ function extendRange(doc, range, head, other) {
4691
+ if (doc.cm && doc.cm.display.shift || doc.extend) {
4692
+ var anchor = range.anchor
4693
+ if (other) {
4694
+ var posBefore = cmp(head, anchor) < 0
4695
+ if (posBefore != (cmp(other, anchor) < 0)) {
4696
+ anchor = head
4697
+ head = other
4698
+ } else if (posBefore != (cmp(head, other) < 0)) {
4699
+ head = other
4700
+ }
4701
+ }
4702
+ return new Range(anchor, head)
4703
+ } else {
4704
+ return new Range(other || head, head)
4705
+ }
4706
+ }
4707
+
4708
+ // Extend the primary selection range, discard the rest.
4709
+ function extendSelection(doc, head, other, options) {
4710
+ setSelection(doc, new Selection([extendRange(doc, doc.sel.primary(), head, other)], 0), options)
4711
+ }
4712
+
4713
+ // Extend all selections (pos is an array of selections with length
4714
+ // equal the number of selections)
4715
+ function extendSelections(doc, heads, options) {
4716
+ var out = []
4717
+ for (var i = 0; i < doc.sel.ranges.length; i++)
4718
+ { out[i] = extendRange(doc, doc.sel.ranges[i], heads[i], null) }
4719
+ var newSel = normalizeSelection(out, doc.sel.primIndex)
4720
+ setSelection(doc, newSel, options)
4721
+ }
4722
+
4723
+ // Updates a single range in the selection.
4724
+ function replaceOneSelection(doc, i, range, options) {
4725
+ var ranges = doc.sel.ranges.slice(0)
4726
+ ranges[i] = range
4727
+ setSelection(doc, normalizeSelection(ranges, doc.sel.primIndex), options)
4728
+ }
4729
+
4730
+ // Reset the selection to a single range.
4731
+ function setSimpleSelection(doc, anchor, head, options) {
4732
+ setSelection(doc, simpleSelection(anchor, head), options)
4733
+ }
4734
+
4735
+ // Give beforeSelectionChange handlers a change to influence a
4736
+ // selection update.
4737
+ function filterSelectionChange(doc, sel, options) {
4738
+ var obj = {
4739
+ ranges: sel.ranges,
4740
+ update: function(ranges) {
4741
+ var this$1 = this;
4742
+
4743
+ this.ranges = []
4744
+ for (var i = 0; i < ranges.length; i++)
4745
+ { this$1.ranges[i] = new Range(clipPos(doc, ranges[i].anchor),
4746
+ clipPos(doc, ranges[i].head)) }
4747
+ },
4748
+ origin: options && options.origin
4749
+ }
4750
+ signal(doc, "beforeSelectionChange", doc, obj)
4751
+ if (doc.cm) { signal(doc.cm, "beforeSelectionChange", doc.cm, obj) }
4752
+ if (obj.ranges != sel.ranges) { return normalizeSelection(obj.ranges, obj.ranges.length - 1) }
4753
+ else { return sel }
4754
+ }
4755
+
4756
+ function setSelectionReplaceHistory(doc, sel, options) {
4757
+ var done = doc.history.done, last = lst(done)
4758
+ if (last && last.ranges) {
4759
+ done[done.length - 1] = sel
4760
+ setSelectionNoUndo(doc, sel, options)
4761
+ } else {
4762
+ setSelection(doc, sel, options)
4763
+ }
4764
+ }
4765
+
4766
+ // Set a new selection.
4767
+ function setSelection(doc, sel, options) {
4768
+ setSelectionNoUndo(doc, sel, options)
4769
+ addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options)
4770
+ }
4771
+
4772
+ function setSelectionNoUndo(doc, sel, options) {
4773
+ if (hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange"))
4774
+ { sel = filterSelectionChange(doc, sel, options) }
4775
+
4776
+ var bias = options && options.bias ||
4777
+ (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1)
4778
+ setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true))
4779
+
4780
+ if (!(options && options.scroll === false) && doc.cm)
4781
+ { ensureCursorVisible(doc.cm) }
4782
+ }
4783
+
4784
+ function setSelectionInner(doc, sel) {
4785
+ if (sel.equals(doc.sel)) { return }
4786
+
4787
+ doc.sel = sel
4788
+
4789
+ if (doc.cm) {
4790
+ doc.cm.curOp.updateInput = doc.cm.curOp.selectionChanged = true
4791
+ signalCursorActivity(doc.cm)
4792
+ }
4793
+ signalLater(doc, "cursorActivity", doc)
4794
+ }
4795
+
4796
+ // Verify that the selection does not partially select any atomic
4797
+ // marked ranges.
4798
+ function reCheckSelection(doc) {
4799
+ setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false), sel_dontScroll)
4800
+ }
4801
+
4802
+ // Return a selection that does not partially select any atomic
4803
+ // ranges.
4804
+ function skipAtomicInSelection(doc, sel, bias, mayClear) {
4805
+ var out
4806
+ for (var i = 0; i < sel.ranges.length; i++) {
4807
+ var range = sel.ranges[i]
4808
+ var old = sel.ranges.length == doc.sel.ranges.length && doc.sel.ranges[i]
4809
+ var newAnchor = skipAtomic(doc, range.anchor, old && old.anchor, bias, mayClear)
4810
+ var newHead = skipAtomic(doc, range.head, old && old.head, bias, mayClear)
4811
+ if (out || newAnchor != range.anchor || newHead != range.head) {
4812
+ if (!out) { out = sel.ranges.slice(0, i) }
4813
+ out[i] = new Range(newAnchor, newHead)
4814
+ }
4815
+ }
4816
+ return out ? normalizeSelection(out, sel.primIndex) : sel
4817
+ }
4818
+
4819
+ function skipAtomicInner(doc, pos, oldPos, dir, mayClear) {
4820
+ var line = getLine(doc, pos.line)
4821
+ if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) {
4822
+ var sp = line.markedSpans[i], m = sp.marker
4823
+ if ((sp.from == null || (m.inclusiveLeft ? sp.from <= pos.ch : sp.from < pos.ch)) &&
4824
+ (sp.to == null || (m.inclusiveRight ? sp.to >= pos.ch : sp.to > pos.ch))) {
4825
+ if (mayClear) {
4826
+ signal(m, "beforeCursorEnter")
4827
+ if (m.explicitlyCleared) {
4828
+ if (!line.markedSpans) { break }
4829
+ else {--i; continue}
4830
+ }
4831
+ }
4832
+ if (!m.atomic) { continue }
4833
+
4834
+ if (oldPos) {
4835
+ var near = m.find(dir < 0 ? 1 : -1), diff = void 0
4836
+ if (dir < 0 ? m.inclusiveRight : m.inclusiveLeft)
4837
+ { near = movePos(doc, near, -dir, near && near.line == pos.line ? line : null) }
4838
+ if (near && near.line == pos.line && (diff = cmp(near, oldPos)) && (dir < 0 ? diff < 0 : diff > 0))
4839
+ { return skipAtomicInner(doc, near, pos, dir, mayClear) }
4840
+ }
4841
+
4842
+ var far = m.find(dir < 0 ? -1 : 1)
4843
+ if (dir < 0 ? m.inclusiveLeft : m.inclusiveRight)
4844
+ { far = movePos(doc, far, dir, far.line == pos.line ? line : null) }
4845
+ return far ? skipAtomicInner(doc, far, pos, dir, mayClear) : null
4846
+ }
4847
+ } }
4848
+ return pos
4849
+ }
4850
+
4851
+ // Ensure a given position is not inside an atomic range.
4852
+ function skipAtomic(doc, pos, oldPos, bias, mayClear) {
4853
+ var dir = bias || 1
4854
+ var found = skipAtomicInner(doc, pos, oldPos, dir, mayClear) ||
4855
+ (!mayClear && skipAtomicInner(doc, pos, oldPos, dir, true)) ||
4856
+ skipAtomicInner(doc, pos, oldPos, -dir, mayClear) ||
4857
+ (!mayClear && skipAtomicInner(doc, pos, oldPos, -dir, true))
4858
+ if (!found) {
4859
+ doc.cantEdit = true
4860
+ return Pos(doc.first, 0)
4861
+ }
4862
+ return found
4863
+ }
4864
+
4865
+ function movePos(doc, pos, dir, line) {
4866
+ if (dir < 0 && pos.ch == 0) {
4867
+ if (pos.line > doc.first) { return clipPos(doc, Pos(pos.line - 1)) }
4868
+ else { return null }
4869
+ } else if (dir > 0 && pos.ch == (line || getLine(doc, pos.line)).text.length) {
4870
+ if (pos.line < doc.first + doc.size - 1) { return Pos(pos.line + 1, 0) }
4871
+ else { return null }
4872
+ } else {
4873
+ return new Pos(pos.line, pos.ch + dir)
4874
+ }
4875
+ }
4876
+
4877
+ function selectAll(cm) {
4878
+ cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll)
4879
+ }
4880
+
4881
+ // UPDATING
4882
+
4883
+ // Allow "beforeChange" event handlers to influence a change
4884
+ function filterChange(doc, change, update) {
4885
+ var obj = {
4886
+ canceled: false,
4887
+ from: change.from,
4888
+ to: change.to,
4889
+ text: change.text,
4890
+ origin: change.origin,
4891
+ cancel: function () { return obj.canceled = true; }
4892
+ }
4893
+ if (update) { obj.update = function (from, to, text, origin) {
4894
+ if (from) { obj.from = clipPos(doc, from) }
4895
+ if (to) { obj.to = clipPos(doc, to) }
4896
+ if (text) { obj.text = text }
4897
+ if (origin !== undefined) { obj.origin = origin }
4898
+ } }
4899
+ signal(doc, "beforeChange", doc, obj)
4900
+ if (doc.cm) { signal(doc.cm, "beforeChange", doc.cm, obj) }
4901
+
4902
+ if (obj.canceled) { return null }
4903
+ return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin}
4904
+ }
4905
+
4906
+ // Apply a change to a document, and add it to the document's
4907
+ // history, and propagating it to all linked documents.
4908
+ function makeChange(doc, change, ignoreReadOnly) {
4909
+ if (doc.cm) {
4910
+ if (!doc.cm.curOp) { return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly) }
4911
+ if (doc.cm.state.suppressEdits) { return }
4912
+ }
4913
+
4914
+ if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) {
4915
+ change = filterChange(doc, change, true)
4916
+ if (!change) { return }
4917
+ }
4918
+
4919
+ // Possibly split or suppress the update based on the presence
4920
+ // of read-only spans in its range.
4921
+ var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to)
4922
+ if (split) {
4923
+ for (var i = split.length - 1; i >= 0; --i)
4924
+ { makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [""] : change.text}) }
4925
+ } else {
4926
+ makeChangeInner(doc, change)
4927
+ }
4928
+ }
4929
+
4930
+ function makeChangeInner(doc, change) {
4931
+ if (change.text.length == 1 && change.text[0] == "" && cmp(change.from, change.to) == 0) { return }
4932
+ var selAfter = computeSelAfterChange(doc, change)
4933
+ addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN)
4934
+
4935
+ makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change))
4936
+ var rebased = []
4937
+
4938
+ linkedDocs(doc, function (doc, sharedHist) {
4939
+ if (!sharedHist && indexOf(rebased, doc.history) == -1) {
4940
+ rebaseHist(doc.history, change)
4941
+ rebased.push(doc.history)
4942
+ }
4943
+ makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change))
4944
+ })
4945
+ }
4946
+
4947
+ // Revert a change stored in a document's history.
4948
+ function makeChangeFromHistory(doc, type, allowSelectionOnly) {
4949
+ if (doc.cm && doc.cm.state.suppressEdits && !allowSelectionOnly) { return }
4950
+
4951
+ var hist = doc.history, event, selAfter = doc.sel
4952
+ var source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done
4953
+
4954
+ // Verify that there is a useable event (so that ctrl-z won't
4955
+ // needlessly clear selection events)
4956
+ var i = 0
4957
+ for (; i < source.length; i++) {
4958
+ event = source[i]
4959
+ if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ranges)
4960
+ { break }
4961
+ }
4962
+ if (i == source.length) { return }
4963
+ hist.lastOrigin = hist.lastSelOrigin = null
4964
+
4965
+ for (;;) {
4966
+ event = source.pop()
4967
+ if (event.ranges) {
4968
+ pushSelectionToHistory(event, dest)
4969
+ if (allowSelectionOnly && !event.equals(doc.sel)) {
4970
+ setSelection(doc, event, {clearRedo: false})
4971
+ return
4972
+ }
4973
+ selAfter = event
4974
+ }
4975
+ else { break }
4976
+ }
4977
+
4978
+ // Build up a reverse change object to add to the opposite history
4979
+ // stack (redo when undoing, and vice versa).
4980
+ var antiChanges = []
4981
+ pushSelectionToHistory(selAfter, dest)
4982
+ dest.push({changes: antiChanges, generation: hist.generation})
4983
+ hist.generation = event.generation || ++hist.maxGeneration
4984
+
4985
+ var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")
4986
+
4987
+ var loop = function ( i ) {
4988
+ var change = event.changes[i]
4989
+ change.origin = type
4990
+ if (filter && !filterChange(doc, change, false)) {
4991
+ source.length = 0
4992
+ return {}
4993
+ }
4994
+
4995
+ antiChanges.push(historyChangeFromChange(doc, change))
4996
+
4997
+ var after = i ? computeSelAfterChange(doc, change) : lst(source)
4998
+ makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change))
4999
+ if (!i && doc.cm) { doc.cm.scrollIntoView({from: change.from, to: changeEnd(change)}) }
5000
+ var rebased = []
5001
+
5002
+ // Propagate to the linked documents
5003
+ linkedDocs(doc, function (doc, sharedHist) {
5004
+ if (!sharedHist && indexOf(rebased, doc.history) == -1) {
5005
+ rebaseHist(doc.history, change)
5006
+ rebased.push(doc.history)
5007
+ }
5008
+ makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change))
5009
+ })
5010
+ };
5011
+
5012
+ for (var i$1 = event.changes.length - 1; i$1 >= 0; --i$1) {
5013
+ var returned = loop( i$1 );
5014
+
5015
+ if ( returned ) return returned.v;
5016
+ }
5017
+ }
5018
+
5019
+ // Sub-views need their line numbers shifted when text is added
5020
+ // above or below them in the parent document.
5021
+ function shiftDoc(doc, distance) {
5022
+ if (distance == 0) { return }
5023
+ doc.first += distance
5024
+ doc.sel = new Selection(map(doc.sel.ranges, function (range) { return new Range(
5025
+ Pos(range.anchor.line + distance, range.anchor.ch),
5026
+ Pos(range.head.line + distance, range.head.ch)
5027
+ ); }), doc.sel.primIndex)
5028
+ if (doc.cm) {
5029
+ regChange(doc.cm, doc.first, doc.first - distance, distance)
5030
+ for (var d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++)
5031
+ { regLineChange(doc.cm, l, "gutter") }
5032
+ }
5033
+ }
5034
+
5035
+ // More lower-level change function, handling only a single document
5036
+ // (not linked ones).
5037
+ function makeChangeSingleDoc(doc, change, selAfter, spans) {
5038
+ if (doc.cm && !doc.cm.curOp)
5039
+ { return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans) }
5040
+
5041
+ if (change.to.line < doc.first) {
5042
+ shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line))
5043
+ return
5044
+ }
5045
+ if (change.from.line > doc.lastLine()) { return }
5046
+
5047
+ // Clip the change to the size of this doc
5048
+ if (change.from.line < doc.first) {
5049
+ var shift = change.text.length - 1 - (doc.first - change.from.line)
5050
+ shiftDoc(doc, shift)
5051
+ change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch),
5052
+ text: [lst(change.text)], origin: change.origin}
5053
+ }
5054
+ var last = doc.lastLine()
5055
+ if (change.to.line > last) {
5056
+ change = {from: change.from, to: Pos(last, getLine(doc, last).text.length),
5057
+ text: [change.text[0]], origin: change.origin}
5058
+ }
5059
+
5060
+ change.removed = getBetween(doc, change.from, change.to)
5061
+
5062
+ if (!selAfter) { selAfter = computeSelAfterChange(doc, change) }
5063
+ if (doc.cm) { makeChangeSingleDocInEditor(doc.cm, change, spans) }
5064
+ else { updateDoc(doc, change, spans) }
5065
+ setSelectionNoUndo(doc, selAfter, sel_dontScroll)
5066
+ }
5067
+
5068
+ // Handle the interaction of a change to a document with the editor
5069
+ // that this document is part of.
5070
+ function makeChangeSingleDocInEditor(cm, change, spans) {
5071
+ var doc = cm.doc, display = cm.display, from = change.from, to = change.to
5072
+
5073
+ var recomputeMaxLength = false, checkWidthStart = from.line
5074
+ if (!cm.options.lineWrapping) {
5075
+ checkWidthStart = lineNo(visualLine(getLine(doc, from.line)))
5076
+ doc.iter(checkWidthStart, to.line + 1, function (line) {
5077
+ if (line == display.maxLine) {
5078
+ recomputeMaxLength = true
5079
+ return true
5080
+ }
5081
+ })
5082
+ }
5083
+
5084
+ if (doc.sel.contains(change.from, change.to) > -1)
5085
+ { signalCursorActivity(cm) }
5086
+
5087
+ updateDoc(doc, change, spans, estimateHeight(cm))
5088
+
5089
+ if (!cm.options.lineWrapping) {
5090
+ doc.iter(checkWidthStart, from.line + change.text.length, function (line) {
5091
+ var len = lineLength(line)
5092
+ if (len > display.maxLineLength) {
5093
+ display.maxLine = line
5094
+ display.maxLineLength = len
5095
+ display.maxLineChanged = true
5096
+ recomputeMaxLength = false
5097
+ }
5098
+ })
5099
+ if (recomputeMaxLength) { cm.curOp.updateMaxLine = true }
5100
+ }
5101
+
5102
+ // Adjust frontier, schedule worker
5103
+ doc.frontier = Math.min(doc.frontier, from.line)
5104
+ startWorker(cm, 400)
5105
+
5106
+ var lendiff = change.text.length - (to.line - from.line) - 1
5107
+ // Remember that these lines changed, for updating the display
5108
+ if (change.full)
5109
+ { regChange(cm) }
5110
+ else if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change))
5111
+ { regLineChange(cm, from.line, "text") }
5112
+ else
5113
+ { regChange(cm, from.line, to.line + 1, lendiff) }
5114
+
5115
+ var changesHandler = hasHandler(cm, "changes"), changeHandler = hasHandler(cm, "change")
5116
+ if (changeHandler || changesHandler) {
5117
+ var obj = {
5118
+ from: from, to: to,
5119
+ text: change.text,
5120
+ removed: change.removed,
5121
+ origin: change.origin
5122
+ }
5123
+ if (changeHandler) { signalLater(cm, "change", cm, obj) }
5124
+ if (changesHandler) { (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj) }
5125
+ }
5126
+ cm.display.selForContextMenu = null
5127
+ }
5128
+
5129
+ function replaceRange(doc, code, from, to, origin) {
5130
+ if (!to) { to = from }
5131
+ if (cmp(to, from) < 0) { var tmp = to; to = from; from = tmp }
5132
+ if (typeof code == "string") { code = doc.splitLines(code) }
5133
+ makeChange(doc, {from: from, to: to, text: code, origin: origin})
5134
+ }
5135
+
5136
+ // Rebasing/resetting history to deal with externally-sourced changes
5137
+
5138
+ function rebaseHistSelSingle(pos, from, to, diff) {
5139
+ if (to < pos.line) {
5140
+ pos.line += diff
5141
+ } else if (from < pos.line) {
5142
+ pos.line = from
5143
+ pos.ch = 0
5144
+ }
5145
+ }
5146
+
5147
+ // Tries to rebase an array of history events given a change in the
5148
+ // document. If the change touches the same lines as the event, the
5149
+ // event, and everything 'behind' it, is discarded. If the change is
5150
+ // before the event, the event's positions are updated. Uses a
5151
+ // copy-on-write scheme for the positions, to avoid having to
5152
+ // reallocate them all on every rebase, but also avoid problems with
5153
+ // shared position objects being unsafely updated.
5154
+ function rebaseHistArray(array, from, to, diff) {
5155
+ for (var i = 0; i < array.length; ++i) {
5156
+ var sub = array[i], ok = true
5157
+ if (sub.ranges) {
5158
+ if (!sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied = true }
5159
+ for (var j = 0; j < sub.ranges.length; j++) {
5160
+ rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff)
5161
+ rebaseHistSelSingle(sub.ranges[j].head, from, to, diff)
5162
+ }
5163
+ continue
5164
+ }
5165
+ for (var j$1 = 0; j$1 < sub.changes.length; ++j$1) {
5166
+ var cur = sub.changes[j$1]
5167
+ if (to < cur.from.line) {
5168
+ cur.from = Pos(cur.from.line + diff, cur.from.ch)
5169
+ cur.to = Pos(cur.to.line + diff, cur.to.ch)
5170
+ } else if (from <= cur.to.line) {
5171
+ ok = false
5172
+ break
5173
+ }
5174
+ }
5175
+ if (!ok) {
5176
+ array.splice(0, i + 1)
5177
+ i = 0
5178
+ }
5179
+ }
5180
+ }
5181
+
5182
+ function rebaseHist(hist, change) {
5183
+ var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1
5184
+ rebaseHistArray(hist.done, from, to, diff)
5185
+ rebaseHistArray(hist.undone, from, to, diff)
5186
+ }
5187
+
5188
+ // Utility for applying a change to a line by handle or number,
5189
+ // returning the number and optionally registering the line as
5190
+ // changed.
5191
+ function changeLine(doc, handle, changeType, op) {
5192
+ var no = handle, line = handle
5193
+ if (typeof handle == "number") { line = getLine(doc, clipLine(doc, handle)) }
5194
+ else { no = lineNo(handle) }
5195
+ if (no == null) { return null }
5196
+ if (op(line, no) && doc.cm) { regLineChange(doc.cm, no, changeType) }
5197
+ return line
5198
+ }
5199
+
5200
+ // The document is represented as a BTree consisting of leaves, with
5201
+ // chunk of lines in them, and branches, with up to ten leaves or
5202
+ // other branch nodes below them. The top node is always a branch
5203
+ // node, and is the document object itself (meaning it has
5204
+ // additional methods and properties).
5205
+ //
5206
+ // All nodes have parent links. The tree is used both to go from
5207
+ // line numbers to line objects, and to go from objects to numbers.
5208
+ // It also indexes by height, and is used to convert between height
5209
+ // and line object, and to find the total height of the document.
5210
+ //
5211
+ // See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html
5212
+
5213
+ function LeafChunk(lines) {
5214
+ var this$1 = this;
5215
+
5216
+ this.lines = lines
5217
+ this.parent = null
5218
+ var height = 0
5219
+ for (var i = 0; i < lines.length; ++i) {
5220
+ lines[i].parent = this$1
5221
+ height += lines[i].height
5222
+ }
5223
+ this.height = height
5224
+ }
5225
+
5226
+ LeafChunk.prototype = {
5227
+ chunkSize: function() { return this.lines.length },
5228
+ // Remove the n lines at offset 'at'.
5229
+ removeInner: function(at, n) {
5230
+ var this$1 = this;
5231
+
5232
+ for (var i = at, e = at + n; i < e; ++i) {
5233
+ var line = this$1.lines[i]
5234
+ this$1.height -= line.height
5235
+ cleanUpLine(line)
5236
+ signalLater(line, "delete")
5237
+ }
5238
+ this.lines.splice(at, n)
5239
+ },
5240
+ // Helper used to collapse a small branch into a single leaf.
5241
+ collapse: function(lines) {
5242
+ lines.push.apply(lines, this.lines)
5243
+ },
5244
+ // Insert the given array of lines at offset 'at', count them as
5245
+ // having the given height.
5246
+ insertInner: function(at, lines, height) {
5247
+ var this$1 = this;
5248
+
5249
+ this.height += height
5250
+ this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at))
5251
+ for (var i = 0; i < lines.length; ++i) { lines[i].parent = this$1 }
5252
+ },
5253
+ // Used to iterate over a part of the tree.
5254
+ iterN: function(at, n, op) {
5255
+ var this$1 = this;
5256
+
5257
+ for (var e = at + n; at < e; ++at)
5258
+ { if (op(this$1.lines[at])) { return true } }
5259
+ }
5260
+ }
5261
+
5262
+ function BranchChunk(children) {
5263
+ var this$1 = this;
5264
+
5265
+ this.children = children
5266
+ var size = 0, height = 0
5267
+ for (var i = 0; i < children.length; ++i) {
5268
+ var ch = children[i]
5269
+ size += ch.chunkSize(); height += ch.height
5270
+ ch.parent = this$1
5271
+ }
5272
+ this.size = size
5273
+ this.height = height
5274
+ this.parent = null
5275
+ }
5276
+
5277
+ BranchChunk.prototype = {
5278
+ chunkSize: function() { return this.size },
5279
+ removeInner: function(at, n) {
5280
+ var this$1 = this;
5281
+
5282
+ this.size -= n
5283
+ for (var i = 0; i < this.children.length; ++i) {
5284
+ var child = this$1.children[i], sz = child.chunkSize()
5285
+ if (at < sz) {
5286
+ var rm = Math.min(n, sz - at), oldHeight = child.height
5287
+ child.removeInner(at, rm)
5288
+ this$1.height -= oldHeight - child.height
5289
+ if (sz == rm) { this$1.children.splice(i--, 1); child.parent = null }
5290
+ if ((n -= rm) == 0) { break }
5291
+ at = 0
5292
+ } else { at -= sz }
5293
+ }
5294
+ // If the result is smaller than 25 lines, ensure that it is a
5295
+ // single leaf node.
5296
+ if (this.size - n < 25 &&
5297
+ (this.children.length > 1 || !(this.children[0] instanceof LeafChunk))) {
5298
+ var lines = []
5299
+ this.collapse(lines)
5300
+ this.children = [new LeafChunk(lines)]
5301
+ this.children[0].parent = this
5302
+ }
5303
+ },
5304
+ collapse: function(lines) {
5305
+ var this$1 = this;
5306
+
5307
+ for (var i = 0; i < this.children.length; ++i) { this$1.children[i].collapse(lines) }
5308
+ },
5309
+ insertInner: function(at, lines, height) {
5310
+ var this$1 = this;
5311
+
5312
+ this.size += lines.length
5313
+ this.height += height
5314
+ for (var i = 0; i < this.children.length; ++i) {
5315
+ var child = this$1.children[i], sz = child.chunkSize()
5316
+ if (at <= sz) {
5317
+ child.insertInner(at, lines, height)
5318
+ if (child.lines && child.lines.length > 50) {
5319
+ // To avoid memory thrashing when child.lines is huge (e.g. first view of a large file), it's never spliced.
5320
+ // Instead, small slices are taken. They're taken in order because sequential memory accesses are fastest.
5321
+ var remaining = child.lines.length % 25 + 25
5322
+ for (var pos = remaining; pos < child.lines.length;) {
5323
+ var leaf = new LeafChunk(child.lines.slice(pos, pos += 25))
5324
+ child.height -= leaf.height
5325
+ this$1.children.splice(++i, 0, leaf)
5326
+ leaf.parent = this$1
5327
+ }
5328
+ child.lines = child.lines.slice(0, remaining)
5329
+ this$1.maybeSpill()
5330
+ }
5331
+ break
5332
+ }
5333
+ at -= sz
5334
+ }
5335
+ },
5336
+ // When a node has grown, check whether it should be split.
5337
+ maybeSpill: function() {
5338
+ if (this.children.length <= 10) { return }
5339
+ var me = this
5340
+ do {
5341
+ var spilled = me.children.splice(me.children.length - 5, 5)
5342
+ var sibling = new BranchChunk(spilled)
5343
+ if (!me.parent) { // Become the parent node
5344
+ var copy = new BranchChunk(me.children)
5345
+ copy.parent = me
5346
+ me.children = [copy, sibling]
5347
+ me = copy
5348
+ } else {
5349
+ me.size -= sibling.size
5350
+ me.height -= sibling.height
5351
+ var myIndex = indexOf(me.parent.children, me)
5352
+ me.parent.children.splice(myIndex + 1, 0, sibling)
5353
+ }
5354
+ sibling.parent = me.parent
5355
+ } while (me.children.length > 10)
5356
+ me.parent.maybeSpill()
5357
+ },
5358
+ iterN: function(at, n, op) {
5359
+ var this$1 = this;
5360
+
5361
+ for (var i = 0; i < this.children.length; ++i) {
5362
+ var child = this$1.children[i], sz = child.chunkSize()
5363
+ if (at < sz) {
5364
+ var used = Math.min(n, sz - at)
5365
+ if (child.iterN(at, used, op)) { return true }
5366
+ if ((n -= used) == 0) { break }
5367
+ at = 0
5368
+ } else { at -= sz }
5369
+ }
5370
+ }
5371
+ }
5372
+
5373
+ // Line widgets are block elements displayed above or below a line.
5374
+
5375
+ function LineWidget(doc, node, options) {
5376
+ var this$1 = this;
5377
+
5378
+ if (options) { for (var opt in options) { if (options.hasOwnProperty(opt))
5379
+ { this$1[opt] = options[opt] } } }
5380
+ this.doc = doc
5381
+ this.node = node
5382
+ }
5383
+ eventMixin(LineWidget)
5384
+
5385
+ function adjustScrollWhenAboveVisible(cm, line, diff) {
5386
+ if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop))
5387
+ { addToScrollPos(cm, null, diff) }
5388
+ }
5389
+
5390
+ LineWidget.prototype.clear = function() {
5391
+ var this$1 = this;
5392
+
5393
+ var cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line)
5394
+ if (no == null || !ws) { return }
5395
+ for (var i = 0; i < ws.length; ++i) { if (ws[i] == this$1) { ws.splice(i--, 1) } }
5396
+ if (!ws.length) { line.widgets = null }
5397
+ var height = widgetHeight(this)
5398
+ updateLineHeight(line, Math.max(0, line.height - height))
5399
+ if (cm) { runInOp(cm, function () {
5400
+ adjustScrollWhenAboveVisible(cm, line, -height)
5401
+ regLineChange(cm, no, "widget")
5402
+ }) }
5403
+ }
5404
+ LineWidget.prototype.changed = function() {
5405
+ var oldH = this.height, cm = this.doc.cm, line = this.line
5406
+ this.height = null
5407
+ var diff = widgetHeight(this) - oldH
5408
+ if (!diff) { return }
5409
+ updateLineHeight(line, line.height + diff)
5410
+ if (cm) { runInOp(cm, function () {
5411
+ cm.curOp.forceUpdate = true
5412
+ adjustScrollWhenAboveVisible(cm, line, diff)
5413
+ }) }
5414
+ }
5415
+
5416
+ function addLineWidget(doc, handle, node, options) {
5417
+ var widget = new LineWidget(doc, node, options)
5418
+ var cm = doc.cm
5419
+ if (cm && widget.noHScroll) { cm.display.alignWidgets = true }
5420
+ changeLine(doc, handle, "widget", function (line) {
5421
+ var widgets = line.widgets || (line.widgets = [])
5422
+ if (widget.insertAt == null) { widgets.push(widget) }
5423
+ else { widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget) }
5424
+ widget.line = line
5425
+ if (cm && !lineIsHidden(doc, line)) {
5426
+ var aboveVisible = heightAtLine(line) < doc.scrollTop
5427
+ updateLineHeight(line, line.height + widgetHeight(widget))
5428
+ if (aboveVisible) { addToScrollPos(cm, null, widget.height) }
5429
+ cm.curOp.forceUpdate = true
5430
+ }
5431
+ return true
5432
+ })
5433
+ return widget
5434
+ }
5435
+
5436
+ // TEXTMARKERS
5437
+
5438
+ // Created with markText and setBookmark methods. A TextMarker is a
5439
+ // handle that can be used to clear or find a marked position in the
5440
+ // document. Line objects hold arrays (markedSpans) containing
5441
+ // {from, to, marker} object pointing to such marker objects, and
5442
+ // indicating that such a marker is present on that line. Multiple
5443
+ // lines may point to the same marker when it spans across lines.
5444
+ // The spans will have null for their from/to properties when the
5445
+ // marker continues beyond the start/end of the line. Markers have
5446
+ // links back to the lines they currently touch.
5447
+
5448
+ // Collapsed markers have unique ids, in order to be able to order
5449
+ // them, which is needed for uniquely determining an outer marker
5450
+ // when they overlap (they may nest, but not partially overlap).
5451
+ var nextMarkerId = 0
5452
+
5453
+ function TextMarker(doc, type) {
5454
+ this.lines = []
5455
+ this.type = type
5456
+ this.doc = doc
5457
+ this.id = ++nextMarkerId
5458
+ }
5459
+ eventMixin(TextMarker)
5460
+
5461
+ // Clear the marker.
5462
+ TextMarker.prototype.clear = function() {
5463
+ var this$1 = this;
5464
+
5465
+ if (this.explicitlyCleared) { return }
5466
+ var cm = this.doc.cm, withOp = cm && !cm.curOp
5467
+ if (withOp) { startOperation(cm) }
5468
+ if (hasHandler(this, "clear")) {
5469
+ var found = this.find()
5470
+ if (found) { signalLater(this, "clear", found.from, found.to) }
5471
+ }
5472
+ var min = null, max = null
5473
+ for (var i = 0; i < this.lines.length; ++i) {
5474
+ var line = this$1.lines[i]
5475
+ var span = getMarkedSpanFor(line.markedSpans, this$1)
5476
+ if (cm && !this$1.collapsed) { regLineChange(cm, lineNo(line), "text") }
5477
+ else if (cm) {
5478
+ if (span.to != null) { max = lineNo(line) }
5479
+ if (span.from != null) { min = lineNo(line) }
5480
+ }
5481
+ line.markedSpans = removeMarkedSpan(line.markedSpans, span)
5482
+ if (span.from == null && this$1.collapsed && !lineIsHidden(this$1.doc, line) && cm)
5483
+ { updateLineHeight(line, textHeight(cm.display)) }
5484
+ }
5485
+ if (cm && this.collapsed && !cm.options.lineWrapping) { for (var i$1 = 0; i$1 < this.lines.length; ++i$1) {
5486
+ var visual = visualLine(this$1.lines[i$1]), len = lineLength(visual)
5487
+ if (len > cm.display.maxLineLength) {
5488
+ cm.display.maxLine = visual
5489
+ cm.display.maxLineLength = len
5490
+ cm.display.maxLineChanged = true
5491
+ }
5492
+ } }
5493
+
5494
+ if (min != null && cm && this.collapsed) { regChange(cm, min, max + 1) }
5495
+ this.lines.length = 0
5496
+ this.explicitlyCleared = true
5497
+ if (this.atomic && this.doc.cantEdit) {
5498
+ this.doc.cantEdit = false
5499
+ if (cm) { reCheckSelection(cm.doc) }
5500
+ }
5501
+ if (cm) { signalLater(cm, "markerCleared", cm, this) }
5502
+ if (withOp) { endOperation(cm) }
5503
+ if (this.parent) { this.parent.clear() }
5504
+ }
5505
+
5506
+ // Find the position of the marker in the document. Returns a {from,
5507
+ // to} object by default. Side can be passed to get a specific side
5508
+ // -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the
5509
+ // Pos objects returned contain a line object, rather than a line
5510
+ // number (used to prevent looking up the same line twice).
5511
+ TextMarker.prototype.find = function(side, lineObj) {
5512
+ var this$1 = this;
5513
+
5514
+ if (side == null && this.type == "bookmark") { side = 1 }
5515
+ var from, to
5516
+ for (var i = 0; i < this.lines.length; ++i) {
5517
+ var line = this$1.lines[i]
5518
+ var span = getMarkedSpanFor(line.markedSpans, this$1)
5519
+ if (span.from != null) {
5520
+ from = Pos(lineObj ? line : lineNo(line), span.from)
5521
+ if (side == -1) { return from }
5522
+ }
5523
+ if (span.to != null) {
5524
+ to = Pos(lineObj ? line : lineNo(line), span.to)
5525
+ if (side == 1) { return to }
5526
+ }
5527
+ }
5528
+ return from && {from: from, to: to}
5529
+ }
5530
+
5531
+ // Signals that the marker's widget changed, and surrounding layout
5532
+ // should be recomputed.
5533
+ TextMarker.prototype.changed = function() {
5534
+ var pos = this.find(-1, true), widget = this, cm = this.doc.cm
5535
+ if (!pos || !cm) { return }
5536
+ runInOp(cm, function () {
5537
+ var line = pos.line, lineN = lineNo(pos.line)
5538
+ var view = findViewForLine(cm, lineN)
5539
+ if (view) {
5540
+ clearLineMeasurementCacheFor(view)
5541
+ cm.curOp.selectionChanged = cm.curOp.forceUpdate = true
5542
+ }
5543
+ cm.curOp.updateMaxLine = true
5544
+ if (!lineIsHidden(widget.doc, line) && widget.height != null) {
5545
+ var oldHeight = widget.height
5546
+ widget.height = null
5547
+ var dHeight = widgetHeight(widget) - oldHeight
5548
+ if (dHeight)
5549
+ { updateLineHeight(line, line.height + dHeight) }
5550
+ }
5551
+ })
5552
+ }
5553
+
5554
+ TextMarker.prototype.attachLine = function(line) {
5555
+ if (!this.lines.length && this.doc.cm) {
5556
+ var op = this.doc.cm.curOp
5557
+ if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1)
5558
+ { (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this) }
5559
+ }
5560
+ this.lines.push(line)
5561
+ }
5562
+ TextMarker.prototype.detachLine = function(line) {
5563
+ this.lines.splice(indexOf(this.lines, line), 1)
5564
+ if (!this.lines.length && this.doc.cm) {
5565
+ var op = this.doc.cm.curOp;(op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this)
5566
+ }
5567
+ }
5568
+
5569
+ // Create a marker, wire it up to the right lines, and
5570
+ function markText(doc, from, to, options, type) {
5571
+ // Shared markers (across linked documents) are handled separately
5572
+ // (markTextShared will call out to this again, once per
5573
+ // document).
5574
+ if (options && options.shared) { return markTextShared(doc, from, to, options, type) }
5575
+ // Ensure we are in an operation.
5576
+ if (doc.cm && !doc.cm.curOp) { return operation(doc.cm, markText)(doc, from, to, options, type) }
5577
+
5578
+ var marker = new TextMarker(doc, type), diff = cmp(from, to)
5579
+ if (options) { copyObj(options, marker, false) }
5580
+ // Don't connect empty markers unless clearWhenEmpty is false
5581
+ if (diff > 0 || diff == 0 && marker.clearWhenEmpty !== false)
5582
+ { return marker }
5583
+ if (marker.replacedWith) {
5584
+ // Showing up as a widget implies collapsed (widget replaces text)
5585
+ marker.collapsed = true
5586
+ marker.widgetNode = elt("span", [marker.replacedWith], "CodeMirror-widget")
5587
+ if (!options.handleMouseEvents) { marker.widgetNode.setAttribute("cm-ignore-events", "true") }
5588
+ if (options.insertLeft) { marker.widgetNode.insertLeft = true }
5589
+ }
5590
+ if (marker.collapsed) {
5591
+ if (conflictingCollapsedRange(doc, from.line, from, to, marker) ||
5592
+ from.line != to.line && conflictingCollapsedRange(doc, to.line, from, to, marker))
5593
+ { throw new Error("Inserting collapsed marker partially overlapping an existing one") }
5594
+ seeCollapsedSpans()
5595
+ }
5596
+
5597
+ if (marker.addToHistory)
5598
+ { addChangeToHistory(doc, {from: from, to: to, origin: "markText"}, doc.sel, NaN) }
5599
+
5600
+ var curLine = from.line, cm = doc.cm, updateMaxLine
5601
+ doc.iter(curLine, to.line + 1, function (line) {
5602
+ if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) == cm.display.maxLine)
5603
+ { updateMaxLine = true }
5604
+ if (marker.collapsed && curLine != from.line) { updateLineHeight(line, 0) }
5605
+ addMarkedSpan(line, new MarkedSpan(marker,
5606
+ curLine == from.line ? from.ch : null,
5607
+ curLine == to.line ? to.ch : null))
5608
+ ++curLine
5609
+ })
5610
+ // lineIsHidden depends on the presence of the spans, so needs a second pass
5611
+ if (marker.collapsed) { doc.iter(from.line, to.line + 1, function (line) {
5612
+ if (lineIsHidden(doc, line)) { updateLineHeight(line, 0) }
5613
+ }) }
5614
+
5615
+ if (marker.clearOnEnter) { on(marker, "beforeCursorEnter", function () { return marker.clear(); }) }
5616
+
5617
+ if (marker.readOnly) {
5618
+ seeReadOnlySpans()
5619
+ if (doc.history.done.length || doc.history.undone.length)
5620
+ { doc.clearHistory() }
5621
+ }
5622
+ if (marker.collapsed) {
5623
+ marker.id = ++nextMarkerId
5624
+ marker.atomic = true
5625
+ }
5626
+ if (cm) {
5627
+ // Sync editor state
5628
+ if (updateMaxLine) { cm.curOp.updateMaxLine = true }
5629
+ if (marker.collapsed)
5630
+ { regChange(cm, from.line, to.line + 1) }
5631
+ else if (marker.className || marker.title || marker.startStyle || marker.endStyle || marker.css)
5632
+ { for (var i = from.line; i <= to.line; i++) { regLineChange(cm, i, "text") } }
5633
+ if (marker.atomic) { reCheckSelection(cm.doc) }
5634
+ signalLater(cm, "markerAdded", cm, marker)
5635
+ }
5636
+ return marker
5637
+ }
5638
+
5639
+ // SHARED TEXTMARKERS
5640
+
5641
+ // A shared marker spans multiple linked documents. It is
5642
+ // implemented as a meta-marker-object controlling multiple normal
5643
+ // markers.
5644
+ function SharedTextMarker(markers, primary) {
5645
+ var this$1 = this;
5646
+
5647
+ this.markers = markers
5648
+ this.primary = primary
5649
+ for (var i = 0; i < markers.length; ++i)
5650
+ { markers[i].parent = this$1 }
5651
+ }
5652
+ eventMixin(SharedTextMarker)
5653
+
5654
+ SharedTextMarker.prototype.clear = function() {
5655
+ var this$1 = this;
5656
+
5657
+ if (this.explicitlyCleared) { return }
5658
+ this.explicitlyCleared = true
5659
+ for (var i = 0; i < this.markers.length; ++i)
5660
+ { this$1.markers[i].clear() }
5661
+ signalLater(this, "clear")
5662
+ }
5663
+ SharedTextMarker.prototype.find = function(side, lineObj) {
5664
+ return this.primary.find(side, lineObj)
5665
+ }
5666
+
5667
+ function markTextShared(doc, from, to, options, type) {
5668
+ options = copyObj(options)
5669
+ options.shared = false
5670
+ var markers = [markText(doc, from, to, options, type)], primary = markers[0]
5671
+ var widget = options.widgetNode
5672
+ linkedDocs(doc, function (doc) {
5673
+ if (widget) { options.widgetNode = widget.cloneNode(true) }
5674
+ markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type))
5675
+ for (var i = 0; i < doc.linked.length; ++i)
5676
+ { if (doc.linked[i].isParent) { return } }
5677
+ primary = lst(markers)
5678
+ })
5679
+ return new SharedTextMarker(markers, primary)
5680
+ }
5681
+
5682
+ function findSharedMarkers(doc) {
5683
+ return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())), function (m) { return m.parent; })
5684
+ }
5685
+
5686
+ function copySharedMarkers(doc, markers) {
5687
+ for (var i = 0; i < markers.length; i++) {
5688
+ var marker = markers[i], pos = marker.find()
5689
+ var mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to)
5690
+ if (cmp(mFrom, mTo)) {
5691
+ var subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type)
5692
+ marker.markers.push(subMark)
5693
+ subMark.parent = marker
5694
+ }
5695
+ }
5696
+ }
5697
+
5698
+ function detachSharedMarkers(markers) {
5699
+ var loop = function ( i ) {
5700
+ var marker = markers[i], linked = [marker.primary.doc]
5701
+ linkedDocs(marker.primary.doc, function (d) { return linked.push(d); })
5702
+ for (var j = 0; j < marker.markers.length; j++) {
5703
+ var subMarker = marker.markers[j]
5704
+ if (indexOf(linked, subMarker.doc) == -1) {
5705
+ subMarker.parent = null
5706
+ marker.markers.splice(j--, 1)
5707
+ }
5708
+ }
5709
+ };
5710
+
5711
+ for (var i = 0; i < markers.length; i++) loop( i );
5712
+ }
5713
+
5714
+ var nextDocId = 0
5715
+ var Doc = function(text, mode, firstLine, lineSep) {
5716
+ if (!(this instanceof Doc)) { return new Doc(text, mode, firstLine, lineSep) }
5717
+ if (firstLine == null) { firstLine = 0 }
5718
+
5719
+ BranchChunk.call(this, [new LeafChunk([new Line("", null)])])
5720
+ this.first = firstLine
5721
+ this.scrollTop = this.scrollLeft = 0
5722
+ this.cantEdit = false
5723
+ this.cleanGeneration = 1
5724
+ this.frontier = firstLine
5725
+ var start = Pos(firstLine, 0)
5726
+ this.sel = simpleSelection(start)
5727
+ this.history = new History(null)
5728
+ this.id = ++nextDocId
5729
+ this.modeOption = mode
5730
+ this.lineSep = lineSep
5731
+ this.extend = false
5732
+
5733
+ if (typeof text == "string") { text = this.splitLines(text) }
5734
+ updateDoc(this, {from: start, to: start, text: text})
5735
+ setSelection(this, simpleSelection(start), sel_dontScroll)
5736
+ }
5737
+
5738
+ Doc.prototype = createObj(BranchChunk.prototype, {
5739
+ constructor: Doc,
5740
+ // Iterate over the document. Supports two forms -- with only one
5741
+ // argument, it calls that for each line in the document. With
5742
+ // three, it iterates over the range given by the first two (with
5743
+ // the second being non-inclusive).
5744
+ iter: function(from, to, op) {
5745
+ if (op) { this.iterN(from - this.first, to - from, op) }
5746
+ else { this.iterN(this.first, this.first + this.size, from) }
5747
+ },
5748
+
5749
+ // Non-public interface for adding and removing lines.
5750
+ insert: function(at, lines) {
5751
+ var height = 0
5752
+ for (var i = 0; i < lines.length; ++i) { height += lines[i].height }
5753
+ this.insertInner(at - this.first, lines, height)
5754
+ },
5755
+ remove: function(at, n) { this.removeInner(at - this.first, n) },
5756
+
5757
+ // From here, the methods are part of the public interface. Most
5758
+ // are also available from CodeMirror (editor) instances.
5759
+
5760
+ getValue: function(lineSep) {
5761
+ var lines = getLines(this, this.first, this.first + this.size)
5762
+ if (lineSep === false) { return lines }
5763
+ return lines.join(lineSep || this.lineSeparator())
5764
+ },
5765
+ setValue: docMethodOp(function(code) {
5766
+ var top = Pos(this.first, 0), last = this.first + this.size - 1
5767
+ makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length),
5768
+ text: this.splitLines(code), origin: "setValue", full: true}, true)
5769
+ setSelection(this, simpleSelection(top))
5770
+ }),
5771
+ replaceRange: function(code, from, to, origin) {
5772
+ from = clipPos(this, from)
5773
+ to = to ? clipPos(this, to) : from
5774
+ replaceRange(this, code, from, to, origin)
5775
+ },
5776
+ getRange: function(from, to, lineSep) {
5777
+ var lines = getBetween(this, clipPos(this, from), clipPos(this, to))
5778
+ if (lineSep === false) { return lines }
5779
+ return lines.join(lineSep || this.lineSeparator())
5780
+ },
5781
+
5782
+ getLine: function(line) {var l = this.getLineHandle(line); return l && l.text},
5783
+
5784
+ getLineHandle: function(line) {if (isLine(this, line)) { return getLine(this, line) }},
5785
+ getLineNumber: function(line) {return lineNo(line)},
5786
+
5787
+ getLineHandleVisualStart: function(line) {
5788
+ if (typeof line == "number") { line = getLine(this, line) }
5789
+ return visualLine(line)
5790
+ },
5791
+
5792
+ lineCount: function() {return this.size},
5793
+ firstLine: function() {return this.first},
5794
+ lastLine: function() {return this.first + this.size - 1},
5795
+
5796
+ clipPos: function(pos) {return clipPos(this, pos)},
5797
+
5798
+ getCursor: function(start) {
5799
+ var range$$1 = this.sel.primary(), pos
5800
+ if (start == null || start == "head") { pos = range$$1.head }
5801
+ else if (start == "anchor") { pos = range$$1.anchor }
5802
+ else if (start == "end" || start == "to" || start === false) { pos = range$$1.to() }
5803
+ else { pos = range$$1.from() }
5804
+ return pos
5805
+ },
5806
+ listSelections: function() { return this.sel.ranges },
5807
+ somethingSelected: function() {return this.sel.somethingSelected()},
5808
+
5809
+ setCursor: docMethodOp(function(line, ch, options) {
5810
+ setSimpleSelection(this, clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line), null, options)
5811
+ }),
5812
+ setSelection: docMethodOp(function(anchor, head, options) {
5813
+ setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options)
5814
+ }),
5815
+ extendSelection: docMethodOp(function(head, other, options) {
5816
+ extendSelection(this, clipPos(this, head), other && clipPos(this, other), options)
5817
+ }),
5818
+ extendSelections: docMethodOp(function(heads, options) {
5819
+ extendSelections(this, clipPosArray(this, heads), options)
5820
+ }),
5821
+ extendSelectionsBy: docMethodOp(function(f, options) {
5822
+ var heads = map(this.sel.ranges, f)
5823
+ extendSelections(this, clipPosArray(this, heads), options)
5824
+ }),
5825
+ setSelections: docMethodOp(function(ranges, primary, options) {
5826
+ var this$1 = this;
5827
+
5828
+ if (!ranges.length) { return }
5829
+ var out = []
5830
+ for (var i = 0; i < ranges.length; i++)
5831
+ { out[i] = new Range(clipPos(this$1, ranges[i].anchor),
5832
+ clipPos(this$1, ranges[i].head)) }
5833
+ if (primary == null) { primary = Math.min(ranges.length - 1, this.sel.primIndex) }
5834
+ setSelection(this, normalizeSelection(out, primary), options)
5835
+ }),
5836
+ addSelection: docMethodOp(function(anchor, head, options) {
5837
+ var ranges = this.sel.ranges.slice(0)
5838
+ ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor)))
5839
+ setSelection(this, normalizeSelection(ranges, ranges.length - 1), options)
5840
+ }),
5841
+
5842
+ getSelection: function(lineSep) {
5843
+ var this$1 = this;
5844
+
5845
+ var ranges = this.sel.ranges, lines
5846
+ for (var i = 0; i < ranges.length; i++) {
5847
+ var sel = getBetween(this$1, ranges[i].from(), ranges[i].to())
5848
+ lines = lines ? lines.concat(sel) : sel
5849
+ }
5850
+ if (lineSep === false) { return lines }
5851
+ else { return lines.join(lineSep || this.lineSeparator()) }
5852
+ },
5853
+ getSelections: function(lineSep) {
5854
+ var this$1 = this;
5855
+
5856
+ var parts = [], ranges = this.sel.ranges
5857
+ for (var i = 0; i < ranges.length; i++) {
5858
+ var sel = getBetween(this$1, ranges[i].from(), ranges[i].to())
5859
+ if (lineSep !== false) { sel = sel.join(lineSep || this$1.lineSeparator()) }
5860
+ parts[i] = sel
5861
+ }
5862
+ return parts
5863
+ },
5864
+ replaceSelection: function(code, collapse, origin) {
5865
+ var dup = []
5866
+ for (var i = 0; i < this.sel.ranges.length; i++)
5867
+ { dup[i] = code }
5868
+ this.replaceSelections(dup, collapse, origin || "+input")
5869
+ },
5870
+ replaceSelections: docMethodOp(function(code, collapse, origin) {
5871
+ var this$1 = this;
5872
+
5873
+ var changes = [], sel = this.sel
5874
+ for (var i = 0; i < sel.ranges.length; i++) {
5875
+ var range$$1 = sel.ranges[i]
5876
+ changes[i] = {from: range$$1.from(), to: range$$1.to(), text: this$1.splitLines(code[i]), origin: origin}
5877
+ }
5878
+ var newSel = collapse && collapse != "end" && computeReplacedSel(this, changes, collapse)
5879
+ for (var i$1 = changes.length - 1; i$1 >= 0; i$1--)
5880
+ { makeChange(this$1, changes[i$1]) }
5881
+ if (newSel) { setSelectionReplaceHistory(this, newSel) }
5882
+ else if (this.cm) { ensureCursorVisible(this.cm) }
5883
+ }),
5884
+ undo: docMethodOp(function() {makeChangeFromHistory(this, "undo")}),
5885
+ redo: docMethodOp(function() {makeChangeFromHistory(this, "redo")}),
5886
+ undoSelection: docMethodOp(function() {makeChangeFromHistory(this, "undo", true)}),
5887
+ redoSelection: docMethodOp(function() {makeChangeFromHistory(this, "redo", true)}),
5888
+
5889
+ setExtending: function(val) {this.extend = val},
5890
+ getExtending: function() {return this.extend},
5891
+
5892
+ historySize: function() {
5893
+ var hist = this.history, done = 0, undone = 0
5894
+ for (var i = 0; i < hist.done.length; i++) { if (!hist.done[i].ranges) { ++done } }
5895
+ for (var i$1 = 0; i$1 < hist.undone.length; i$1++) { if (!hist.undone[i$1].ranges) { ++undone } }
5896
+ return {undo: done, redo: undone}
5897
+ },
5898
+ clearHistory: function() {this.history = new History(this.history.maxGeneration)},
5899
+
5900
+ markClean: function() {
5901
+ this.cleanGeneration = this.changeGeneration(true)
5902
+ },
5903
+ changeGeneration: function(forceSplit) {
5904
+ if (forceSplit)
5905
+ { this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null }
5906
+ return this.history.generation
5907
+ },
5908
+ isClean: function (gen) {
5909
+ return this.history.generation == (gen || this.cleanGeneration)
5910
+ },
5911
+
5912
+ getHistory: function() {
5913
+ return {done: copyHistoryArray(this.history.done),
5914
+ undone: copyHistoryArray(this.history.undone)}
5915
+ },
5916
+ setHistory: function(histData) {
5917
+ var hist = this.history = new History(this.history.maxGeneration)
5918
+ hist.done = copyHistoryArray(histData.done.slice(0), null, true)
5919
+ hist.undone = copyHistoryArray(histData.undone.slice(0), null, true)
5920
+ },
5921
+
5922
+ setGutterMarker: docMethodOp(function(line, gutterID, value) {
5923
+ return changeLine(this, line, "gutter", function (line) {
5924
+ var markers = line.gutterMarkers || (line.gutterMarkers = {})
5925
+ markers[gutterID] = value
5926
+ if (!value && isEmpty(markers)) { line.gutterMarkers = null }
5927
+ return true
5928
+ })
5929
+ }),
5930
+
5931
+ clearGutter: docMethodOp(function(gutterID) {
5932
+ var this$1 = this;
5933
+
5934
+ var i = this.first
5935
+ this.iter(function (line) {
5936
+ if (line.gutterMarkers && line.gutterMarkers[gutterID]) {
5937
+ changeLine(this$1, line, "gutter", function () {
5938
+ line.gutterMarkers[gutterID] = null
5939
+ if (isEmpty(line.gutterMarkers)) { line.gutterMarkers = null }
5940
+ return true
5941
+ })
5942
+ }
5943
+ ++i
5944
+ })
5945
+ }),
5946
+
5947
+ lineInfo: function(line) {
5948
+ var n
5949
+ if (typeof line == "number") {
5950
+ if (!isLine(this, line)) { return null }
5951
+ n = line
5952
+ line = getLine(this, line)
5953
+ if (!line) { return null }
5954
+ } else {
5955
+ n = lineNo(line)
5956
+ if (n == null) { return null }
5957
+ }
5958
+ return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers,
5959
+ textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass,
5960
+ widgets: line.widgets}
5961
+ },
5962
+
5963
+ addLineClass: docMethodOp(function(handle, where, cls) {
5964
+ return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) {
5965
+ var prop = where == "text" ? "textClass"
5966
+ : where == "background" ? "bgClass"
5967
+ : where == "gutter" ? "gutterClass" : "wrapClass"
5968
+ if (!line[prop]) { line[prop] = cls }
5969
+ else if (classTest(cls).test(line[prop])) { return false }
5970
+ else { line[prop] += " " + cls }
5971
+ return true
5972
+ })
5973
+ }),
5974
+ removeLineClass: docMethodOp(function(handle, where, cls) {
5975
+ return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) {
5976
+ var prop = where == "text" ? "textClass"
5977
+ : where == "background" ? "bgClass"
5978
+ : where == "gutter" ? "gutterClass" : "wrapClass"
5979
+ var cur = line[prop]
5980
+ if (!cur) { return false }
5981
+ else if (cls == null) { line[prop] = null }
5982
+ else {
5983
+ var found = cur.match(classTest(cls))
5984
+ if (!found) { return false }
5985
+ var end = found.index + found[0].length
5986
+ line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null
5987
+ }
5988
+ return true
5989
+ })
5990
+ }),
5991
+
5992
+ addLineWidget: docMethodOp(function(handle, node, options) {
5993
+ return addLineWidget(this, handle, node, options)
5994
+ }),
5995
+ removeLineWidget: function(widget) { widget.clear() },
5996
+
5997
+ markText: function(from, to, options) {
5998
+ return markText(this, clipPos(this, from), clipPos(this, to), options, options && options.type || "range")
5999
+ },
6000
+ setBookmark: function(pos, options) {
6001
+ var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options),
6002
+ insertLeft: options && options.insertLeft,
6003
+ clearWhenEmpty: false, shared: options && options.shared,
6004
+ handleMouseEvents: options && options.handleMouseEvents}
6005
+ pos = clipPos(this, pos)
6006
+ return markText(this, pos, pos, realOpts, "bookmark")
6007
+ },
6008
+ findMarksAt: function(pos) {
6009
+ pos = clipPos(this, pos)
6010
+ var markers = [], spans = getLine(this, pos.line).markedSpans
6011
+ if (spans) { for (var i = 0; i < spans.length; ++i) {
6012
+ var span = spans[i]
6013
+ if ((span.from == null || span.from <= pos.ch) &&
6014
+ (span.to == null || span.to >= pos.ch))
6015
+ { markers.push(span.marker.parent || span.marker) }
6016
+ } }
6017
+ return markers
6018
+ },
6019
+ findMarks: function(from, to, filter) {
6020
+ from = clipPos(this, from); to = clipPos(this, to)
6021
+ var found = [], lineNo$$1 = from.line
6022
+ this.iter(from.line, to.line + 1, function (line) {
6023
+ var spans = line.markedSpans
6024
+ if (spans) { for (var i = 0; i < spans.length; i++) {
6025
+ var span = spans[i]
6026
+ if (!(span.to != null && lineNo$$1 == from.line && from.ch >= span.to ||
6027
+ span.from == null && lineNo$$1 != from.line ||
6028
+ span.from != null && lineNo$$1 == to.line && span.from >= to.ch) &&
6029
+ (!filter || filter(span.marker)))
6030
+ { found.push(span.marker.parent || span.marker) }
6031
+ } }
6032
+ ++lineNo$$1
6033
+ })
6034
+ return found
6035
+ },
6036
+ getAllMarks: function() {
6037
+ var markers = []
6038
+ this.iter(function (line) {
6039
+ var sps = line.markedSpans
6040
+ if (sps) { for (var i = 0; i < sps.length; ++i)
6041
+ { if (sps[i].from != null) { markers.push(sps[i].marker) } } }
6042
+ })
6043
+ return markers
6044
+ },
6045
+
6046
+ posFromIndex: function(off) {
6047
+ var ch, lineNo$$1 = this.first, sepSize = this.lineSeparator().length
6048
+ this.iter(function (line) {
6049
+ var sz = line.text.length + sepSize
6050
+ if (sz > off) { ch = off; return true }
6051
+ off -= sz
6052
+ ++lineNo$$1
6053
+ })
6054
+ return clipPos(this, Pos(lineNo$$1, ch))
6055
+ },
6056
+ indexFromPos: function (coords) {
6057
+ coords = clipPos(this, coords)
6058
+ var index = coords.ch
6059
+ if (coords.line < this.first || coords.ch < 0) { return 0 }
6060
+ var sepSize = this.lineSeparator().length
6061
+ this.iter(this.first, coords.line, function (line) { // iter aborts when callback returns a truthy value
6062
+ index += line.text.length + sepSize
6063
+ })
6064
+ return index
6065
+ },
6066
+
6067
+ copy: function(copyHistory) {
6068
+ var doc = new Doc(getLines(this, this.first, this.first + this.size),
6069
+ this.modeOption, this.first, this.lineSep)
6070
+ doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft
6071
+ doc.sel = this.sel
6072
+ doc.extend = false
6073
+ if (copyHistory) {
6074
+ doc.history.undoDepth = this.history.undoDepth
6075
+ doc.setHistory(this.getHistory())
6076
+ }
6077
+ return doc
6078
+ },
6079
+
6080
+ linkedDoc: function(options) {
6081
+ if (!options) { options = {} }
6082
+ var from = this.first, to = this.first + this.size
6083
+ if (options.from != null && options.from > from) { from = options.from }
6084
+ if (options.to != null && options.to < to) { to = options.to }
6085
+ var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from, this.lineSep)
6086
+ if (options.sharedHist) { copy.history = this.history
6087
+ ; }(this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist})
6088
+ copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}]
6089
+ copySharedMarkers(copy, findSharedMarkers(this))
6090
+ return copy
6091
+ },
6092
+ unlinkDoc: function(other) {
6093
+ var this$1 = this;
6094
+
6095
+ if (other instanceof CodeMirror$1) { other = other.doc }
6096
+ if (this.linked) { for (var i = 0; i < this.linked.length; ++i) {
6097
+ var link = this$1.linked[i]
6098
+ if (link.doc != other) { continue }
6099
+ this$1.linked.splice(i, 1)
6100
+ other.unlinkDoc(this$1)
6101
+ detachSharedMarkers(findSharedMarkers(this$1))
6102
+ break
6103
+ } }
6104
+ // If the histories were shared, split them again
6105
+ if (other.history == this.history) {
6106
+ var splitIds = [other.id]
6107
+ linkedDocs(other, function (doc) { return splitIds.push(doc.id); }, true)
6108
+ other.history = new History(null)
6109
+ other.history.done = copyHistoryArray(this.history.done, splitIds)
6110
+ other.history.undone = copyHistoryArray(this.history.undone, splitIds)
6111
+ }
6112
+ },
6113
+ iterLinkedDocs: function(f) {linkedDocs(this, f)},
6114
+
6115
+ getMode: function() {return this.mode},
6116
+ getEditor: function() {return this.cm},
6117
+
6118
+ splitLines: function(str) {
6119
+ if (this.lineSep) { return str.split(this.lineSep) }
6120
+ return splitLinesAuto(str)
6121
+ },
6122
+ lineSeparator: function() { return this.lineSep || "\n" }
6123
+ })
6124
+
6125
+ // Public alias.
6126
+ Doc.prototype.eachLine = Doc.prototype.iter
6127
+
6128
+ // Kludge to work around strange IE behavior where it'll sometimes
6129
+ // re-fire a series of drag-related events right after the drop (#1551)
6130
+ var lastDrop = 0
6131
+
6132
+ function onDrop(e) {
6133
+ var cm = this
6134
+ clearDragCursor(cm)
6135
+ if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e))
6136
+ { return }
6137
+ e_preventDefault(e)
6138
+ if (ie) { lastDrop = +new Date }
6139
+ var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files
6140
+ if (!pos || cm.isReadOnly()) { return }
6141
+ // Might be a file drop, in which case we simply extract the text
6142
+ // and insert it.
6143
+ if (files && files.length && window.FileReader && window.File) {
6144
+ var n = files.length, text = Array(n), read = 0
6145
+ var loadFile = function (file, i) {
6146
+ if (cm.options.allowDropFileTypes &&
6147
+ indexOf(cm.options.allowDropFileTypes, file.type) == -1)
6148
+ { return }
6149
+
6150
+ var reader = new FileReader
6151
+ reader.onload = operation(cm, function () {
6152
+ var content = reader.result
6153
+ if (/[\x00-\x08\x0e-\x1f]{2}/.test(content)) { content = "" }
6154
+ text[i] = content
6155
+ if (++read == n) {
6156
+ pos = clipPos(cm.doc, pos)
6157
+ var change = {from: pos, to: pos,
6158
+ text: cm.doc.splitLines(text.join(cm.doc.lineSeparator())),
6159
+ origin: "paste"}
6160
+ makeChange(cm.doc, change)
6161
+ setSelectionReplaceHistory(cm.doc, simpleSelection(pos, changeEnd(change)))
6162
+ }
6163
+ })
6164
+ reader.readAsText(file)
6165
+ }
6166
+ for (var i = 0; i < n; ++i) { loadFile(files[i], i) }
6167
+ } else { // Normal drop
6168
+ // Don't do a replace if the drop happened inside of the selected text.
6169
+ if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) {
6170
+ cm.state.draggingText(e)
6171
+ // Ensure the editor is re-focused
6172
+ setTimeout(function () { return cm.display.input.focus(); }, 20)
6173
+ return
6174
+ }
6175
+ try {
6176
+ var text$1 = e.dataTransfer.getData("Text")
6177
+ if (text$1) {
6178
+ var selected
6179
+ if (cm.state.draggingText && !cm.state.draggingText.copy)
6180
+ { selected = cm.listSelections() }
6181
+ setSelectionNoUndo(cm.doc, simpleSelection(pos, pos))
6182
+ if (selected) { for (var i$1 = 0; i$1 < selected.length; ++i$1)
6183
+ { replaceRange(cm.doc, "", selected[i$1].anchor, selected[i$1].head, "drag") } }
6184
+ cm.replaceSelection(text$1, "around", "paste")
6185
+ cm.display.input.focus()
6186
+ }
6187
+ }
6188
+ catch(e){}
6189
+ }
6190
+ }
6191
+
6192
+ function onDragStart(cm, e) {
6193
+ if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return }
6194
+ if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) { return }
6195
+
6196
+ e.dataTransfer.setData("Text", cm.getSelection())
6197
+ e.dataTransfer.effectAllowed = "copyMove"
6198
+
6199
+ // Use dummy image instead of default browsers image.
6200
+ // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
6201
+ if (e.dataTransfer.setDragImage && !safari) {
6202
+ var img = elt("img", null, null, "position: fixed; left: 0; top: 0;")
6203
+ img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="
6204
+ if (presto) {
6205
+ img.width = img.height = 1
6206
+ cm.display.wrapper.appendChild(img)
6207
+ // Force a relayout, or Opera won't use our image for some obscure reason
6208
+ img._top = img.offsetTop
6209
+ }
6210
+ e.dataTransfer.setDragImage(img, 0, 0)
6211
+ if (presto) { img.parentNode.removeChild(img) }
6212
+ }
6213
+ }
6214
+
6215
+ function onDragOver(cm, e) {
6216
+ var pos = posFromMouse(cm, e)
6217
+ if (!pos) { return }
6218
+ var frag = document.createDocumentFragment()
6219
+ drawSelectionCursor(cm, pos, frag)
6220
+ if (!cm.display.dragCursor) {
6221
+ cm.display.dragCursor = elt("div", null, "CodeMirror-cursors CodeMirror-dragcursors")
6222
+ cm.display.lineSpace.insertBefore(cm.display.dragCursor, cm.display.cursorDiv)
6223
+ }
6224
+ removeChildrenAndAdd(cm.display.dragCursor, frag)
6225
+ }
6226
+
6227
+ function clearDragCursor(cm) {
6228
+ if (cm.display.dragCursor) {
6229
+ cm.display.lineSpace.removeChild(cm.display.dragCursor)
6230
+ cm.display.dragCursor = null
6231
+ }
6232
+ }
6233
+
6234
+ // These must be handled carefully, because naively registering a
6235
+ // handler for each editor will cause the editors to never be
6236
+ // garbage collected.
6237
+
6238
+ function forEachCodeMirror(f) {
6239
+ if (!document.body.getElementsByClassName) { return }
6240
+ var byClass = document.body.getElementsByClassName("CodeMirror")
6241
+ for (var i = 0; i < byClass.length; i++) {
6242
+ var cm = byClass[i].CodeMirror
6243
+ if (cm) { f(cm) }
6244
+ }
6245
+ }
6246
+
6247
+ var globalsRegistered = false
6248
+ function ensureGlobalHandlers() {
6249
+ if (globalsRegistered) { return }
6250
+ registerGlobalHandlers()
6251
+ globalsRegistered = true
6252
+ }
6253
+ function registerGlobalHandlers() {
6254
+ // When the window resizes, we need to refresh active editors.
6255
+ var resizeTimer
6256
+ on(window, "resize", function () {
6257
+ if (resizeTimer == null) { resizeTimer = setTimeout(function () {
6258
+ resizeTimer = null
6259
+ forEachCodeMirror(onResize)
6260
+ }, 100) }
6261
+ })
6262
+ // When the window loses focus, we want to show the editor as blurred
6263
+ on(window, "blur", function () { return forEachCodeMirror(onBlur); })
6264
+ }
6265
+ // Called when the window resizes
6266
+ function onResize(cm) {
6267
+ var d = cm.display
6268
+ if (d.lastWrapHeight == d.wrapper.clientHeight && d.lastWrapWidth == d.wrapper.clientWidth)
6269
+ { return }
6270
+ // Might be a text scaling operation, clear size caches.
6271
+ d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null
6272
+ d.scrollbarsClipped = false
6273
+ cm.setSize()
6274
+ }
6275
+
6276
+ var keyNames = {
6277
+ 3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
6278
+ 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
6279
+ 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
6280
+ 46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod",
6281
+ 106: "*", 107: "=", 109: "-", 110: ".", 111: "/", 127: "Delete",
6282
+ 173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\",
6283
+ 221: "]", 222: "'", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete",
6284
+ 63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Insert"
6285
+ }
6286
+
6287
+ // Number keys
6288
+ for (var i = 0; i < 10; i++) { keyNames[i + 48] = keyNames[i + 96] = String(i) }
6289
+ // Alphabetic keys
6290
+ for (var i$1 = 65; i$1 <= 90; i$1++) { keyNames[i$1] = String.fromCharCode(i$1) }
6291
+ // Function keys
6292
+ for (var i$2 = 1; i$2 <= 12; i$2++) { keyNames[i$2 + 111] = keyNames[i$2 + 63235] = "F" + i$2 }
6293
+
6294
+ var keyMap = {}
6295
+
6296
+ keyMap.basic = {
6297
+ "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
6298
+ "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
6299
+ "Delete": "delCharAfter", "Backspace": "delCharBefore", "Shift-Backspace": "delCharBefore",
6300
+ "Tab": "defaultTab", "Shift-Tab": "indentAuto",
6301
+ "Enter": "newlineAndIndent", "Insert": "toggleOverwrite",
6302
+ "Esc": "singleSelection"
6303
+ }
6304
+ // Note that the save and find-related commands aren't defined by
6305
+ // default. User code or addons can define them. Unknown commands
6306
+ // are simply ignored.
6307
+ keyMap.pcDefault = {
6308
+ "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
6309
+ "Ctrl-Home": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Up": "goLineUp", "Ctrl-Down": "goLineDown",
6310
+ "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
6311
+ "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find",
6312
+ "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
6313
+ "Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
6314
+ "Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSelection",
6315
+ fallthrough: "basic"
6316
+ }
6317
+ // Very basic readline/emacs-style bindings, which are standard on Mac.
6318
+ keyMap.emacsy = {
6319
+ "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
6320
+ "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
6321
+ "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore",
6322
+ "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars",
6323
+ "Ctrl-O": "openLine"
6324
+ }
6325
+ keyMap.macDefault = {
6326
+ "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
6327
+ "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft",
6328
+ "Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineRight", "Alt-Backspace": "delGroupBefore",
6329
+ "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find",
6330
+ "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
6331
+ "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLineLeft", "Cmd-Delete": "delWrappedLineRight",
6332
+ "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocStart", "Ctrl-Down": "goDocEnd",
6333
+ fallthrough: ["basic", "emacsy"]
6334
+ }
6335
+ keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault
6336
+
6337
+ // KEYMAP DISPATCH
6338
+
6339
+ function normalizeKeyName(name) {
6340
+ var parts = name.split(/-(?!$)/)
6341
+ name = parts[parts.length - 1]
6342
+ var alt, ctrl, shift, cmd
6343
+ for (var i = 0; i < parts.length - 1; i++) {
6344
+ var mod = parts[i]
6345
+ if (/^(cmd|meta|m)$/i.test(mod)) { cmd = true }
6346
+ else if (/^a(lt)?$/i.test(mod)) { alt = true }
6347
+ else if (/^(c|ctrl|control)$/i.test(mod)) { ctrl = true }
6348
+ else if (/^s(hift)?$/i.test(mod)) { shift = true }
6349
+ else { throw new Error("Unrecognized modifier name: " + mod) }
6350
+ }
6351
+ if (alt) { name = "Alt-" + name }
6352
+ if (ctrl) { name = "Ctrl-" + name }
6353
+ if (cmd) { name = "Cmd-" + name }
6354
+ if (shift) { name = "Shift-" + name }
6355
+ return name
6356
+ }
6357
+
6358
+ // This is a kludge to keep keymaps mostly working as raw objects
6359
+ // (backwards compatibility) while at the same time support features
6360
+ // like normalization and multi-stroke key bindings. It compiles a
6361
+ // new normalized keymap, and then updates the old object to reflect
6362
+ // this.
6363
+ function normalizeKeyMap(keymap) {
6364
+ var copy = {}
6365
+ for (var keyname in keymap) { if (keymap.hasOwnProperty(keyname)) {
6366
+ var value = keymap[keyname]
6367
+ if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) { continue }
6368
+ if (value == "...") { delete keymap[keyname]; continue }
6369
+
6370
+ var keys = map(keyname.split(" "), normalizeKeyName)
6371
+ for (var i = 0; i < keys.length; i++) {
6372
+ var val = void 0, name = void 0
6373
+ if (i == keys.length - 1) {
6374
+ name = keys.join(" ")
6375
+ val = value
6376
+ } else {
6377
+ name = keys.slice(0, i + 1).join(" ")
6378
+ val = "..."
6379
+ }
6380
+ var prev = copy[name]
6381
+ if (!prev) { copy[name] = val }
6382
+ else if (prev != val) { throw new Error("Inconsistent bindings for " + name) }
6383
+ }
6384
+ delete keymap[keyname]
6385
+ } }
6386
+ for (var prop in copy) { keymap[prop] = copy[prop] }
6387
+ return keymap
6388
+ }
6389
+
6390
+ function lookupKey(key, map$$1, handle, context) {
6391
+ map$$1 = getKeyMap(map$$1)
6392
+ var found = map$$1.call ? map$$1.call(key, context) : map$$1[key]
6393
+ if (found === false) { return "nothing" }
6394
+ if (found === "...") { return "multi" }
6395
+ if (found != null && handle(found)) { return "handled" }
6396
+
6397
+ if (map$$1.fallthrough) {
6398
+ if (Object.prototype.toString.call(map$$1.fallthrough) != "[object Array]")
6399
+ { return lookupKey(key, map$$1.fallthrough, handle, context) }
6400
+ for (var i = 0; i < map$$1.fallthrough.length; i++) {
6401
+ var result = lookupKey(key, map$$1.fallthrough[i], handle, context)
6402
+ if (result) { return result }
6403
+ }
6404
+ }
6405
+ }
6406
+
6407
+ // Modifier key presses don't count as 'real' key presses for the
6408
+ // purpose of keymap fallthrough.
6409
+ function isModifierKey(value) {
6410
+ var name = typeof value == "string" ? value : keyNames[value.keyCode]
6411
+ return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod"
6412
+ }
6413
+
6414
+ // Look up the name of a key as indicated by an event object.
6415
+ function keyName(event, noShift) {
6416
+ if (presto && event.keyCode == 34 && event["char"]) { return false }
6417
+ var base = keyNames[event.keyCode], name = base
6418
+ if (name == null || event.altGraphKey) { return false }
6419
+ if (event.altKey && base != "Alt") { name = "Alt-" + name }
6420
+ if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != "Ctrl") { name = "Ctrl-" + name }
6421
+ if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != "Cmd") { name = "Cmd-" + name }
6422
+ if (!noShift && event.shiftKey && base != "Shift") { name = "Shift-" + name }
6423
+ return name
6424
+ }
6425
+
6426
+ function getKeyMap(val) {
6427
+ return typeof val == "string" ? keyMap[val] : val
6428
+ }
6429
+
6430
+ // Helper for deleting text near the selection(s), used to implement
6431
+ // backspace, delete, and similar functionality.
6432
+ function deleteNearSelection(cm, compute) {
6433
+ var ranges = cm.doc.sel.ranges, kill = []
6434
+ // Build up a set of ranges to kill first, merging overlapping
6435
+ // ranges.
6436
+ for (var i = 0; i < ranges.length; i++) {
6437
+ var toKill = compute(ranges[i])
6438
+ while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) {
6439
+ var replaced = kill.pop()
6440
+ if (cmp(replaced.from, toKill.from) < 0) {
6441
+ toKill.from = replaced.from
6442
+ break
6443
+ }
6444
+ }
6445
+ kill.push(toKill)
6446
+ }
6447
+ // Next, remove those actual ranges.
6448
+ runInOp(cm, function () {
6449
+ for (var i = kill.length - 1; i >= 0; i--)
6450
+ { replaceRange(cm.doc, "", kill[i].from, kill[i].to, "+delete") }
6451
+ ensureCursorVisible(cm)
6452
+ })
6453
+ }
6454
+
6455
+ // Commands are parameter-less actions that can be performed on an
6456
+ // editor, mostly used for keybindings.
6457
+ var commands = {
6458
+ selectAll: selectAll,
6459
+ singleSelection: function (cm) { return cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head"), sel_dontScroll); },
6460
+ killLine: function (cm) { return deleteNearSelection(cm, function (range) {
6461
+ if (range.empty()) {
6462
+ var len = getLine(cm.doc, range.head.line).text.length
6463
+ if (range.head.ch == len && range.head.line < cm.lastLine())
6464
+ { return {from: range.head, to: Pos(range.head.line + 1, 0)} }
6465
+ else
6466
+ { return {from: range.head, to: Pos(range.head.line, len)} }
6467
+ } else {
6468
+ return {from: range.from(), to: range.to()}
6469
+ }
6470
+ }); },
6471
+ deleteLine: function (cm) { return deleteNearSelection(cm, function (range) { return ({
6472
+ from: Pos(range.from().line, 0),
6473
+ to: clipPos(cm.doc, Pos(range.to().line + 1, 0))
6474
+ }); }); },
6475
+ delLineLeft: function (cm) { return deleteNearSelection(cm, function (range) { return ({
6476
+ from: Pos(range.from().line, 0), to: range.from()
6477
+ }); }); },
6478
+ delWrappedLineLeft: function (cm) { return deleteNearSelection(cm, function (range) {
6479
+ var top = cm.charCoords(range.head, "div").top + 5
6480
+ var leftPos = cm.coordsChar({left: 0, top: top}, "div")
6481
+ return {from: leftPos, to: range.from()}
6482
+ }); },
6483
+ delWrappedLineRight: function (cm) { return deleteNearSelection(cm, function (range) {
6484
+ var top = cm.charCoords(range.head, "div").top + 5
6485
+ var rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div")
6486
+ return {from: range.from(), to: rightPos }
6487
+ }); },
6488
+ undo: function (cm) { return cm.undo(); },
6489
+ redo: function (cm) { return cm.redo(); },
6490
+ undoSelection: function (cm) { return cm.undoSelection(); },
6491
+ redoSelection: function (cm) { return cm.redoSelection(); },
6492
+ goDocStart: function (cm) { return cm.extendSelection(Pos(cm.firstLine(), 0)); },
6493
+ goDocEnd: function (cm) { return cm.extendSelection(Pos(cm.lastLine())); },
6494
+ goLineStart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStart(cm, range.head.line); },
6495
+ {origin: "+move", bias: 1}
6496
+ ); },
6497
+ goLineStartSmart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStartSmart(cm, range.head); },
6498
+ {origin: "+move", bias: 1}
6499
+ ); },
6500
+ goLineEnd: function (cm) { return cm.extendSelectionsBy(function (range) { return lineEnd(cm, range.head.line); },
6501
+ {origin: "+move", bias: -1}
6502
+ ); },
6503
+ goLineRight: function (cm) { return cm.extendSelectionsBy(function (range) {
6504
+ var top = cm.charCoords(range.head, "div").top + 5
6505
+ return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div")
6506
+ }, sel_move); },
6507
+ goLineLeft: function (cm) { return cm.extendSelectionsBy(function (range) {
6508
+ var top = cm.charCoords(range.head, "div").top + 5
6509
+ return cm.coordsChar({left: 0, top: top}, "div")
6510
+ }, sel_move); },
6511
+ goLineLeftSmart: function (cm) { return cm.extendSelectionsBy(function (range) {
6512
+ var top = cm.charCoords(range.head, "div").top + 5
6513
+ var pos = cm.coordsChar({left: 0, top: top}, "div")
6514
+ if (pos.ch < cm.getLine(pos.line).search(/\S/)) { return lineStartSmart(cm, range.head) }
6515
+ return pos
6516
+ }, sel_move); },
6517
+ goLineUp: function (cm) { return cm.moveV(-1, "line"); },
6518
+ goLineDown: function (cm) { return cm.moveV(1, "line"); },
6519
+ goPageUp: function (cm) { return cm.moveV(-1, "page"); },
6520
+ goPageDown: function (cm) { return cm.moveV(1, "page"); },
6521
+ goCharLeft: function (cm) { return cm.moveH(-1, "char"); },
6522
+ goCharRight: function (cm) { return cm.moveH(1, "char"); },
6523
+ goColumnLeft: function (cm) { return cm.moveH(-1, "column"); },
6524
+ goColumnRight: function (cm) { return cm.moveH(1, "column"); },
6525
+ goWordLeft: function (cm) { return cm.moveH(-1, "word"); },
6526
+ goGroupRight: function (cm) { return cm.moveH(1, "group"); },
6527
+ goGroupLeft: function (cm) { return cm.moveH(-1, "group"); },
6528
+ goWordRight: function (cm) { return cm.moveH(1, "word"); },
6529
+ delCharBefore: function (cm) { return cm.deleteH(-1, "char"); },
6530
+ delCharAfter: function (cm) { return cm.deleteH(1, "char"); },
6531
+ delWordBefore: function (cm) { return cm.deleteH(-1, "word"); },
6532
+ delWordAfter: function (cm) { return cm.deleteH(1, "word"); },
6533
+ delGroupBefore: function (cm) { return cm.deleteH(-1, "group"); },
6534
+ delGroupAfter: function (cm) { return cm.deleteH(1, "group"); },
6535
+ indentAuto: function (cm) { return cm.indentSelection("smart"); },
6536
+ indentMore: function (cm) { return cm.indentSelection("add"); },
6537
+ indentLess: function (cm) { return cm.indentSelection("subtract"); },
6538
+ insertTab: function (cm) { return cm.replaceSelection("\t"); },
6539
+ insertSoftTab: function (cm) {
6540
+ var spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize
6541
+ for (var i = 0; i < ranges.length; i++) {
6542
+ var pos = ranges[i].from()
6543
+ var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize)
6544
+ spaces.push(spaceStr(tabSize - col % tabSize))
6545
+ }
6546
+ cm.replaceSelections(spaces)
6547
+ },
6548
+ defaultTab: function (cm) {
6549
+ if (cm.somethingSelected()) { cm.indentSelection("add") }
6550
+ else { cm.execCommand("insertTab") }
6551
+ },
6552
+ // Swap the two chars left and right of each selection's head.
6553
+ // Move cursor behind the two swapped characters afterwards.
6554
+ //
6555
+ // Doesn't consider line feeds a character.
6556
+ // Doesn't scan more than one line above to find a character.
6557
+ // Doesn't do anything on an empty line.
6558
+ // Doesn't do anything with non-empty selections.
6559
+ transposeChars: function (cm) { return runInOp(cm, function () {
6560
+ var ranges = cm.listSelections(), newSel = []
6561
+ for (var i = 0; i < ranges.length; i++) {
6562
+ if (!ranges[i].empty()) { continue }
6563
+ var cur = ranges[i].head, line = getLine(cm.doc, cur.line).text
6564
+ if (line) {
6565
+ if (cur.ch == line.length) { cur = new Pos(cur.line, cur.ch - 1) }
6566
+ if (cur.ch > 0) {
6567
+ cur = new Pos(cur.line, cur.ch + 1)
6568
+ cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2),
6569
+ Pos(cur.line, cur.ch - 2), cur, "+transpose")
6570
+ } else if (cur.line > cm.doc.first) {
6571
+ var prev = getLine(cm.doc, cur.line - 1).text
6572
+ if (prev) {
6573
+ cur = new Pos(cur.line, 1)
6574
+ cm.replaceRange(line.charAt(0) + cm.doc.lineSeparator() +
6575
+ prev.charAt(prev.length - 1),
6576
+ Pos(cur.line - 1, prev.length - 1), cur, "+transpose")
6577
+ }
6578
+ }
6579
+ }
6580
+ newSel.push(new Range(cur, cur))
6581
+ }
6582
+ cm.setSelections(newSel)
6583
+ }); },
6584
+ newlineAndIndent: function (cm) { return runInOp(cm, function () {
6585
+ var sels = cm.listSelections()
6586
+ for (var i = sels.length - 1; i >= 0; i--)
6587
+ { cm.replaceRange(cm.doc.lineSeparator(), sels[i].anchor, sels[i].head, "+input") }
6588
+ sels = cm.listSelections()
6589
+ for (var i$1 = 0; i$1 < sels.length; i$1++)
6590
+ { cm.indentLine(sels[i$1].from().line, null, true) }
6591
+ ensureCursorVisible(cm)
6592
+ }); },
6593
+ openLine: function (cm) { return cm.replaceSelection("\n", "start"); },
6594
+ toggleOverwrite: function (cm) { return cm.toggleOverwrite(); }
6595
+ }
6596
+
6597
+
6598
+ function lineStart(cm, lineN) {
6599
+ var line = getLine(cm.doc, lineN)
6600
+ var visual = visualLine(line)
6601
+ if (visual != line) { lineN = lineNo(visual) }
6602
+ var order = getOrder(visual)
6603
+ var ch = !order ? 0 : order[0].level % 2 ? lineRight(visual) : lineLeft(visual)
6604
+ return Pos(lineN, ch)
6605
+ }
6606
+ function lineEnd(cm, lineN) {
6607
+ var merged, line = getLine(cm.doc, lineN)
6608
+ while (merged = collapsedSpanAtEnd(line)) {
6609
+ line = merged.find(1, true).line
6610
+ lineN = null
6611
+ }
6612
+ var order = getOrder(line)
6613
+ var ch = !order ? line.text.length : order[0].level % 2 ? lineLeft(line) : lineRight(line)
6614
+ return Pos(lineN == null ? lineNo(line) : lineN, ch)
6615
+ }
6616
+ function lineStartSmart(cm, pos) {
6617
+ var start = lineStart(cm, pos.line)
6618
+ var line = getLine(cm.doc, start.line)
6619
+ var order = getOrder(line)
6620
+ if (!order || order[0].level == 0) {
6621
+ var firstNonWS = Math.max(0, line.text.search(/\S/))
6622
+ var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch
6623
+ return Pos(start.line, inWS ? 0 : firstNonWS)
6624
+ }
6625
+ return start
6626
+ }
6627
+
6628
+ // Run a handler that was bound to a key.
6629
+ function doHandleBinding(cm, bound, dropShift) {
6630
+ if (typeof bound == "string") {
6631
+ bound = commands[bound]
6632
+ if (!bound) { return false }
6633
+ }
6634
+ // Ensure previous input has been read, so that the handler sees a
6635
+ // consistent view of the document
6636
+ cm.display.input.ensurePolled()
6637
+ var prevShift = cm.display.shift, done = false
6638
+ try {
6639
+ if (cm.isReadOnly()) { cm.state.suppressEdits = true }
6640
+ if (dropShift) { cm.display.shift = false }
6641
+ done = bound(cm) != Pass
6642
+ } finally {
6643
+ cm.display.shift = prevShift
6644
+ cm.state.suppressEdits = false
6645
+ }
6646
+ return done
6647
+ }
6648
+
6649
+ function lookupKeyForEditor(cm, name, handle) {
6650
+ for (var i = 0; i < cm.state.keyMaps.length; i++) {
6651
+ var result = lookupKey(name, cm.state.keyMaps[i], handle, cm)
6652
+ if (result) { return result }
6653
+ }
6654
+ return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle, cm))
6655
+ || lookupKey(name, cm.options.keyMap, handle, cm)
6656
+ }
6657
+
6658
+ var stopSeq = new Delayed
6659
+ function dispatchKey(cm, name, e, handle) {
6660
+ var seq = cm.state.keySeq
6661
+ if (seq) {
6662
+ if (isModifierKey(name)) { return "handled" }
6663
+ stopSeq.set(50, function () {
6664
+ if (cm.state.keySeq == seq) {
6665
+ cm.state.keySeq = null
6666
+ cm.display.input.reset()
6667
+ }
6668
+ })
6669
+ name = seq + " " + name
6670
+ }
6671
+ var result = lookupKeyForEditor(cm, name, handle)
6672
+
6673
+ if (result == "multi")
6674
+ { cm.state.keySeq = name }
6675
+ if (result == "handled")
6676
+ { signalLater(cm, "keyHandled", cm, name, e) }
6677
+
6678
+ if (result == "handled" || result == "multi") {
6679
+ e_preventDefault(e)
6680
+ restartBlink(cm)
6681
+ }
6682
+
6683
+ if (seq && !result && /\'$/.test(name)) {
6684
+ e_preventDefault(e)
6685
+ return true
6686
+ }
6687
+ return !!result
6688
+ }
6689
+
6690
+ // Handle a key from the keydown event.
6691
+ function handleKeyBinding(cm, e) {
6692
+ var name = keyName(e, true)
6693
+ if (!name) { return false }
6694
+
6695
+ if (e.shiftKey && !cm.state.keySeq) {
6696
+ // First try to resolve full name (including 'Shift-'). Failing
6697
+ // that, see if there is a cursor-motion command (starting with
6698
+ // 'go') bound to the keyname without 'Shift-'.
6699
+ return dispatchKey(cm, "Shift-" + name, e, function (b) { return doHandleBinding(cm, b, true); })
6700
+ || dispatchKey(cm, name, e, function (b) {
6701
+ if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion)
6702
+ { return doHandleBinding(cm, b) }
6703
+ })
6704
+ } else {
6705
+ return dispatchKey(cm, name, e, function (b) { return doHandleBinding(cm, b); })
6706
+ }
6707
+ }
6708
+
6709
+ // Handle a key from the keypress event
6710
+ function handleCharBinding(cm, e, ch) {
6711
+ return dispatchKey(cm, "'" + ch + "'", e, function (b) { return doHandleBinding(cm, b, true); })
6712
+ }
6713
+
6714
+ var lastStoppedKey = null
6715
+ function onKeyDown(e) {
6716
+ var cm = this
6717
+ cm.curOp.focus = activeElt()
6718
+ if (signalDOMEvent(cm, e)) { return }
6719
+ // IE does strange things with escape.
6720
+ if (ie && ie_version < 11 && e.keyCode == 27) { e.returnValue = false }
6721
+ var code = e.keyCode
6722
+ cm.display.shift = code == 16 || e.shiftKey
6723
+ var handled = handleKeyBinding(cm, e)
6724
+ if (presto) {
6725
+ lastStoppedKey = handled ? code : null
6726
+ // Opera has no cut event... we try to at least catch the key combo
6727
+ if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey))
6728
+ { cm.replaceSelection("", null, "cut") }
6729
+ }
6730
+
6731
+ // Turn mouse into crosshair when Alt is held on Mac.
6732
+ if (code == 18 && !/\bCodeMirror-crosshair\b/.test(cm.display.lineDiv.className))
6733
+ { showCrossHair(cm) }
6734
+ }
6735
+
6736
+ function showCrossHair(cm) {
6737
+ var lineDiv = cm.display.lineDiv
6738
+ addClass(lineDiv, "CodeMirror-crosshair")
6739
+
6740
+ function up(e) {
6741
+ if (e.keyCode == 18 || !e.altKey) {
6742
+ rmClass(lineDiv, "CodeMirror-crosshair")
6743
+ off(document, "keyup", up)
6744
+ off(document, "mouseover", up)
6745
+ }
6746
+ }
6747
+ on(document, "keyup", up)
6748
+ on(document, "mouseover", up)
6749
+ }
6750
+
6751
+ function onKeyUp(e) {
6752
+ if (e.keyCode == 16) { this.doc.sel.shift = false }
6753
+ signalDOMEvent(this, e)
6754
+ }
6755
+
6756
+ function onKeyPress$1(e) {
6757
+ var cm = this
6758
+ if (eventInWidget(cm.display, e) || signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) { return }
6759
+ var keyCode = e.keyCode, charCode = e.charCode
6760
+ if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return}
6761
+ if ((presto && (!e.which || e.which < 10)) && handleKeyBinding(cm, e)) { return }
6762
+ var ch = String.fromCharCode(charCode == null ? keyCode : charCode)
6763
+ // Some browsers fire keypress events for backspace
6764
+ if (ch == "\x08") { return }
6765
+ if (handleCharBinding(cm, e, ch)) { return }
6766
+ cm.display.input.onKeyPress(e)
6767
+ }
6768
+
6769
+ // A mouse down can be a single click, double click, triple click,
6770
+ // start of selection drag, start of text drag, new cursor
6771
+ // (ctrl-click), rectangle drag (alt-drag), or xwin
6772
+ // middle-click-paste. Or it might be a click on something we should
6773
+ // not interfere with, such as a scrollbar or widget.
6774
+ function onMouseDown(e) {
6775
+ var cm = this, display = cm.display
6776
+ if (signalDOMEvent(cm, e) || display.activeTouch && display.input.supportsTouch()) { return }
6777
+ display.input.ensurePolled()
6778
+ display.shift = e.shiftKey
6779
+
6780
+ if (eventInWidget(display, e)) {
6781
+ if (!webkit) {
6782
+ // Briefly turn off draggability, to allow widgets to do
6783
+ // normal dragging things.
6784
+ display.scroller.draggable = false
6785
+ setTimeout(function () { return display.scroller.draggable = true; }, 100)
6786
+ }
6787
+ return
6788
+ }
6789
+ if (clickInGutter(cm, e)) { return }
6790
+ var start = posFromMouse(cm, e)
6791
+ window.focus()
6792
+
6793
+ switch (e_button(e)) {
6794
+ case 1:
6795
+ // #3261: make sure, that we're not starting a second selection
6796
+ if (cm.state.selectingText)
6797
+ { cm.state.selectingText(e) }
6798
+ else if (start)
6799
+ { leftButtonDown(cm, e, start) }
6800
+ else if (e_target(e) == display.scroller)
6801
+ { e_preventDefault(e) }
6802
+ break
6803
+ case 2:
6804
+ if (webkit) { cm.state.lastMiddleDown = +new Date }
6805
+ if (start) { extendSelection(cm.doc, start) }
6806
+ setTimeout(function () { return display.input.focus(); }, 20)
6807
+ e_preventDefault(e)
6808
+ break
6809
+ case 3:
6810
+ if (captureRightClick) { onContextMenu$1(cm, e) }
6811
+ else { delayBlurEvent(cm) }
6812
+ break
6813
+ }
6814
+ }
6815
+
6816
+ var lastClick;
6817
+ var lastDoubleClick
6818
+ function leftButtonDown(cm, e, start) {
6819
+ if (ie) { setTimeout(bind(ensureFocus, cm), 0) }
6820
+ else { cm.curOp.focus = activeElt() }
6821
+
6822
+ var now = +new Date, type
6823
+ if (lastDoubleClick && lastDoubleClick.time > now - 400 && cmp(lastDoubleClick.pos, start) == 0) {
6824
+ type = "triple"
6825
+ } else if (lastClick && lastClick.time > now - 400 && cmp(lastClick.pos, start) == 0) {
6826
+ type = "double"
6827
+ lastDoubleClick = {time: now, pos: start}
6828
+ } else {
6829
+ type = "single"
6830
+ lastClick = {time: now, pos: start}
6831
+ }
6832
+
6833
+ var sel = cm.doc.sel, modifier = mac ? e.metaKey : e.ctrlKey, contained
6834
+ if (cm.options.dragDrop && dragAndDrop && !cm.isReadOnly() &&
6835
+ type == "single" && (contained = sel.contains(start)) > -1 &&
6836
+ (cmp((contained = sel.ranges[contained]).from(), start) < 0 || start.xRel > 0) &&
6837
+ (cmp(contained.to(), start) > 0 || start.xRel < 0))
6838
+ { leftButtonStartDrag(cm, e, start, modifier) }
6839
+ else
6840
+ { leftButtonSelect(cm, e, start, type, modifier) }
6841
+ }
6842
+
6843
+ // Start a text drag. When it ends, see if any dragging actually
6844
+ // happen, and treat as a click if it didn't.
6845
+ function leftButtonStartDrag(cm, e, start, modifier) {
6846
+ var display = cm.display, startTime = +new Date
6847
+ var dragEnd = operation(cm, function (e2) {
6848
+ if (webkit) { display.scroller.draggable = false }
6849
+ cm.state.draggingText = false
6850
+ off(document, "mouseup", dragEnd)
6851
+ off(display.scroller, "drop", dragEnd)
6852
+ if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) {
6853
+ e_preventDefault(e2)
6854
+ if (!modifier && +new Date - 200 < startTime)
6855
+ { extendSelection(cm.doc, start) }
6856
+ // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081)
6857
+ if (webkit || ie && ie_version == 9)
6858
+ { setTimeout(function () {document.body.focus(); display.input.focus()}, 20) }
6859
+ else
6860
+ { display.input.focus() }
6861
+ }
6862
+ })
6863
+ // Let the drag handler handle this.
6864
+ if (webkit) { display.scroller.draggable = true }
6865
+ cm.state.draggingText = dragEnd
6866
+ dragEnd.copy = mac ? e.altKey : e.ctrlKey
6867
+ // IE's approach to draggable
6868
+ if (display.scroller.dragDrop) { display.scroller.dragDrop() }
6869
+ on(document, "mouseup", dragEnd)
6870
+ on(display.scroller, "drop", dragEnd)
6871
+ }
6872
+
6873
+ // Normal selection, as opposed to text dragging.
6874
+ function leftButtonSelect(cm, e, start, type, addNew) {
6875
+ var display = cm.display, doc = cm.doc
6876
+ e_preventDefault(e)
6877
+
6878
+ var ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges
6879
+ if (addNew && !e.shiftKey) {
6880
+ ourIndex = doc.sel.contains(start)
6881
+ if (ourIndex > -1)
6882
+ { ourRange = ranges[ourIndex] }
6883
+ else
6884
+ { ourRange = new Range(start, start) }
6885
+ } else {
6886
+ ourRange = doc.sel.primary()
6887
+ ourIndex = doc.sel.primIndex
6888
+ }
6889
+
6890
+ if (chromeOS ? e.shiftKey && e.metaKey : e.altKey) {
6891
+ type = "rect"
6892
+ if (!addNew) { ourRange = new Range(start, start) }
6893
+ start = posFromMouse(cm, e, true, true)
6894
+ ourIndex = -1
6895
+ } else if (type == "double") {
6896
+ var word = cm.findWordAt(start)
6897
+ if (cm.display.shift || doc.extend)
6898
+ { ourRange = extendRange(doc, ourRange, word.anchor, word.head) }
6899
+ else
6900
+ { ourRange = word }
6901
+ } else if (type == "triple") {
6902
+ var line = new Range(Pos(start.line, 0), clipPos(doc, Pos(start.line + 1, 0)))
6903
+ if (cm.display.shift || doc.extend)
6904
+ { ourRange = extendRange(doc, ourRange, line.anchor, line.head) }
6905
+ else
6906
+ { ourRange = line }
6907
+ } else {
6908
+ ourRange = extendRange(doc, ourRange, start)
6909
+ }
6910
+
6911
+ if (!addNew) {
6912
+ ourIndex = 0
6913
+ setSelection(doc, new Selection([ourRange], 0), sel_mouse)
6914
+ startSel = doc.sel
6915
+ } else if (ourIndex == -1) {
6916
+ ourIndex = ranges.length
6917
+ setSelection(doc, normalizeSelection(ranges.concat([ourRange]), ourIndex),
6918
+ {scroll: false, origin: "*mouse"})
6919
+ } else if (ranges.length > 1 && ranges[ourIndex].empty() && type == "single" && !e.shiftKey) {
6920
+ setSelection(doc, normalizeSelection(ranges.slice(0, ourIndex).concat(ranges.slice(ourIndex + 1)), 0),
6921
+ {scroll: false, origin: "*mouse"})
6922
+ startSel = doc.sel
6923
+ } else {
6924
+ replaceOneSelection(doc, ourIndex, ourRange, sel_mouse)
6925
+ }
6926
+
6927
+ var lastPos = start
6928
+ function extendTo(pos) {
6929
+ if (cmp(lastPos, pos) == 0) { return }
6930
+ lastPos = pos
6931
+
6932
+ if (type == "rect") {
6933
+ var ranges = [], tabSize = cm.options.tabSize
6934
+ var startCol = countColumn(getLine(doc, start.line).text, start.ch, tabSize)
6935
+ var posCol = countColumn(getLine(doc, pos.line).text, pos.ch, tabSize)
6936
+ var left = Math.min(startCol, posCol), right = Math.max(startCol, posCol)
6937
+ for (var line = Math.min(start.line, pos.line), end = Math.min(cm.lastLine(), Math.max(start.line, pos.line));
6938
+ line <= end; line++) {
6939
+ var text = getLine(doc, line).text, leftPos = findColumn(text, left, tabSize)
6940
+ if (left == right)
6941
+ { ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos))) }
6942
+ else if (text.length > leftPos)
6943
+ { ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize)))) }
6944
+ }
6945
+ if (!ranges.length) { ranges.push(new Range(start, start)) }
6946
+ setSelection(doc, normalizeSelection(startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex),
6947
+ {origin: "*mouse", scroll: false})
6948
+ cm.scrollIntoView(pos)
6949
+ } else {
6950
+ var oldRange = ourRange
6951
+ var anchor = oldRange.anchor, head = pos
6952
+ if (type != "single") {
6953
+ var range$$1
6954
+ if (type == "double")
6955
+ { range$$1 = cm.findWordAt(pos) }
6956
+ else
6957
+ { range$$1 = new Range(Pos(pos.line, 0), clipPos(doc, Pos(pos.line + 1, 0))) }
6958
+ if (cmp(range$$1.anchor, anchor) > 0) {
6959
+ head = range$$1.head
6960
+ anchor = minPos(oldRange.from(), range$$1.anchor)
6961
+ } else {
6962
+ head = range$$1.anchor
6963
+ anchor = maxPos(oldRange.to(), range$$1.head)
6964
+ }
6965
+ }
6966
+ var ranges$1 = startSel.ranges.slice(0)
6967
+ ranges$1[ourIndex] = new Range(clipPos(doc, anchor), head)
6968
+ setSelection(doc, normalizeSelection(ranges$1, ourIndex), sel_mouse)
6969
+ }
6970
+ }
6971
+
6972
+ var editorSize = display.wrapper.getBoundingClientRect()
6973
+ // Used to ensure timeout re-tries don't fire when another extend
6974
+ // happened in the meantime (clearTimeout isn't reliable -- at
6975
+ // least on Chrome, the timeouts still happen even when cleared,
6976
+ // if the clear happens after their scheduled firing time).
6977
+ var counter = 0
6978
+
6979
+ function extend(e) {
6980
+ var curCount = ++counter
6981
+ var cur = posFromMouse(cm, e, true, type == "rect")
6982
+ if (!cur) { return }
6983
+ if (cmp(cur, lastPos) != 0) {
6984
+ cm.curOp.focus = activeElt()
6985
+ extendTo(cur)
6986
+ var visible = visibleLines(display, doc)
6987
+ if (cur.line >= visible.to || cur.line < visible.from)
6988
+ { setTimeout(operation(cm, function () {if (counter == curCount) { extend(e) }}), 150) }
6989
+ } else {
6990
+ var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0
6991
+ if (outside) { setTimeout(operation(cm, function () {
6992
+ if (counter != curCount) { return }
6993
+ display.scroller.scrollTop += outside
6994
+ extend(e)
6995
+ }), 50) }
6996
+ }
6997
+ }
6998
+
6999
+ function done(e) {
7000
+ cm.state.selectingText = false
7001
+ counter = Infinity
7002
+ e_preventDefault(e)
7003
+ display.input.focus()
7004
+ off(document, "mousemove", move)
7005
+ off(document, "mouseup", up)
7006
+ doc.history.lastSelOrigin = null
7007
+ }
7008
+
7009
+ var move = operation(cm, function (e) {
7010
+ if (!e_button(e)) { done(e) }
7011
+ else { extend(e) }
7012
+ })
7013
+ var up = operation(cm, done)
7014
+ cm.state.selectingText = up
7015
+ on(document, "mousemove", move)
7016
+ on(document, "mouseup", up)
7017
+ }
7018
+
7019
+
7020
+ // Determines whether an event happened in the gutter, and fires the
7021
+ // handlers for the corresponding event.
7022
+ function gutterEvent(cm, e, type, prevent) {
7023
+ var mX, mY
7024
+ try { mX = e.clientX; mY = e.clientY }
7025
+ catch(e) { return false }
7026
+ if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) { return false }
7027
+ if (prevent) { e_preventDefault(e) }
7028
+
7029
+ var display = cm.display
7030
+ var lineBox = display.lineDiv.getBoundingClientRect()
7031
+
7032
+ if (mY > lineBox.bottom || !hasHandler(cm, type)) { return e_defaultPrevented(e) }
7033
+ mY -= lineBox.top - display.viewOffset
7034
+
7035
+ for (var i = 0; i < cm.options.gutters.length; ++i) {
7036
+ var g = display.gutters.childNodes[i]
7037
+ if (g && g.getBoundingClientRect().right >= mX) {
7038
+ var line = lineAtHeight(cm.doc, mY)
7039
+ var gutter = cm.options.gutters[i]
7040
+ signal(cm, type, cm, line, gutter, e)
7041
+ return e_defaultPrevented(e)
7042
+ }
7043
+ }
7044
+ }
7045
+
7046
+ function clickInGutter(cm, e) {
7047
+ return gutterEvent(cm, e, "gutterClick", true)
7048
+ }
7049
+
7050
+ // CONTEXT MENU HANDLING
7051
+
7052
+ // To make the context menu work, we need to briefly unhide the
7053
+ // textarea (making it as unobtrusive as possible) to let the
7054
+ // right-click take effect on it.
7055
+ function onContextMenu$1(cm, e) {
7056
+ if (eventInWidget(cm.display, e) || contextMenuInGutter(cm, e)) { return }
7057
+ if (signalDOMEvent(cm, e, "contextmenu")) { return }
7058
+ cm.display.input.onContextMenu(e)
7059
+ }
7060
+
7061
+ function contextMenuInGutter(cm, e) {
7062
+ if (!hasHandler(cm, "gutterContextMenu")) { return false }
7063
+ return gutterEvent(cm, e, "gutterContextMenu", false)
7064
+ }
7065
+
7066
+ function themeChanged(cm) {
7067
+ cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") +
7068
+ cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-")
7069
+ clearCaches(cm)
7070
+ }
7071
+
7072
+ var Init = {toString: function(){return "CodeMirror.Init"}}
7073
+
7074
+ var defaults = {}
7075
+ var optionHandlers = {}
7076
+
7077
+ function defineOptions(CodeMirror) {
7078
+ var optionHandlers = CodeMirror.optionHandlers
7079
+
7080
+ function option(name, deflt, handle, notOnInit) {
7081
+ CodeMirror.defaults[name] = deflt
7082
+ if (handle) { optionHandlers[name] =
7083
+ notOnInit ? function (cm, val, old) {if (old != Init) { handle(cm, val, old) }} : handle }
7084
+ }
7085
+
7086
+ CodeMirror.defineOption = option
7087
+
7088
+ // Passed to option handlers when there is no old value.
7089
+ CodeMirror.Init = Init
7090
+
7091
+ // These two are, on init, called from the constructor because they
7092
+ // have to be initialized before the editor can start at all.
7093
+ option("value", "", function (cm, val) { return cm.setValue(val); }, true)
7094
+ option("mode", null, function (cm, val) {
7095
+ cm.doc.modeOption = val
7096
+ loadMode(cm)
7097
+ }, true)
7098
+
7099
+ option("indentUnit", 2, loadMode, true)
7100
+ option("indentWithTabs", false)
7101
+ option("smartIndent", true)
7102
+ option("tabSize", 4, function (cm) {
7103
+ resetModeState(cm)
7104
+ clearCaches(cm)
7105
+ regChange(cm)
7106
+ }, true)
7107
+ option("lineSeparator", null, function (cm, val) {
7108
+ cm.doc.lineSep = val
7109
+ if (!val) { return }
7110
+ var newBreaks = [], lineNo = cm.doc.first
7111
+ cm.doc.iter(function (line) {
7112
+ for (var pos = 0;;) {
7113
+ var found = line.text.indexOf(val, pos)
7114
+ if (found == -1) { break }
7115
+ pos = found + val.length
7116
+ newBreaks.push(Pos(lineNo, found))
7117
+ }
7118
+ lineNo++
7119
+ })
7120
+ for (var i = newBreaks.length - 1; i >= 0; i--)
7121
+ { replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i].ch + val.length)) }
7122
+ })
7123
+ option("specialChars", /[\u0000-\u001f\u007f\u00ad\u061c\u200b-\u200f\u2028\u2029\ufeff]/g, function (cm, val, old) {
7124
+ cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g")
7125
+ if (old != Init) { cm.refresh() }
7126
+ })
7127
+ option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function (cm) { return cm.refresh(); }, true)
7128
+ option("electricChars", true)
7129
+ option("inputStyle", mobile ? "contenteditable" : "textarea", function () {
7130
+ throw new Error("inputStyle can not (yet) be changed in a running editor") // FIXME
7131
+ }, true)
7132
+ option("spellcheck", false, function (cm, val) { return cm.getInputField().spellcheck = val; }, true)
7133
+ option("rtlMoveVisually", !windows)
7134
+ option("wholeLineUpdateBefore", true)
7135
+
7136
+ option("theme", "default", function (cm) {
7137
+ themeChanged(cm)
7138
+ guttersChanged(cm)
7139
+ }, true)
7140
+ option("keyMap", "default", function (cm, val, old) {
7141
+ var next = getKeyMap(val)
7142
+ var prev = old != Init && getKeyMap(old)
7143
+ if (prev && prev.detach) { prev.detach(cm, next) }
7144
+ if (next.attach) { next.attach(cm, prev || null) }
7145
+ })
7146
+ option("extraKeys", null)
7147
+
7148
+ option("lineWrapping", false, wrappingChanged, true)
7149
+ option("gutters", [], function (cm) {
7150
+ setGuttersForLineNumbers(cm.options)
7151
+ guttersChanged(cm)
7152
+ }, true)
7153
+ option("fixedGutter", true, function (cm, val) {
7154
+ cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0"
7155
+ cm.refresh()
7156
+ }, true)
7157
+ option("coverGutterNextToScrollbar", false, function (cm) { return updateScrollbars(cm); }, true)
7158
+ option("scrollbarStyle", "native", function (cm) {
7159
+ initScrollbars(cm)
7160
+ updateScrollbars(cm)
7161
+ cm.display.scrollbars.setScrollTop(cm.doc.scrollTop)
7162
+ cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft)
7163
+ }, true)
7164
+ option("lineNumbers", false, function (cm) {
7165
+ setGuttersForLineNumbers(cm.options)
7166
+ guttersChanged(cm)
7167
+ }, true)
7168
+ option("firstLineNumber", 1, guttersChanged, true)
7169
+ option("lineNumberFormatter", function (integer) { return integer; }, guttersChanged, true)
7170
+ option("showCursorWhenSelecting", false, updateSelection, true)
7171
+
7172
+ option("resetSelectionOnContextMenu", true)
7173
+ option("lineWiseCopyCut", true)
7174
+
7175
+ option("readOnly", false, function (cm, val) {
7176
+ if (val == "nocursor") {
7177
+ onBlur(cm)
7178
+ cm.display.input.blur()
7179
+ cm.display.disabled = true
7180
+ } else {
7181
+ cm.display.disabled = false
7182
+ }
7183
+ cm.display.input.readOnlyChanged(val)
7184
+ })
7185
+ option("disableInput", false, function (cm, val) {if (!val) { cm.display.input.reset() }}, true)
7186
+ option("dragDrop", true, dragDropChanged)
7187
+ option("allowDropFileTypes", null)
7188
+
7189
+ option("cursorBlinkRate", 530)
7190
+ option("cursorScrollMargin", 0)
7191
+ option("cursorHeight", 1, updateSelection, true)
7192
+ option("singleCursorHeightPerLine", true, updateSelection, true)
7193
+ option("workTime", 100)
7194
+ option("workDelay", 100)
7195
+ option("flattenSpans", true, resetModeState, true)
7196
+ option("addModeClass", false, resetModeState, true)
7197
+ option("pollInterval", 100)
7198
+ option("undoDepth", 200, function (cm, val) { return cm.doc.history.undoDepth = val; })
7199
+ option("historyEventDelay", 1250)
7200
+ option("viewportMargin", 10, function (cm) { return cm.refresh(); }, true)
7201
+ option("maxHighlightLength", 10000, resetModeState, true)
7202
+ option("moveInputWithCursor", true, function (cm, val) {
7203
+ if (!val) { cm.display.input.resetPosition() }
7204
+ })
7205
+
7206
+ option("tabindex", null, function (cm, val) { return cm.display.input.getField().tabIndex = val || ""; })
7207
+ option("autofocus", null)
7208
+ }
7209
+
7210
+ function guttersChanged(cm) {
7211
+ updateGutters(cm)
7212
+ regChange(cm)
7213
+ alignHorizontally(cm)
7214
+ }
7215
+
7216
+ function dragDropChanged(cm, value, old) {
7217
+ var wasOn = old && old != Init
7218
+ if (!value != !wasOn) {
7219
+ var funcs = cm.display.dragFunctions
7220
+ var toggle = value ? on : off
7221
+ toggle(cm.display.scroller, "dragstart", funcs.start)
7222
+ toggle(cm.display.scroller, "dragenter", funcs.enter)
7223
+ toggle(cm.display.scroller, "dragover", funcs.over)
7224
+ toggle(cm.display.scroller, "dragleave", funcs.leave)
7225
+ toggle(cm.display.scroller, "drop", funcs.drop)
7226
+ }
7227
+ }
7228
+
7229
+ function wrappingChanged(cm) {
7230
+ if (cm.options.lineWrapping) {
7231
+ addClass(cm.display.wrapper, "CodeMirror-wrap")
7232
+ cm.display.sizer.style.minWidth = ""
7233
+ cm.display.sizerWidth = null
7234
+ } else {
7235
+ rmClass(cm.display.wrapper, "CodeMirror-wrap")
7236
+ findMaxLine(cm)
7237
+ }
7238
+ estimateLineHeights(cm)
7239
+ regChange(cm)
7240
+ clearCaches(cm)
7241
+ setTimeout(function () { return updateScrollbars(cm); }, 100)
7242
+ }
7243
+
7244
+ // A CodeMirror instance represents an editor. This is the object
7245
+ // that user code is usually dealing with.
7246
+
7247
+ function CodeMirror$1(place, options) {
7248
+ var this$1 = this;
7249
+
7250
+ if (!(this instanceof CodeMirror$1)) { return new CodeMirror$1(place, options) }
7251
+
7252
+ this.options = options = options ? copyObj(options) : {}
7253
+ // Determine effective options based on given values and defaults.
7254
+ copyObj(defaults, options, false)
7255
+ setGuttersForLineNumbers(options)
7256
+
7257
+ var doc = options.value
7258
+ if (typeof doc == "string") { doc = new Doc(doc, options.mode, null, options.lineSeparator) }
7259
+ this.doc = doc
7260
+
7261
+ var input = new CodeMirror$1.inputStyles[options.inputStyle](this)
7262
+ var display = this.display = new Display(place, doc, input)
7263
+ display.wrapper.CodeMirror = this
7264
+ updateGutters(this)
7265
+ themeChanged(this)
7266
+ if (options.lineWrapping)
7267
+ { this.display.wrapper.className += " CodeMirror-wrap" }
7268
+ initScrollbars(this)
7269
+
7270
+ this.state = {
7271
+ keyMaps: [], // stores maps added by addKeyMap
7272
+ overlays: [], // highlighting overlays, as added by addOverlay
7273
+ modeGen: 0, // bumped when mode/overlay changes, used to invalidate highlighting info
7274
+ overwrite: false,
7275
+ delayingBlurEvent: false,
7276
+ focused: false,
7277
+ suppressEdits: false, // used to disable editing during key handlers when in readOnly mode
7278
+ pasteIncoming: false, cutIncoming: false, // help recognize paste/cut edits in input.poll
7279
+ selectingText: false,
7280
+ draggingText: false,
7281
+ highlight: new Delayed(), // stores highlight worker timeout
7282
+ keySeq: null, // Unfinished key sequence
7283
+ specialChars: null
7284
+ }
7285
+
7286
+ if (options.autofocus && !mobile) { display.input.focus() }
7287
+
7288
+ // Override magic textarea content restore that IE sometimes does
7289
+ // on our hidden textarea on reload
7290
+ if (ie && ie_version < 11) { setTimeout(function () { return this$1.display.input.reset(true); }, 20) }
7291
+
7292
+ registerEventHandlers(this)
7293
+ ensureGlobalHandlers()
7294
+
7295
+ startOperation(this)
7296
+ this.curOp.forceUpdate = true
7297
+ attachDoc(this, doc)
7298
+
7299
+ if ((options.autofocus && !mobile) || this.hasFocus())
7300
+ { setTimeout(bind(onFocus, this), 20) }
7301
+ else
7302
+ { onBlur(this) }
7303
+
7304
+ for (var opt in optionHandlers) { if (optionHandlers.hasOwnProperty(opt))
7305
+ { optionHandlers[opt](this$1, options[opt], Init) } }
7306
+ maybeUpdateLineNumberWidth(this)
7307
+ if (options.finishInit) { options.finishInit(this) }
7308
+ for (var i = 0; i < initHooks.length; ++i) { initHooks[i](this$1) }
7309
+ endOperation(this)
7310
+ // Suppress optimizelegibility in Webkit, since it breaks text
7311
+ // measuring on line wrapping boundaries.
7312
+ if (webkit && options.lineWrapping &&
7313
+ getComputedStyle(display.lineDiv).textRendering == "optimizelegibility")
7314
+ { display.lineDiv.style.textRendering = "auto" }
7315
+ }
7316
+
7317
+ // The default configuration options.
7318
+ CodeMirror$1.defaults = defaults
7319
+ // Functions to run when options are changed.
7320
+ CodeMirror$1.optionHandlers = optionHandlers
7321
+
7322
+ // Attach the necessary event handlers when initializing the editor
7323
+ function registerEventHandlers(cm) {
7324
+ var d = cm.display
7325
+ on(d.scroller, "mousedown", operation(cm, onMouseDown))
7326
+ // Older IE's will not fire a second mousedown for a double click
7327
+ if (ie && ie_version < 11)
7328
+ { on(d.scroller, "dblclick", operation(cm, function (e) {
7329
+ if (signalDOMEvent(cm, e)) { return }
7330
+ var pos = posFromMouse(cm, e)
7331
+ if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) { return }
7332
+ e_preventDefault(e)
7333
+ var word = cm.findWordAt(pos)
7334
+ extendSelection(cm.doc, word.anchor, word.head)
7335
+ })) }
7336
+ else
7337
+ { on(d.scroller, "dblclick", function (e) { return signalDOMEvent(cm, e) || e_preventDefault(e); }) }
7338
+ // Some browsers fire contextmenu *after* opening the menu, at
7339
+ // which point we can't mess with it anymore. Context menu is
7340
+ // handled in onMouseDown for these browsers.
7341
+ if (!captureRightClick) { on(d.scroller, "contextmenu", function (e) { return onContextMenu$1(cm, e); }) }
7342
+
7343
+ // Used to suppress mouse event handling when a touch happens
7344
+ var touchFinished, prevTouch = {end: 0}
7345
+ function finishTouch() {
7346
+ if (d.activeTouch) {
7347
+ touchFinished = setTimeout(function () { return d.activeTouch = null; }, 1000)
7348
+ prevTouch = d.activeTouch
7349
+ prevTouch.end = +new Date
7350
+ }
7351
+ }
7352
+ function isMouseLikeTouchEvent(e) {
7353
+ if (e.touches.length != 1) { return false }
7354
+ var touch = e.touches[0]
7355
+ return touch.radiusX <= 1 && touch.radiusY <= 1
7356
+ }
7357
+ function farAway(touch, other) {
7358
+ if (other.left == null) { return true }
7359
+ var dx = other.left - touch.left, dy = other.top - touch.top
7360
+ return dx * dx + dy * dy > 20 * 20
7361
+ }
7362
+ on(d.scroller, "touchstart", function (e) {
7363
+ if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e)) {
7364
+ d.input.ensurePolled()
7365
+ clearTimeout(touchFinished)
7366
+ var now = +new Date
7367
+ d.activeTouch = {start: now, moved: false,
7368
+ prev: now - prevTouch.end <= 300 ? prevTouch : null}
7369
+ if (e.touches.length == 1) {
7370
+ d.activeTouch.left = e.touches[0].pageX
7371
+ d.activeTouch.top = e.touches[0].pageY
7372
+ }
7373
+ }
7374
+ })
7375
+ on(d.scroller, "touchmove", function () {
7376
+ if (d.activeTouch) { d.activeTouch.moved = true }
7377
+ })
7378
+ on(d.scroller, "touchend", function (e) {
7379
+ var touch = d.activeTouch
7380
+ if (touch && !eventInWidget(d, e) && touch.left != null &&
7381
+ !touch.moved && new Date - touch.start < 300) {
7382
+ var pos = cm.coordsChar(d.activeTouch, "page"), range
7383
+ if (!touch.prev || farAway(touch, touch.prev)) // Single tap
7384
+ { range = new Range(pos, pos) }
7385
+ else if (!touch.prev.prev || farAway(touch, touch.prev.prev)) // Double tap
7386
+ { range = cm.findWordAt(pos) }
7387
+ else // Triple tap
7388
+ { range = new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))) }
7389
+ cm.setSelection(range.anchor, range.head)
7390
+ cm.focus()
7391
+ e_preventDefault(e)
7392
+ }
7393
+ finishTouch()
7394
+ })
7395
+ on(d.scroller, "touchcancel", finishTouch)
7396
+
7397
+ // Sync scrolling between fake scrollbars and real scrollable
7398
+ // area, ensure viewport is updated when scrolling.
7399
+ on(d.scroller, "scroll", function () {
7400
+ if (d.scroller.clientHeight) {
7401
+ setScrollTop(cm, d.scroller.scrollTop)
7402
+ setScrollLeft(cm, d.scroller.scrollLeft, true)
7403
+ signal(cm, "scroll", cm)
7404
+ }
7405
+ })
7406
+
7407
+ // Listen to wheel events in order to try and update the viewport on time.
7408
+ on(d.scroller, "mousewheel", function (e) { return onScrollWheel(cm, e); })
7409
+ on(d.scroller, "DOMMouseScroll", function (e) { return onScrollWheel(cm, e); })
7410
+
7411
+ // Prevent wrapper from ever scrolling
7412
+ on(d.wrapper, "scroll", function () { return d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; })
7413
+
7414
+ d.dragFunctions = {
7415
+ enter: function (e) {if (!signalDOMEvent(cm, e)) { e_stop(e) }},
7416
+ over: function (e) {if (!signalDOMEvent(cm, e)) { onDragOver(cm, e); e_stop(e) }},
7417
+ start: function (e) { return onDragStart(cm, e); },
7418
+ drop: operation(cm, onDrop),
7419
+ leave: function (e) {if (!signalDOMEvent(cm, e)) { clearDragCursor(cm) }}
7420
+ }
7421
+
7422
+ var inp = d.input.getField()
7423
+ on(inp, "keyup", function (e) { return onKeyUp.call(cm, e); })
7424
+ on(inp, "keydown", operation(cm, onKeyDown))
7425
+ on(inp, "keypress", operation(cm, onKeyPress$1))
7426
+ on(inp, "focus", function (e) { return onFocus(cm, e); })
7427
+ on(inp, "blur", function (e) { return onBlur(cm, e); })
7428
+ }
7429
+
7430
+ var initHooks = []
7431
+ CodeMirror$1.defineInitHook = function (f) { return initHooks.push(f); }
7432
+
7433
+ // Indent the given line. The how parameter can be "smart",
7434
+ // "add"/null, "subtract", or "prev". When aggressive is false
7435
+ // (typically set to true for forced single-line indents), empty
7436
+ // lines are not indented, and places where the mode returns Pass
7437
+ // are left alone.
7438
+ function indentLine(cm, n, how, aggressive) {
7439
+ var doc = cm.doc, state
7440
+ if (how == null) { how = "add" }
7441
+ if (how == "smart") {
7442
+ // Fall back to "prev" when the mode doesn't have an indentation
7443
+ // method.
7444
+ if (!doc.mode.indent) { how = "prev" }
7445
+ else { state = getStateBefore(cm, n) }
7446
+ }
7447
+
7448
+ var tabSize = cm.options.tabSize
7449
+ var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize)
7450
+ if (line.stateAfter) { line.stateAfter = null }
7451
+ var curSpaceString = line.text.match(/^\s*/)[0], indentation
7452
+ if (!aggressive && !/\S/.test(line.text)) {
7453
+ indentation = 0
7454
+ how = "not"
7455
+ } else if (how == "smart") {
7456
+ indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text)
7457
+ if (indentation == Pass || indentation > 150) {
7458
+ if (!aggressive) { return }
7459
+ how = "prev"
7460
+ }
7461
+ }
7462
+ if (how == "prev") {
7463
+ if (n > doc.first) { indentation = countColumn(getLine(doc, n-1).text, null, tabSize) }
7464
+ else { indentation = 0 }
7465
+ } else if (how == "add") {
7466
+ indentation = curSpace + cm.options.indentUnit
7467
+ } else if (how == "subtract") {
7468
+ indentation = curSpace - cm.options.indentUnit
7469
+ } else if (typeof how == "number") {
7470
+ indentation = curSpace + how
7471
+ }
7472
+ indentation = Math.max(0, indentation)
7473
+
7474
+ var indentString = "", pos = 0
7475
+ if (cm.options.indentWithTabs)
7476
+ { for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t"} }
7477
+ if (pos < indentation) { indentString += spaceStr(indentation - pos) }
7478
+
7479
+ if (indentString != curSpaceString) {
7480
+ replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input")
7481
+ line.stateAfter = null
7482
+ return true
7483
+ } else {
7484
+ // Ensure that, if the cursor was in the whitespace at the start
7485
+ // of the line, it is moved to the end of that space.
7486
+ for (var i$1 = 0; i$1 < doc.sel.ranges.length; i$1++) {
7487
+ var range = doc.sel.ranges[i$1]
7488
+ if (range.head.line == n && range.head.ch < curSpaceString.length) {
7489
+ var pos$1 = Pos(n, curSpaceString.length)
7490
+ replaceOneSelection(doc, i$1, new Range(pos$1, pos$1))
7491
+ break
7492
+ }
7493
+ }
7494
+ }
7495
+ }
7496
+
7497
+ // This will be set to a {lineWise: bool, text: [string]} object, so
7498
+ // that, when pasting, we know what kind of selections the copied
7499
+ // text was made out of.
7500
+ var lastCopied = null
7501
+
7502
+ function setLastCopied(newLastCopied) {
7503
+ lastCopied = newLastCopied
7504
+ }
7505
+
7506
+ function applyTextInput(cm, inserted, deleted, sel, origin) {
7507
+ var doc = cm.doc
7508
+ cm.display.shift = false
7509
+ if (!sel) { sel = doc.sel }
7510
+
7511
+ var paste = cm.state.pasteIncoming || origin == "paste"
7512
+ var textLines = splitLinesAuto(inserted), multiPaste = null
7513
+ // When pasing N lines into N selections, insert one line per selection
7514
+ if (paste && sel.ranges.length > 1) {
7515
+ if (lastCopied && lastCopied.text.join("\n") == inserted) {
7516
+ if (sel.ranges.length % lastCopied.text.length == 0) {
7517
+ multiPaste = []
7518
+ for (var i = 0; i < lastCopied.text.length; i++)
7519
+ { multiPaste.push(doc.splitLines(lastCopied.text[i])) }
7520
+ }
7521
+ } else if (textLines.length == sel.ranges.length) {
7522
+ multiPaste = map(textLines, function (l) { return [l]; })
7523
+ }
7524
+ }
7525
+
7526
+ var updateInput
7527
+ // Normal behavior is to insert the new text into every selection
7528
+ for (var i$1 = sel.ranges.length - 1; i$1 >= 0; i$1--) {
7529
+ var range$$1 = sel.ranges[i$1]
7530
+ var from = range$$1.from(), to = range$$1.to()
7531
+ if (range$$1.empty()) {
7532
+ if (deleted && deleted > 0) // Handle deletion
7533
+ { from = Pos(from.line, from.ch - deleted) }
7534
+ else if (cm.state.overwrite && !paste) // Handle overwrite
7535
+ { to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length)) }
7536
+ else if (lastCopied && lastCopied.lineWise && lastCopied.text.join("\n") == inserted)
7537
+ { from = to = Pos(from.line, 0) }
7538
+ }
7539
+ updateInput = cm.curOp.updateInput
7540
+ var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i$1 % multiPaste.length] : textLines,
7541
+ origin: origin || (paste ? "paste" : cm.state.cutIncoming ? "cut" : "+input")}
7542
+ makeChange(cm.doc, changeEvent)
7543
+ signalLater(cm, "inputRead", cm, changeEvent)
7544
+ }
7545
+ if (inserted && !paste)
7546
+ { triggerElectric(cm, inserted) }
7547
+
7548
+ ensureCursorVisible(cm)
7549
+ cm.curOp.updateInput = updateInput
7550
+ cm.curOp.typing = true
7551
+ cm.state.pasteIncoming = cm.state.cutIncoming = false
7552
+ }
7553
+
7554
+ function handlePaste(e, cm) {
7555
+ var pasted = e.clipboardData && e.clipboardData.getData("Text")
7556
+ if (pasted) {
7557
+ e.preventDefault()
7558
+ if (!cm.isReadOnly() && !cm.options.disableInput)
7559
+ { runInOp(cm, function () { return applyTextInput(cm, pasted, 0, null, "paste"); }) }
7560
+ return true
7561
+ }
7562
+ }
7563
+
7564
+ function triggerElectric(cm, inserted) {
7565
+ // When an 'electric' character is inserted, immediately trigger a reindent
7566
+ if (!cm.options.electricChars || !cm.options.smartIndent) { return }
7567
+ var sel = cm.doc.sel
7568
+
7569
+ for (var i = sel.ranges.length - 1; i >= 0; i--) {
7570
+ var range$$1 = sel.ranges[i]
7571
+ if (range$$1.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range$$1.head.line)) { continue }
7572
+ var mode = cm.getModeAt(range$$1.head)
7573
+ var indented = false
7574
+ if (mode.electricChars) {
7575
+ for (var j = 0; j < mode.electricChars.length; j++)
7576
+ { if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) {
7577
+ indented = indentLine(cm, range$$1.head.line, "smart")
7578
+ break
7579
+ } }
7580
+ } else if (mode.electricInput) {
7581
+ if (mode.electricInput.test(getLine(cm.doc, range$$1.head.line).text.slice(0, range$$1.head.ch)))
7582
+ { indented = indentLine(cm, range$$1.head.line, "smart") }
7583
+ }
7584
+ if (indented) { signalLater(cm, "electricInput", cm, range$$1.head.line) }
7585
+ }
7586
+ }
7587
+
7588
+ function copyableRanges(cm) {
7589
+ var text = [], ranges = []
7590
+ for (var i = 0; i < cm.doc.sel.ranges.length; i++) {
7591
+ var line = cm.doc.sel.ranges[i].head.line
7592
+ var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)}
7593
+ ranges.push(lineRange)
7594
+ text.push(cm.getRange(lineRange.anchor, lineRange.head))
7595
+ }
7596
+ return {text: text, ranges: ranges}
7597
+ }
7598
+
7599
+ function disableBrowserMagic(field, spellcheck) {
7600
+ field.setAttribute("autocorrect", "off")
7601
+ field.setAttribute("autocapitalize", "off")
7602
+ field.setAttribute("spellcheck", !!spellcheck)
7603
+ }
7604
+
7605
+ function hiddenTextarea() {
7606
+ var te = elt("textarea", null, null, "position: absolute; bottom: -1em; padding: 0; width: 1px; height: 1em; outline: none")
7607
+ var div = elt("div", [te], null, "overflow: hidden; position: relative; width: 3px; height: 0px;")
7608
+ // The textarea is kept positioned near the cursor to prevent the
7609
+ // fact that it'll be scrolled into view on input from scrolling
7610
+ // our fake cursor out of view. On webkit, when wrap=off, paste is
7611
+ // very slow. So make the area wide instead.
7612
+ if (webkit) { te.style.width = "1000px" }
7613
+ else { te.setAttribute("wrap", "off") }
7614
+ // If border: 0; -- iOS fails to open keyboard (issue #1287)
7615
+ if (ios) { te.style.border = "1px solid black" }
7616
+ disableBrowserMagic(te)
7617
+ return div
7618
+ }
7619
+
7620
+ // The publicly visible API. Note that methodOp(f) means
7621
+ // 'wrap f in an operation, performed on its `this` parameter'.
7622
+
7623
+ // This is not the complete set of editor methods. Most of the
7624
+ // methods defined on the Doc type are also injected into
7625
+ // CodeMirror.prototype, for backwards compatibility and
7626
+ // convenience.
7627
+
7628
+ var addEditorMethods = function(CodeMirror) {
7629
+ var optionHandlers = CodeMirror.optionHandlers
7630
+
7631
+ var helpers = CodeMirror.helpers = {}
7632
+
7633
+ CodeMirror.prototype = {
7634
+ constructor: CodeMirror,
7635
+ focus: function(){window.focus(); this.display.input.focus()},
7636
+
7637
+ setOption: function(option, value) {
7638
+ var options = this.options, old = options[option]
7639
+ if (options[option] == value && option != "mode") { return }
7640
+ options[option] = value
7641
+ if (optionHandlers.hasOwnProperty(option))
7642
+ { operation(this, optionHandlers[option])(this, value, old) }
7643
+ signal(this, "optionChange", this, option)
7644
+ },
7645
+
7646
+ getOption: function(option) {return this.options[option]},
7647
+ getDoc: function() {return this.doc},
7648
+
7649
+ addKeyMap: function(map$$1, bottom) {
7650
+ this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map$$1))
7651
+ },
7652
+ removeKeyMap: function(map$$1) {
7653
+ var maps = this.state.keyMaps
7654
+ for (var i = 0; i < maps.length; ++i)
7655
+ { if (maps[i] == map$$1 || maps[i].name == map$$1) {
7656
+ maps.splice(i, 1)
7657
+ return true
7658
+ } }
7659
+ },
7660
+
7661
+ addOverlay: methodOp(function(spec, options) {
7662
+ var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec)
7663
+ if (mode.startState) { throw new Error("Overlays may not be stateful.") }
7664
+ insertSorted(this.state.overlays,
7665
+ {mode: mode, modeSpec: spec, opaque: options && options.opaque,
7666
+ priority: (options && options.priority) || 0},
7667
+ function (overlay) { return overlay.priority; })
7668
+ this.state.modeGen++
7669
+ regChange(this)
7670
+ }),
7671
+ removeOverlay: methodOp(function(spec) {
7672
+ var this$1 = this;
7673
+
7674
+ var overlays = this.state.overlays
7675
+ for (var i = 0; i < overlays.length; ++i) {
7676
+ var cur = overlays[i].modeSpec
7677
+ if (cur == spec || typeof spec == "string" && cur.name == spec) {
7678
+ overlays.splice(i, 1)
7679
+ this$1.state.modeGen++
7680
+ regChange(this$1)
7681
+ return
7682
+ }
7683
+ }
7684
+ }),
7685
+
7686
+ indentLine: methodOp(function(n, dir, aggressive) {
7687
+ if (typeof dir != "string" && typeof dir != "number") {
7688
+ if (dir == null) { dir = this.options.smartIndent ? "smart" : "prev" }
7689
+ else { dir = dir ? "add" : "subtract" }
7690
+ }
7691
+ if (isLine(this.doc, n)) { indentLine(this, n, dir, aggressive) }
7692
+ }),
7693
+ indentSelection: methodOp(function(how) {
7694
+ var this$1 = this;
7695
+
7696
+ var ranges = this.doc.sel.ranges, end = -1
7697
+ for (var i = 0; i < ranges.length; i++) {
7698
+ var range$$1 = ranges[i]
7699
+ if (!range$$1.empty()) {
7700
+ var from = range$$1.from(), to = range$$1.to()
7701
+ var start = Math.max(end, from.line)
7702
+ end = Math.min(this$1.lastLine(), to.line - (to.ch ? 0 : 1)) + 1
7703
+ for (var j = start; j < end; ++j)
7704
+ { indentLine(this$1, j, how) }
7705
+ var newRanges = this$1.doc.sel.ranges
7706
+ if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].from().ch > 0)
7707
+ { replaceOneSelection(this$1.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll) }
7708
+ } else if (range$$1.head.line > end) {
7709
+ indentLine(this$1, range$$1.head.line, how, true)
7710
+ end = range$$1.head.line
7711
+ if (i == this$1.doc.sel.primIndex) { ensureCursorVisible(this$1) }
7712
+ }
7713
+ }
7714
+ }),
7715
+
7716
+ // Fetch the parser token for a given character. Useful for hacks
7717
+ // that want to inspect the mode state (say, for completion).
7718
+ getTokenAt: function(pos, precise) {
7719
+ return takeToken(this, pos, precise)
7720
+ },
7721
+
7722
+ getLineTokens: function(line, precise) {
7723
+ return takeToken(this, Pos(line), precise, true)
7724
+ },
7725
+
7726
+ getTokenTypeAt: function(pos) {
7727
+ pos = clipPos(this.doc, pos)
7728
+ var styles = getLineStyles(this, getLine(this.doc, pos.line))
7729
+ var before = 0, after = (styles.length - 1) / 2, ch = pos.ch
7730
+ var type
7731
+ if (ch == 0) { type = styles[2] }
7732
+ else { for (;;) {
7733
+ var mid = (before + after) >> 1
7734
+ if ((mid ? styles[mid * 2 - 1] : 0) >= ch) { after = mid }
7735
+ else if (styles[mid * 2 + 1] < ch) { before = mid + 1 }
7736
+ else { type = styles[mid * 2 + 2]; break }
7737
+ } }
7738
+ var cut = type ? type.indexOf("overlay ") : -1
7739
+ return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1)
7740
+ },
7741
+
7742
+ getModeAt: function(pos) {
7743
+ var mode = this.doc.mode
7744
+ if (!mode.innerMode) { return mode }
7745
+ return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode
7746
+ },
7747
+
7748
+ getHelper: function(pos, type) {
7749
+ return this.getHelpers(pos, type)[0]
7750
+ },
7751
+
7752
+ getHelpers: function(pos, type) {
7753
+ var this$1 = this;
7754
+
7755
+ var found = []
7756
+ if (!helpers.hasOwnProperty(type)) { return found }
7757
+ var help = helpers[type], mode = this.getModeAt(pos)
7758
+ if (typeof mode[type] == "string") {
7759
+ if (help[mode[type]]) { found.push(help[mode[type]]) }
7760
+ } else if (mode[type]) {
7761
+ for (var i = 0; i < mode[type].length; i++) {
7762
+ var val = help[mode[type][i]]
7763
+ if (val) { found.push(val) }
7764
+ }
7765
+ } else if (mode.helperType && help[mode.helperType]) {
7766
+ found.push(help[mode.helperType])
7767
+ } else if (help[mode.name]) {
7768
+ found.push(help[mode.name])
7769
+ }
7770
+ for (var i$1 = 0; i$1 < help._global.length; i$1++) {
7771
+ var cur = help._global[i$1]
7772
+ if (cur.pred(mode, this$1) && indexOf(found, cur.val) == -1)
7773
+ { found.push(cur.val) }
7774
+ }
7775
+ return found
7776
+ },
7777
+
7778
+ getStateAfter: function(line, precise) {
7779
+ var doc = this.doc
7780
+ line = clipLine(doc, line == null ? doc.first + doc.size - 1: line)
7781
+ return getStateBefore(this, line + 1, precise)
7782
+ },
7783
+
7784
+ cursorCoords: function(start, mode) {
7785
+ var pos, range$$1 = this.doc.sel.primary()
7786
+ if (start == null) { pos = range$$1.head }
7787
+ else if (typeof start == "object") { pos = clipPos(this.doc, start) }
7788
+ else { pos = start ? range$$1.from() : range$$1.to() }
7789
+ return cursorCoords(this, pos, mode || "page")
7790
+ },
7791
+
7792
+ charCoords: function(pos, mode) {
7793
+ return charCoords(this, clipPos(this.doc, pos), mode || "page")
7794
+ },
7795
+
7796
+ coordsChar: function(coords, mode) {
7797
+ coords = fromCoordSystem(this, coords, mode || "page")
7798
+ return coordsChar(this, coords.left, coords.top)
7799
+ },
7800
+
7801
+ lineAtHeight: function(height, mode) {
7802
+ height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top
7803
+ return lineAtHeight(this.doc, height + this.display.viewOffset)
7804
+ },
7805
+ heightAtLine: function(line, mode, includeWidgets) {
7806
+ var end = false, lineObj
7807
+ if (typeof line == "number") {
7808
+ var last = this.doc.first + this.doc.size - 1
7809
+ if (line < this.doc.first) { line = this.doc.first }
7810
+ else if (line > last) { line = last; end = true }
7811
+ lineObj = getLine(this.doc, line)
7812
+ } else {
7813
+ lineObj = line
7814
+ }
7815
+ return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || "page", includeWidgets).top +
7816
+ (end ? this.doc.height - heightAtLine(lineObj) : 0)
7817
+ },
7818
+
7819
+ defaultTextHeight: function() { return textHeight(this.display) },
7820
+ defaultCharWidth: function() { return charWidth(this.display) },
7821
+
7822
+ getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo}},
7823
+
7824
+ addWidget: function(pos, node, scroll, vert, horiz) {
7825
+ var display = this.display
7826
+ pos = cursorCoords(this, clipPos(this.doc, pos))
7827
+ var top = pos.bottom, left = pos.left
7828
+ node.style.position = "absolute"
7829
+ node.setAttribute("cm-ignore-events", "true")
7830
+ this.display.input.setUneditable(node)
7831
+ display.sizer.appendChild(node)
7832
+ if (vert == "over") {
7833
+ top = pos.top
7834
+ } else if (vert == "above" || vert == "near") {
7835
+ var vspace = Math.max(display.wrapper.clientHeight, this.doc.height),
7836
+ hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth)
7837
+ // Default to positioning above (if specified and possible); otherwise default to positioning below
7838
+ if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight)
7839
+ { top = pos.top - node.offsetHeight }
7840
+ else if (pos.bottom + node.offsetHeight <= vspace)
7841
+ { top = pos.bottom }
7842
+ if (left + node.offsetWidth > hspace)
7843
+ { left = hspace - node.offsetWidth }
7844
+ }
7845
+ node.style.top = top + "px"
7846
+ node.style.left = node.style.right = ""
7847
+ if (horiz == "right") {
7848
+ left = display.sizer.clientWidth - node.offsetWidth
7849
+ node.style.right = "0px"
7850
+ } else {
7851
+ if (horiz == "left") { left = 0 }
7852
+ else if (horiz == "middle") { left = (display.sizer.clientWidth - node.offsetWidth) / 2 }
7853
+ node.style.left = left + "px"
7854
+ }
7855
+ if (scroll)
7856
+ { scrollIntoView(this, left, top, left + node.offsetWidth, top + node.offsetHeight) }
7857
+ },
7858
+
7859
+ triggerOnKeyDown: methodOp(onKeyDown),
7860
+ triggerOnKeyPress: methodOp(onKeyPress$1),
7861
+ triggerOnKeyUp: onKeyUp,
7862
+
7863
+ execCommand: function(cmd) {
7864
+ if (commands.hasOwnProperty(cmd))
7865
+ { return commands[cmd].call(null, this) }
7866
+ },
7867
+
7868
+ triggerElectric: methodOp(function(text) { triggerElectric(this, text) }),
7869
+
7870
+ findPosH: function(from, amount, unit, visually) {
7871
+ var this$1 = this;
7872
+
7873
+ var dir = 1
7874
+ if (amount < 0) { dir = -1; amount = -amount }
7875
+ var cur = clipPos(this.doc, from)
7876
+ for (var i = 0; i < amount; ++i) {
7877
+ cur = findPosH(this$1.doc, cur, dir, unit, visually)
7878
+ if (cur.hitSide) { break }
7879
+ }
7880
+ return cur
7881
+ },
7882
+
7883
+ moveH: methodOp(function(dir, unit) {
7884
+ var this$1 = this;
7885
+
7886
+ this.extendSelectionsBy(function (range$$1) {
7887
+ if (this$1.display.shift || this$1.doc.extend || range$$1.empty())
7888
+ { return findPosH(this$1.doc, range$$1.head, dir, unit, this$1.options.rtlMoveVisually) }
7889
+ else
7890
+ { return dir < 0 ? range$$1.from() : range$$1.to() }
7891
+ }, sel_move)
7892
+ }),
7893
+
7894
+ deleteH: methodOp(function(dir, unit) {
7895
+ var sel = this.doc.sel, doc = this.doc
7896
+ if (sel.somethingSelected())
7897
+ { doc.replaceSelection("", null, "+delete") }
7898
+ else
7899
+ { deleteNearSelection(this, function (range$$1) {
7900
+ var other = findPosH(doc, range$$1.head, dir, unit, false)
7901
+ return dir < 0 ? {from: other, to: range$$1.head} : {from: range$$1.head, to: other}
7902
+ }) }
7903
+ }),
7904
+
7905
+ findPosV: function(from, amount, unit, goalColumn) {
7906
+ var this$1 = this;
7907
+
7908
+ var dir = 1, x = goalColumn
7909
+ if (amount < 0) { dir = -1; amount = -amount }
7910
+ var cur = clipPos(this.doc, from)
7911
+ for (var i = 0; i < amount; ++i) {
7912
+ var coords = cursorCoords(this$1, cur, "div")
7913
+ if (x == null) { x = coords.left }
7914
+ else { coords.left = x }
7915
+ cur = findPosV(this$1, coords, dir, unit)
7916
+ if (cur.hitSide) { break }
7917
+ }
7918
+ return cur
7919
+ },
7920
+
7921
+ moveV: methodOp(function(dir, unit) {
7922
+ var this$1 = this;
7923
+
7924
+ var doc = this.doc, goals = []
7925
+ var collapse = !this.display.shift && !doc.extend && doc.sel.somethingSelected()
7926
+ doc.extendSelectionsBy(function (range$$1) {
7927
+ if (collapse)
7928
+ { return dir < 0 ? range$$1.from() : range$$1.to() }
7929
+ var headPos = cursorCoords(this$1, range$$1.head, "div")
7930
+ if (range$$1.goalColumn != null) { headPos.left = range$$1.goalColumn }
7931
+ goals.push(headPos.left)
7932
+ var pos = findPosV(this$1, headPos, dir, unit)
7933
+ if (unit == "page" && range$$1 == doc.sel.primary())
7934
+ { addToScrollPos(this$1, null, charCoords(this$1, pos, "div").top - headPos.top) }
7935
+ return pos
7936
+ }, sel_move)
7937
+ if (goals.length) { for (var i = 0; i < doc.sel.ranges.length; i++)
7938
+ { doc.sel.ranges[i].goalColumn = goals[i] } }
7939
+ }),
7940
+
7941
+ // Find the word at the given position (as returned by coordsChar).
7942
+ findWordAt: function(pos) {
7943
+ var doc = this.doc, line = getLine(doc, pos.line).text
7944
+ var start = pos.ch, end = pos.ch
7945
+ if (line) {
7946
+ var helper = this.getHelper(pos, "wordChars")
7947
+ if ((pos.xRel < 0 || end == line.length) && start) { --start; } else { ++end }
7948
+ var startChar = line.charAt(start)
7949
+ var check = isWordChar(startChar, helper)
7950
+ ? function (ch) { return isWordChar(ch, helper); }
7951
+ : /\s/.test(startChar) ? function (ch) { return /\s/.test(ch); }
7952
+ : function (ch) { return (!/\s/.test(ch) && !isWordChar(ch)); }
7953
+ while (start > 0 && check(line.charAt(start - 1))) { --start }
7954
+ while (end < line.length && check(line.charAt(end))) { ++end }
7955
+ }
7956
+ return new Range(Pos(pos.line, start), Pos(pos.line, end))
7957
+ },
7958
+
7959
+ toggleOverwrite: function(value) {
7960
+ if (value != null && value == this.state.overwrite) { return }
7961
+ if (this.state.overwrite = !this.state.overwrite)
7962
+ { addClass(this.display.cursorDiv, "CodeMirror-overwrite") }
7963
+ else
7964
+ { rmClass(this.display.cursorDiv, "CodeMirror-overwrite") }
7965
+
7966
+ signal(this, "overwriteToggle", this, this.state.overwrite)
7967
+ },
7968
+ hasFocus: function() { return this.display.input.getField() == activeElt() },
7969
+ isReadOnly: function() { return !!(this.options.readOnly || this.doc.cantEdit) },
7970
+
7971
+ scrollTo: methodOp(function(x, y) {
7972
+ if (x != null || y != null) { resolveScrollToPos(this) }
7973
+ if (x != null) { this.curOp.scrollLeft = x }
7974
+ if (y != null) { this.curOp.scrollTop = y }
7975
+ }),
7976
+ getScrollInfo: function() {
7977
+ var scroller = this.display.scroller
7978
+ return {left: scroller.scrollLeft, top: scroller.scrollTop,
7979
+ height: scroller.scrollHeight - scrollGap(this) - this.display.barHeight,
7980
+ width: scroller.scrollWidth - scrollGap(this) - this.display.barWidth,
7981
+ clientHeight: displayHeight(this), clientWidth: displayWidth(this)}
7982
+ },
7983
+
7984
+ scrollIntoView: methodOp(function(range$$1, margin) {
7985
+ if (range$$1 == null) {
7986
+ range$$1 = {from: this.doc.sel.primary().head, to: null}
7987
+ if (margin == null) { margin = this.options.cursorScrollMargin }
7988
+ } else if (typeof range$$1 == "number") {
7989
+ range$$1 = {from: Pos(range$$1, 0), to: null}
7990
+ } else if (range$$1.from == null) {
7991
+ range$$1 = {from: range$$1, to: null}
7992
+ }
7993
+ if (!range$$1.to) { range$$1.to = range$$1.from }
7994
+ range$$1.margin = margin || 0
7995
+
7996
+ if (range$$1.from.line != null) {
7997
+ resolveScrollToPos(this)
7998
+ this.curOp.scrollToPos = range$$1
7999
+ } else {
8000
+ var sPos = calculateScrollPos(this, Math.min(range$$1.from.left, range$$1.to.left),
8001
+ Math.min(range$$1.from.top, range$$1.to.top) - range$$1.margin,
8002
+ Math.max(range$$1.from.right, range$$1.to.right),
8003
+ Math.max(range$$1.from.bottom, range$$1.to.bottom) + range$$1.margin)
8004
+ this.scrollTo(sPos.scrollLeft, sPos.scrollTop)
8005
+ }
8006
+ }),
8007
+
8008
+ setSize: methodOp(function(width, height) {
8009
+ var this$1 = this;
8010
+
8011
+ var interpret = function (val) { return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val; }
8012
+ if (width != null) { this.display.wrapper.style.width = interpret(width) }
8013
+ if (height != null) { this.display.wrapper.style.height = interpret(height) }
8014
+ if (this.options.lineWrapping) { clearLineMeasurementCache(this) }
8015
+ var lineNo$$1 = this.display.viewFrom
8016
+ this.doc.iter(lineNo$$1, this.display.viewTo, function (line) {
8017
+ if (line.widgets) { for (var i = 0; i < line.widgets.length; i++)
8018
+ { if (line.widgets[i].noHScroll) { regLineChange(this$1, lineNo$$1, "widget"); break } } }
8019
+ ++lineNo$$1
8020
+ })
8021
+ this.curOp.forceUpdate = true
8022
+ signal(this, "refresh", this)
8023
+ }),
8024
+
8025
+ operation: function(f){return runInOp(this, f)},
8026
+
8027
+ refresh: methodOp(function() {
8028
+ var oldHeight = this.display.cachedTextHeight
8029
+ regChange(this)
8030
+ this.curOp.forceUpdate = true
8031
+ clearCaches(this)
8032
+ this.scrollTo(this.doc.scrollLeft, this.doc.scrollTop)
8033
+ updateGutterSpace(this)
8034
+ if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5)
8035
+ { estimateLineHeights(this) }
8036
+ signal(this, "refresh", this)
8037
+ }),
8038
+
8039
+ swapDoc: methodOp(function(doc) {
8040
+ var old = this.doc
8041
+ old.cm = null
8042
+ attachDoc(this, doc)
8043
+ clearCaches(this)
8044
+ this.display.input.reset()
8045
+ this.scrollTo(doc.scrollLeft, doc.scrollTop)
8046
+ this.curOp.forceScroll = true
8047
+ signalLater(this, "swapDoc", this, old)
8048
+ return old
8049
+ }),
8050
+
8051
+ getInputField: function(){return this.display.input.getField()},
8052
+ getWrapperElement: function(){return this.display.wrapper},
8053
+ getScrollerElement: function(){return this.display.scroller},
8054
+ getGutterElement: function(){return this.display.gutters}
8055
+ }
8056
+ eventMixin(CodeMirror)
8057
+
8058
+ CodeMirror.registerHelper = function(type, name, value) {
8059
+ if (!helpers.hasOwnProperty(type)) { helpers[type] = CodeMirror[type] = {_global: []} }
8060
+ helpers[type][name] = value
8061
+ }
8062
+ CodeMirror.registerGlobalHelper = function(type, name, predicate, value) {
8063
+ CodeMirror.registerHelper(type, name, value)
8064
+ helpers[type]._global.push({pred: predicate, val: value})
8065
+ }
8066
+ }
8067
+
8068
+ // Used for horizontal relative motion. Dir is -1 or 1 (left or
8069
+ // right), unit can be "char", "column" (like char, but doesn't
8070
+ // cross line boundaries), "word" (across next word), or "group" (to
8071
+ // the start of next group of word or non-word-non-whitespace
8072
+ // chars). The visually param controls whether, in right-to-left
8073
+ // text, direction 1 means to move towards the next index in the
8074
+ // string, or towards the character to the right of the current
8075
+ // position. The resulting position will have a hitSide=true
8076
+ // property if it reached the end of the document.
8077
+ function findPosH(doc, pos, dir, unit, visually) {
8078
+ var line = pos.line, ch = pos.ch, origDir = dir
8079
+ var lineObj = getLine(doc, line)
8080
+ function findNextLine() {
8081
+ var l = line + dir
8082
+ if (l < doc.first || l >= doc.first + doc.size) { return false }
8083
+ line = l
8084
+ return lineObj = getLine(doc, l)
8085
+ }
8086
+ function moveOnce(boundToLine) {
8087
+ var next = (visually ? moveVisually : moveLogically)(lineObj, ch, dir, true)
8088
+ if (next == null) {
8089
+ if (!boundToLine && findNextLine()) {
8090
+ if (visually) { ch = (dir < 0 ? lineRight : lineLeft)(lineObj) }
8091
+ else { ch = dir < 0 ? lineObj.text.length : 0 }
8092
+ } else { return false }
8093
+ } else { ch = next }
8094
+ return true
8095
+ }
8096
+
8097
+ if (unit == "char") {
8098
+ moveOnce()
8099
+ } else if (unit == "column") {
8100
+ moveOnce(true)
8101
+ } else if (unit == "word" || unit == "group") {
8102
+ var sawType = null, group = unit == "group"
8103
+ var helper = doc.cm && doc.cm.getHelper(pos, "wordChars")
8104
+ for (var first = true;; first = false) {
8105
+ if (dir < 0 && !moveOnce(!first)) { break }
8106
+ var cur = lineObj.text.charAt(ch) || "\n"
8107
+ var type = isWordChar(cur, helper) ? "w"
8108
+ : group && cur == "\n" ? "n"
8109
+ : !group || /\s/.test(cur) ? null
8110
+ : "p"
8111
+ if (group && !first && !type) { type = "s" }
8112
+ if (sawType && sawType != type) {
8113
+ if (dir < 0) {dir = 1; moveOnce()}
8114
+ break
8115
+ }
8116
+
8117
+ if (type) { sawType = type }
8118
+ if (dir > 0 && !moveOnce(!first)) { break }
8119
+ }
8120
+ }
8121
+ var result = skipAtomic(doc, Pos(line, ch), pos, origDir, true)
8122
+ if (!cmp(pos, result)) { result.hitSide = true }
8123
+ return result
8124
+ }
8125
+
8126
+ // For relative vertical movement. Dir may be -1 or 1. Unit can be
8127
+ // "page" or "line". The resulting position will have a hitSide=true
8128
+ // property if it reached the end of the document.
8129
+ function findPosV(cm, pos, dir, unit) {
8130
+ var doc = cm.doc, x = pos.left, y
8131
+ if (unit == "page") {
8132
+ var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight)
8133
+ var moveAmount = Math.max(pageSize - .5 * textHeight(cm.display), 3)
8134
+ y = (dir > 0 ? pos.bottom : pos.top) + dir * moveAmount
8135
+
8136
+ } else if (unit == "line") {
8137
+ y = dir > 0 ? pos.bottom + 3 : pos.top - 3
8138
+ }
8139
+ var target
8140
+ for (;;) {
8141
+ target = coordsChar(cm, x, y)
8142
+ if (!target.outside) { break }
8143
+ if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break }
8144
+ y += dir * 5
8145
+ }
8146
+ return target
8147
+ }
8148
+
8149
+ // CONTENTEDITABLE INPUT STYLE
8150
+
8151
+ var ContentEditableInput = function ContentEditableInput(cm) {
8152
+ this.cm = cm
8153
+ this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null
8154
+ this.polling = new Delayed()
8155
+ this.composing = null
8156
+ this.gracePeriod = false
8157
+ this.readDOMTimeout = null
8158
+ };
8159
+
8160
+ ContentEditableInput.prototype.init = function init (display) {
8161
+ var this$1 = this;
8162
+
8163
+ var input = this, cm = input.cm
8164
+ var div = input.div = display.lineDiv
8165
+ disableBrowserMagic(div, cm.options.spellcheck)
8166
+
8167
+ on(div, "paste", function (e) {
8168
+ if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return }
8169
+ // IE doesn't fire input events, so we schedule a read for the pasted content in this way
8170
+ if (ie_version <= 11) { setTimeout(operation(cm, function () {
8171
+ if (!input.pollContent()) { regChange(cm) }
8172
+ }), 20) }
8173
+ })
8174
+
8175
+ on(div, "compositionstart", function (e) {
8176
+ this$1.composing = {data: e.data, done: false}
8177
+ })
8178
+ on(div, "compositionupdate", function (e) {
8179
+ if (!this$1.composing) { this$1.composing = {data: e.data, done: false} }
8180
+ })
8181
+ on(div, "compositionend", function (e) {
8182
+ if (this$1.composing) {
8183
+ if (e.data != this$1.composing.data) { this$1.readFromDOMSoon() }
8184
+ this$1.composing.done = true
8185
+ }
8186
+ })
8187
+
8188
+ on(div, "touchstart", function () { return input.forceCompositionEnd(); })
8189
+
8190
+ on(div, "input", function () {
8191
+ if (!this$1.composing) { this$1.readFromDOMSoon() }
8192
+ })
8193
+
8194
+ function onCopyCut(e) {
8195
+ if (signalDOMEvent(cm, e)) { return }
8196
+ if (cm.somethingSelected()) {
8197
+ setLastCopied({lineWise: false, text: cm.getSelections()})
8198
+ if (e.type == "cut") { cm.replaceSelection("", null, "cut") }
8199
+ } else if (!cm.options.lineWiseCopyCut) {
8200
+ return
8201
+ } else {
8202
+ var ranges = copyableRanges(cm)
8203
+ setLastCopied({lineWise: true, text: ranges.text})
8204
+ if (e.type == "cut") {
8205
+ cm.operation(function () {
8206
+ cm.setSelections(ranges.ranges, 0, sel_dontScroll)
8207
+ cm.replaceSelection("", null, "cut")
8208
+ })
8209
+ }
8210
+ }
8211
+ if (e.clipboardData) {
8212
+ e.clipboardData.clearData()
8213
+ var content = lastCopied.text.join("\n")
8214
+ // iOS exposes the clipboard API, but seems to discard content inserted into it
8215
+ e.clipboardData.setData("Text", content)
8216
+ if (e.clipboardData.getData("Text") == content) {
8217
+ e.preventDefault()
8218
+ return
8219
+ }
8220
+ }
8221
+ // Old-fashioned briefly-focus-a-textarea hack
8222
+ var kludge = hiddenTextarea(), te = kludge.firstChild
8223
+ cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild)
8224
+ te.value = lastCopied.text.join("\n")
8225
+ var hadFocus = document.activeElement
8226
+ selectInput(te)
8227
+ setTimeout(function () {
8228
+ cm.display.lineSpace.removeChild(kludge)
8229
+ hadFocus.focus()
8230
+ if (hadFocus == div) { input.showPrimarySelection() }
8231
+ }, 50)
8232
+ }
8233
+ on(div, "copy", onCopyCut)
8234
+ on(div, "cut", onCopyCut)
8235
+ };
8236
+
8237
+ ContentEditableInput.prototype.prepareSelection = function prepareSelection$1 () {
8238
+ var result = prepareSelection(this.cm, false)
8239
+ result.focus = this.cm.state.focused
8240
+ return result
8241
+ };
8242
+
8243
+ ContentEditableInput.prototype.showSelection = function showSelection (info, takeFocus) {
8244
+ if (!info || !this.cm.display.view.length) { return }
8245
+ if (info.focus || takeFocus) { this.showPrimarySelection() }
8246
+ this.showMultipleSelections(info)
8247
+ };
8248
+
8249
+ ContentEditableInput.prototype.showPrimarySelection = function showPrimarySelection () {
8250
+ var sel = window.getSelection(), prim = this.cm.doc.sel.primary()
8251
+ var curAnchor = domToPos(this.cm, sel.anchorNode, sel.anchorOffset)
8252
+ var curFocus = domToPos(this.cm, sel.focusNode, sel.focusOffset)
8253
+ if (curAnchor && !curAnchor.bad && curFocus && !curFocus.bad &&
8254
+ cmp(minPos(curAnchor, curFocus), prim.from()) == 0 &&
8255
+ cmp(maxPos(curAnchor, curFocus), prim.to()) == 0)
8256
+ { return }
8257
+
8258
+ var start = posToDOM(this.cm, prim.from())
8259
+ var end = posToDOM(this.cm, prim.to())
8260
+ if (!start && !end) { return }
8261
+
8262
+ var view = this.cm.display.view
8263
+ var old = sel.rangeCount && sel.getRangeAt(0)
8264
+ if (!start) {
8265
+ start = {node: view[0].measure.map[2], offset: 0}
8266
+ } else if (!end) { // FIXME dangerously hacky
8267
+ var measure = view[view.length - 1].measure
8268
+ var map$$1 = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map
8269
+ end = {node: map$$1[map$$1.length - 1], offset: map$$1[map$$1.length - 2] - map$$1[map$$1.length - 3]}
8270
+ }
8271
+
8272
+ var rng
8273
+ try { rng = range(start.node, start.offset, end.offset, end.node) }
8274
+ catch(e) {} // Our model of the DOM might be outdated, in which case the range we try to set can be impossible
8275
+ if (rng) {
8276
+ if (!gecko && this.cm.state.focused) {
8277
+ sel.collapse(start.node, start.offset)
8278
+ if (!rng.collapsed) {
8279
+ sel.removeAllRanges()
8280
+ sel.addRange(rng)
8281
+ }
8282
+ } else {
8283
+ sel.removeAllRanges()
8284
+ sel.addRange(rng)
8285
+ }
8286
+ if (old && sel.anchorNode == null) { sel.addRange(old) }
8287
+ else if (gecko) { this.startGracePeriod() }
8288
+ }
8289
+ this.rememberSelection()
8290
+ };
8291
+
8292
+ ContentEditableInput.prototype.startGracePeriod = function startGracePeriod () {
8293
+ var this$1 = this;
8294
+
8295
+ clearTimeout(this.gracePeriod)
8296
+ this.gracePeriod = setTimeout(function () {
8297
+ this$1.gracePeriod = false
8298
+ if (this$1.selectionChanged())
8299
+ { this$1.cm.operation(function () { return this$1.cm.curOp.selectionChanged = true; }) }
8300
+ }, 20)
8301
+ };
8302
+
8303
+ ContentEditableInput.prototype.showMultipleSelections = function showMultipleSelections (info) {
8304
+ removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors)
8305
+ removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection)
8306
+ };
8307
+
8308
+ ContentEditableInput.prototype.rememberSelection = function rememberSelection () {
8309
+ var sel = window.getSelection()
8310
+ this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset
8311
+ this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset
8312
+ };
8313
+
8314
+ ContentEditableInput.prototype.selectionInEditor = function selectionInEditor () {
8315
+ var sel = window.getSelection()
8316
+ if (!sel.rangeCount) { return false }
8317
+ var node = sel.getRangeAt(0).commonAncestorContainer
8318
+ return contains(this.div, node)
8319
+ };
8320
+
8321
+ ContentEditableInput.prototype.focus = function focus () {
8322
+ if (this.cm.options.readOnly != "nocursor") {
8323
+ if (!this.selectionInEditor())
8324
+ { this.showSelection(this.prepareSelection(), true) }
8325
+ this.div.focus()
8326
+ }
8327
+ };
8328
+ ContentEditableInput.prototype.blur = function blur () { this.div.blur() };
8329
+ ContentEditableInput.prototype.getField = function getField () { return this.div };
8330
+
8331
+ ContentEditableInput.prototype.supportsTouch = function supportsTouch () { return true };
8332
+
8333
+ ContentEditableInput.prototype.receivedFocus = function receivedFocus () {
8334
+ var input = this
8335
+ if (this.selectionInEditor())
8336
+ { this.pollSelection() }
8337
+ else
8338
+ { runInOp(this.cm, function () { return input.cm.curOp.selectionChanged = true; }) }
8339
+
8340
+ function poll() {
8341
+ if (input.cm.state.focused) {
8342
+ input.pollSelection()
8343
+ input.polling.set(input.cm.options.pollInterval, poll)
8344
+ }
8345
+ }
8346
+ this.polling.set(this.cm.options.pollInterval, poll)
8347
+ };
8348
+
8349
+ ContentEditableInput.prototype.selectionChanged = function selectionChanged () {
8350
+ var sel = window.getSelection()
8351
+ return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset ||
8352
+ sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset
8353
+ };
8354
+
8355
+ ContentEditableInput.prototype.pollSelection = function pollSelection () {
8356
+ if (!this.composing && this.readDOMTimeout == null && !this.gracePeriod && this.selectionChanged()) {
8357
+ var sel = window.getSelection(), cm = this.cm
8358
+ this.rememberSelection()
8359
+ var anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset)
8360
+ var head = domToPos(cm, sel.focusNode, sel.focusOffset)
8361
+ if (anchor && head) { runInOp(cm, function () {
8362
+ setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll)
8363
+ if (anchor.bad || head.bad) { cm.curOp.selectionChanged = true }
8364
+ }) }
8365
+ }
8366
+ };
8367
+
8368
+ ContentEditableInput.prototype.pollContent = function pollContent () {
8369
+ if (this.readDOMTimeout != null) {
8370
+ clearTimeout(this.readDOMTimeout)
8371
+ this.readDOMTimeout = null
8372
+ }
8373
+
8374
+ var cm = this.cm, display = cm.display, sel = cm.doc.sel.primary()
8375
+ var from = sel.from(), to = sel.to()
8376
+ if (from.ch == 0 && from.line > cm.firstLine())
8377
+ { from = Pos(from.line - 1, getLine(cm.doc, from.line - 1).length) }
8378
+ if (to.ch == getLine(cm.doc, to.line).text.length && to.line < cm.lastLine())
8379
+ { to = Pos(to.line + 1, 0) }
8380
+ if (from.line < display.viewFrom || to.line > display.viewTo - 1) { return false }
8381
+
8382
+ var fromIndex, fromLine, fromNode
8383
+ if (from.line == display.viewFrom || (fromIndex = findViewIndex(cm, from.line)) == 0) {
8384
+ fromLine = lineNo(display.view[0].line)
8385
+ fromNode = display.view[0].node
8386
+ } else {
8387
+ fromLine = lineNo(display.view[fromIndex].line)
8388
+ fromNode = display.view[fromIndex - 1].node.nextSibling
8389
+ }
8390
+ var toIndex = findViewIndex(cm, to.line)
8391
+ var toLine, toNode
8392
+ if (toIndex == display.view.length - 1) {
8393
+ toLine = display.viewTo - 1
8394
+ toNode = display.lineDiv.lastChild
8395
+ } else {
8396
+ toLine = lineNo(display.view[toIndex + 1].line) - 1
8397
+ toNode = display.view[toIndex + 1].node.previousSibling
8398
+ }
8399
+
8400
+ if (!fromNode) { return false }
8401
+ var newText = cm.doc.splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine))
8402
+ var oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm.doc, toLine).text.length))
8403
+ while (newText.length > 1 && oldText.length > 1) {
8404
+ if (lst(newText) == lst(oldText)) { newText.pop(); oldText.pop(); toLine-- }
8405
+ else if (newText[0] == oldText[0]) { newText.shift(); oldText.shift(); fromLine++ }
8406
+ else { break }
8407
+ }
8408
+
8409
+ var cutFront = 0, cutEnd = 0
8410
+ var newTop = newText[0], oldTop = oldText[0], maxCutFront = Math.min(newTop.length, oldTop.length)
8411
+ while (cutFront < maxCutFront && newTop.charCodeAt(cutFront) == oldTop.charCodeAt(cutFront))
8412
+ { ++cutFront }
8413
+ var newBot = lst(newText), oldBot = lst(oldText)
8414
+ var maxCutEnd = Math.min(newBot.length - (newText.length == 1 ? cutFront : 0),
8415
+ oldBot.length - (oldText.length == 1 ? cutFront : 0))
8416
+ while (cutEnd < maxCutEnd &&
8417
+ newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1))
8418
+ { ++cutEnd }
8419
+
8420
+ newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd).replace(/^\u200b+/, "")
8421
+ newText[0] = newText[0].slice(cutFront).replace(/\u200b+$/, "")
8422
+
8423
+ var chFrom = Pos(fromLine, cutFront)
8424
+ var chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0)
8425
+ if (newText.length > 1 || newText[0] || cmp(chFrom, chTo)) {
8426
+ replaceRange(cm.doc, newText, chFrom, chTo, "+input")
8427
+ return true
8428
+ }
8429
+ };
8430
+
8431
+ ContentEditableInput.prototype.ensurePolled = function ensurePolled () {
8432
+ this.forceCompositionEnd()
8433
+ };
8434
+ ContentEditableInput.prototype.reset = function reset () {
8435
+ this.forceCompositionEnd()
8436
+ };
8437
+ ContentEditableInput.prototype.forceCompositionEnd = function forceCompositionEnd () {
8438
+ if (!this.composing) { return }
8439
+ clearTimeout(this.readDOMTimeout)
8440
+ this.composing = null
8441
+ if (!this.pollContent()) { regChange(this.cm) }
8442
+ this.div.blur()
8443
+ this.div.focus()
8444
+ };
8445
+ ContentEditableInput.prototype.readFromDOMSoon = function readFromDOMSoon () {
8446
+ var this$1 = this;
8447
+
8448
+ if (this.readDOMTimeout != null) { return }
8449
+ this.readDOMTimeout = setTimeout(function () {
8450
+ this$1.readDOMTimeout = null
8451
+ if (this$1.composing) {
8452
+ if (this$1.composing.done) { this$1.composing = null }
8453
+ else { return }
8454
+ }
8455
+ if (this$1.cm.isReadOnly() || !this$1.pollContent())
8456
+ { runInOp(this$1.cm, function () { return regChange(this$1.cm); }) }
8457
+ }, 80)
8458
+ };
8459
+
8460
+ ContentEditableInput.prototype.setUneditable = function setUneditable (node) {
8461
+ node.contentEditable = "false"
8462
+ };
8463
+
8464
+ ContentEditableInput.prototype.onKeyPress = function onKeyPress (e) {
8465
+ e.preventDefault()
8466
+ if (!this.cm.isReadOnly())
8467
+ { operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode), 0) }
8468
+ };
8469
+
8470
+ ContentEditableInput.prototype.readOnlyChanged = function readOnlyChanged (val) {
8471
+ this.div.contentEditable = String(val != "nocursor")
8472
+ };
8473
+
8474
+ ContentEditableInput.prototype.onContextMenu = function onContextMenu () {};
8475
+ ContentEditableInput.prototype.resetPosition = function resetPosition () {};
8476
+
8477
+ ContentEditableInput.prototype.needsContentAttribute = true
8478
+
8479
+ function posToDOM(cm, pos) {
8480
+ var view = findViewForLine(cm, pos.line)
8481
+ if (!view || view.hidden) { return null }
8482
+ var line = getLine(cm.doc, pos.line)
8483
+ var info = mapFromLineView(view, line, pos.line)
8484
+
8485
+ var order = getOrder(line), side = "left"
8486
+ if (order) {
8487
+ var partPos = getBidiPartAt(order, pos.ch)
8488
+ side = partPos % 2 ? "right" : "left"
8489
+ }
8490
+ var result = nodeAndOffsetInLineMap(info.map, pos.ch, side)
8491
+ result.offset = result.collapse == "right" ? result.end : result.start
8492
+ return result
8493
+ }
8494
+
8495
+ function badPos(pos, bad) { if (bad) { pos.bad = true; } return pos }
8496
+
8497
+ function domTextBetween(cm, from, to, fromLine, toLine) {
8498
+ var text = "", closing = false, lineSep = cm.doc.lineSeparator()
8499
+ function recognizeMarker(id) { return function (marker) { return marker.id == id; } }
8500
+ function walk(node) {
8501
+ if (node.nodeType == 1) {
8502
+ var cmText = node.getAttribute("cm-text")
8503
+ if (cmText != null) {
8504
+ if (cmText == "") { text += node.textContent.replace(/\u200b/g, "") }
8505
+ else { text += cmText }
8506
+ return
8507
+ }
8508
+ var markerID = node.getAttribute("cm-marker"), range$$1
8509
+ if (markerID) {
8510
+ var found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recognizeMarker(+markerID))
8511
+ if (found.length && (range$$1 = found[0].find()))
8512
+ { text += getBetween(cm.doc, range$$1.from, range$$1.to).join(lineSep) }
8513
+ return
8514
+ }
8515
+ if (node.getAttribute("contenteditable") == "false") { return }
8516
+ for (var i = 0; i < node.childNodes.length; i++)
8517
+ { walk(node.childNodes[i]) }
8518
+ if (/^(pre|div|p)$/i.test(node.nodeName))
8519
+ { closing = true }
8520
+ } else if (node.nodeType == 3) {
8521
+ var val = node.nodeValue
8522
+ if (!val) { return }
8523
+ if (closing) {
8524
+ text += lineSep
8525
+ closing = false
8526
+ }
8527
+ text += val
8528
+ }
8529
+ }
8530
+ for (;;) {
8531
+ walk(from)
8532
+ if (from == to) { break }
8533
+ from = from.nextSibling
8534
+ }
8535
+ return text
8536
+ }
8537
+
8538
+ function domToPos(cm, node, offset) {
8539
+ var lineNode
8540
+ if (node == cm.display.lineDiv) {
8541
+ lineNode = cm.display.lineDiv.childNodes[offset]
8542
+ if (!lineNode) { return badPos(cm.clipPos(Pos(cm.display.viewTo - 1)), true) }
8543
+ node = null; offset = 0
8544
+ } else {
8545
+ for (lineNode = node;; lineNode = lineNode.parentNode) {
8546
+ if (!lineNode || lineNode == cm.display.lineDiv) { return null }
8547
+ if (lineNode.parentNode && lineNode.parentNode == cm.display.lineDiv) { break }
8548
+ }
8549
+ }
8550
+ for (var i = 0; i < cm.display.view.length; i++) {
8551
+ var lineView = cm.display.view[i]
8552
+ if (lineView.node == lineNode)
8553
+ { return locateNodeInLineView(lineView, node, offset) }
8554
+ }
8555
+ }
8556
+
8557
+ function locateNodeInLineView(lineView, node, offset) {
8558
+ var wrapper = lineView.text.firstChild, bad = false
8559
+ if (!node || !contains(wrapper, node)) { return badPos(Pos(lineNo(lineView.line), 0), true) }
8560
+ if (node == wrapper) {
8561
+ bad = true
8562
+ node = wrapper.childNodes[offset]
8563
+ offset = 0
8564
+ if (!node) {
8565
+ var line = lineView.rest ? lst(lineView.rest) : lineView.line
8566
+ return badPos(Pos(lineNo(line), line.text.length), bad)
8567
+ }
8568
+ }
8569
+
8570
+ var textNode = node.nodeType == 3 ? node : null, topNode = node
8571
+ if (!textNode && node.childNodes.length == 1 && node.firstChild.nodeType == 3) {
8572
+ textNode = node.firstChild
8573
+ if (offset) { offset = textNode.nodeValue.length }
8574
+ }
8575
+ while (topNode.parentNode != wrapper) { topNode = topNode.parentNode }
8576
+ var measure = lineView.measure, maps = measure.maps
8577
+
8578
+ function find(textNode, topNode, offset) {
8579
+ for (var i = -1; i < (maps ? maps.length : 0); i++) {
8580
+ var map$$1 = i < 0 ? measure.map : maps[i]
8581
+ for (var j = 0; j < map$$1.length; j += 3) {
8582
+ var curNode = map$$1[j + 2]
8583
+ if (curNode == textNode || curNode == topNode) {
8584
+ var line = lineNo(i < 0 ? lineView.line : lineView.rest[i])
8585
+ var ch = map$$1[j] + offset
8586
+ if (offset < 0 || curNode != textNode) { ch = map$$1[j + (offset ? 1 : 0)] }
8587
+ return Pos(line, ch)
8588
+ }
8589
+ }
8590
+ }
8591
+ }
8592
+ var found = find(textNode, topNode, offset)
8593
+ if (found) { return badPos(found, bad) }
8594
+
8595
+ // FIXME this is all really shaky. might handle the few cases it needs to handle, but likely to cause problems
8596
+ for (var after = topNode.nextSibling, dist = textNode ? textNode.nodeValue.length - offset : 0; after; after = after.nextSibling) {
8597
+ found = find(after, after.firstChild, 0)
8598
+ if (found)
8599
+ { return badPos(Pos(found.line, found.ch - dist), bad) }
8600
+ else
8601
+ { dist += after.textContent.length }
8602
+ }
8603
+ for (var before = topNode.previousSibling, dist$1 = offset; before; before = before.previousSibling) {
8604
+ found = find(before, before.firstChild, -1)
8605
+ if (found)
8606
+ { return badPos(Pos(found.line, found.ch + dist$1), bad) }
8607
+ else
8608
+ { dist$1 += before.textContent.length }
8609
+ }
8610
+ }
8611
+
8612
+ // TEXTAREA INPUT STYLE
8613
+
8614
+ var TextareaInput = function TextareaInput(cm) {
8615
+ this.cm = cm
8616
+ // See input.poll and input.reset
8617
+ this.prevInput = ""
8618
+
8619
+ // Flag that indicates whether we expect input to appear real soon
8620
+ // now (after some event like 'keypress' or 'input') and are
8621
+ // polling intensively.
8622
+ this.pollingFast = false
8623
+ // Self-resetting timeout for the poller
8624
+ this.polling = new Delayed()
8625
+ // Tracks when input.reset has punted to just putting a short
8626
+ // string into the textarea instead of the full selection.
8627
+ this.inaccurateSelection = false
8628
+ // Used to work around IE issue with selection being forgotten when focus moves away from textarea
8629
+ this.hasSelection = false
8630
+ this.composing = null
8631
+ };
8632
+
8633
+ TextareaInput.prototype.init = function init (display) {
8634
+ var this$1 = this;
8635
+
8636
+ var input = this, cm = this.cm
8637
+
8638
+ // Wraps and hides input textarea
8639
+ var div = this.wrapper = hiddenTextarea()
8640
+ // The semihidden textarea that is focused when the editor is
8641
+ // focused, and receives input.
8642
+ var te = this.textarea = div.firstChild
8643
+ display.wrapper.insertBefore(div, display.wrapper.firstChild)
8644
+
8645
+ // Needed to hide big blue blinking cursor on Mobile Safari (doesn't seem to work in iOS 8 anymore)
8646
+ if (ios) { te.style.width = "0px" }
8647
+
8648
+ on(te, "input", function () {
8649
+ if (ie && ie_version >= 9 && this$1.hasSelection) { this$1.hasSelection = null }
8650
+ input.poll()
8651
+ })
8652
+
8653
+ on(te, "paste", function (e) {
8654
+ if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return }
8655
+
8656
+ cm.state.pasteIncoming = true
8657
+ input.fastPoll()
8658
+ })
8659
+
8660
+ function prepareCopyCut(e) {
8661
+ if (signalDOMEvent(cm, e)) { return }
8662
+ if (cm.somethingSelected()) {
8663
+ setLastCopied({lineWise: false, text: cm.getSelections()})
8664
+ if (input.inaccurateSelection) {
8665
+ input.prevInput = ""
8666
+ input.inaccurateSelection = false
8667
+ te.value = lastCopied.text.join("\n")
8668
+ selectInput(te)
8669
+ }
8670
+ } else if (!cm.options.lineWiseCopyCut) {
8671
+ return
8672
+ } else {
8673
+ var ranges = copyableRanges(cm)
8674
+ setLastCopied({lineWise: true, text: ranges.text})
8675
+ if (e.type == "cut") {
8676
+ cm.setSelections(ranges.ranges, null, sel_dontScroll)
8677
+ } else {
8678
+ input.prevInput = ""
8679
+ te.value = ranges.text.join("\n")
8680
+ selectInput(te)
8681
+ }
8682
+ }
8683
+ if (e.type == "cut") { cm.state.cutIncoming = true }
8684
+ }
8685
+ on(te, "cut", prepareCopyCut)
8686
+ on(te, "copy", prepareCopyCut)
8687
+
8688
+ on(display.scroller, "paste", function (e) {
8689
+ if (eventInWidget(display, e) || signalDOMEvent(cm, e)) { return }
8690
+ cm.state.pasteIncoming = true
8691
+ input.focus()
8692
+ })
8693
+
8694
+ // Prevent normal selection in the editor (we handle our own)
8695
+ on(display.lineSpace, "selectstart", function (e) {
8696
+ if (!eventInWidget(display, e)) { e_preventDefault(e) }
8697
+ })
8698
+
8699
+ on(te, "compositionstart", function () {
8700
+ var start = cm.getCursor("from")
8701
+ if (input.composing) { input.composing.range.clear() }
8702
+ input.composing = {
8703
+ start: start,
8704
+ range: cm.markText(start, cm.getCursor("to"), {className: "CodeMirror-composing"})
8705
+ }
8706
+ })
8707
+ on(te, "compositionend", function () {
8708
+ if (input.composing) {
8709
+ input.poll()
8710
+ input.composing.range.clear()
8711
+ input.composing = null
8712
+ }
8713
+ })
8714
+ };
8715
+
8716
+ TextareaInput.prototype.prepareSelection = function prepareSelection$1 () {
8717
+ // Redraw the selection and/or cursor
8718
+ var cm = this.cm, display = cm.display, doc = cm.doc
8719
+ var result = prepareSelection(cm)
8720
+
8721
+ // Move the hidden textarea near the cursor to prevent scrolling artifacts
8722
+ if (cm.options.moveInputWithCursor) {
8723
+ var headPos = cursorCoords(cm, doc.sel.primary().head, "div")
8724
+ var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect()
8725
+ result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
8726
+ headPos.top + lineOff.top - wrapOff.top))
8727
+ result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
8728
+ headPos.left + lineOff.left - wrapOff.left))
8729
+ }
8730
+
8731
+ return result
8732
+ };
8733
+
8734
+ TextareaInput.prototype.showSelection = function showSelection (drawn) {
8735
+ var cm = this.cm, display = cm.display
8736
+ removeChildrenAndAdd(display.cursorDiv, drawn.cursors)
8737
+ removeChildrenAndAdd(display.selectionDiv, drawn.selection)
8738
+ if (drawn.teTop != null) {
8739
+ this.wrapper.style.top = drawn.teTop + "px"
8740
+ this.wrapper.style.left = drawn.teLeft + "px"
8741
+ }
8742
+ };
8743
+
8744
+ // Reset the input to correspond to the selection (or to be empty,
8745
+ // when not typing and nothing is selected)
8746
+ TextareaInput.prototype.reset = function reset (typing) {
8747
+ if (this.contextMenuPending) { return }
8748
+ var minimal, selected, cm = this.cm, doc = cm.doc
8749
+ if (cm.somethingSelected()) {
8750
+ this.prevInput = ""
8751
+ var range$$1 = doc.sel.primary()
8752
+ minimal = hasCopyEvent &&
8753
+ (range$$1.to().line - range$$1.from().line > 100 || (selected = cm.getSelection()).length > 1000)
8754
+ var content = minimal ? "-" : selected || cm.getSelection()
8755
+ this.textarea.value = content
8756
+ if (cm.state.focused) { selectInput(this.textarea) }
8757
+ if (ie && ie_version >= 9) { this.hasSelection = content }
8758
+ } else if (!typing) {
8759
+ this.prevInput = this.textarea.value = ""
8760
+ if (ie && ie_version >= 9) { this.hasSelection = null }
8761
+ }
8762
+ this.inaccurateSelection = minimal
8763
+ };
8764
+
8765
+ TextareaInput.prototype.getField = function getField () { return this.textarea };
8766
+
8767
+ TextareaInput.prototype.supportsTouch = function supportsTouch () { return false };
8768
+
8769
+ TextareaInput.prototype.focus = function focus () {
8770
+ if (this.cm.options.readOnly != "nocursor" && (!mobile || activeElt() != this.textarea)) {
8771
+ try { this.textarea.focus() }
8772
+ catch (e) {} // IE8 will throw if the textarea is display: none or not in DOM
8773
+ }
8774
+ };
8775
+
8776
+ TextareaInput.prototype.blur = function blur () { this.textarea.blur() };
8777
+
8778
+ TextareaInput.prototype.resetPosition = function resetPosition () {
8779
+ this.wrapper.style.top = this.wrapper.style.left = 0
8780
+ };
8781
+
8782
+ TextareaInput.prototype.receivedFocus = function receivedFocus () { this.slowPoll() };
8783
+
8784
+ // Poll for input changes, using the normal rate of polling. This
8785
+ // runs as long as the editor is focused.
8786
+ TextareaInput.prototype.slowPoll = function slowPoll () {
8787
+ var this$1 = this;
8788
+
8789
+ if (this.pollingFast) { return }
8790
+ this.polling.set(this.cm.options.pollInterval, function () {
8791
+ this$1.poll()
8792
+ if (this$1.cm.state.focused) { this$1.slowPoll() }
8793
+ })
8794
+ };
8795
+
8796
+ // When an event has just come in that is likely to add or change
8797
+ // something in the input textarea, we poll faster, to ensure that
8798
+ // the change appears on the screen quickly.
8799
+ TextareaInput.prototype.fastPoll = function fastPoll () {
8800
+ var missed = false, input = this
8801
+ input.pollingFast = true
8802
+ function p() {
8803
+ var changed = input.poll()
8804
+ if (!changed && !missed) {missed = true; input.polling.set(60, p)}
8805
+ else {input.pollingFast = false; input.slowPoll()}
8806
+ }
8807
+ input.polling.set(20, p)
8808
+ };
8809
+
8810
+ // Read input from the textarea, and update the document to match.
8811
+ // When something is selected, it is present in the textarea, and
8812
+ // selected (unless it is huge, in which case a placeholder is
8813
+ // used). When nothing is selected, the cursor sits after previously
8814
+ // seen text (can be empty), which is stored in prevInput (we must
8815
+ // not reset the textarea when typing, because that breaks IME).
8816
+ TextareaInput.prototype.poll = function poll () {
8817
+ var this$1 = this;
8818
+
8819
+ var cm = this.cm, input = this.textarea, prevInput = this.prevInput
8820
+ // Since this is called a *lot*, try to bail out as cheaply as
8821
+ // possible when it is clear that nothing happened. hasSelection
8822
+ // will be the case when there is a lot of text in the textarea,
8823
+ // in which case reading its value would be expensive.
8824
+ if (this.contextMenuPending || !cm.state.focused ||
8825
+ (hasSelection(input) && !prevInput && !this.composing) ||
8826
+ cm.isReadOnly() || cm.options.disableInput || cm.state.keySeq)
8827
+ { return false }
8828
+
8829
+ var text = input.value
8830
+ // If nothing changed, bail.
8831
+ if (text == prevInput && !cm.somethingSelected()) { return false }
8832
+ // Work around nonsensical selection resetting in IE9/10, and
8833
+ // inexplicable appearance of private area unicode characters on
8834
+ // some key combos in Mac (#2689).
8835
+ if (ie && ie_version >= 9 && this.hasSelection === text ||
8836
+ mac && /[\uf700-\uf7ff]/.test(text)) {
8837
+ cm.display.input.reset()
8838
+ return false
8839
+ }
8840
+
8841
+ if (cm.doc.sel == cm.display.selForContextMenu) {
8842
+ var first = text.charCodeAt(0)
8843
+ if (first == 0x200b && !prevInput) { prevInput = "\u200b" }
8844
+ if (first == 0x21da) { this.reset(); return this.cm.execCommand("undo") }
8845
+ }
8846
+ // Find the part of the input that is actually new
8847
+ var same = 0, l = Math.min(prevInput.length, text.length)
8848
+ while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) { ++same }
8849
+
8850
+ runInOp(cm, function () {
8851
+ applyTextInput(cm, text.slice(same), prevInput.length - same,
8852
+ null, this$1.composing ? "*compose" : null)
8853
+
8854
+ // Don't leave long text in the textarea, since it makes further polling slow
8855
+ if (text.length > 1000 || text.indexOf("\n") > -1) { input.value = this$1.prevInput = "" }
8856
+ else { this$1.prevInput = text }
8857
+
8858
+ if (this$1.composing) {
8859
+ this$1.composing.range.clear()
8860
+ this$1.composing.range = cm.markText(this$1.composing.start, cm.getCursor("to"),
8861
+ {className: "CodeMirror-composing"})
8862
+ }
8863
+ })
8864
+ return true
8865
+ };
8866
+
8867
+ TextareaInput.prototype.ensurePolled = function ensurePolled () {
8868
+ if (this.pollingFast && this.poll()) { this.pollingFast = false }
8869
+ };
8870
+
8871
+ TextareaInput.prototype.onKeyPress = function onKeyPress () {
8872
+ if (ie && ie_version >= 9) { this.hasSelection = null }
8873
+ this.fastPoll()
8874
+ };
8875
+
8876
+ TextareaInput.prototype.onContextMenu = function onContextMenu (e) {
8877
+ var input = this, cm = input.cm, display = cm.display, te = input.textarea
8878
+ var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop
8879
+ if (!pos || presto) { return } // Opera is difficult.
8880
+
8881
+ // Reset the current text selection only if the click is done outside of the selection
8882
+ // and 'resetSelectionOnContextMenu' option is true.
8883
+ var reset = cm.options.resetSelectionOnContextMenu
8884
+ if (reset && cm.doc.sel.contains(pos) == -1)
8885
+ { operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll) }
8886
+
8887
+ var oldCSS = te.style.cssText, oldWrapperCSS = input.wrapper.style.cssText
8888
+ input.wrapper.style.cssText = "position: absolute"
8889
+ var wrapperBox = input.wrapper.getBoundingClientRect()
8890
+ te.style.cssText = "position: absolute; width: 30px; height: 30px;\n top: " + (e.clientY - wrapperBox.top - 5) + "px; left: " + (e.clientX - wrapperBox.left - 5) + "px;\n z-index: 1000; background: " + (ie ? "rgba(255, 255, 255, .05)" : "transparent") + ";\n outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);"
8891
+ var oldScrollY
8892
+ if (webkit) { oldScrollY = window.scrollY } // Work around Chrome issue (#2712)
8893
+ display.input.focus()
8894
+ if (webkit) { window.scrollTo(null, oldScrollY) }
8895
+ display.input.reset()
8896
+ // Adds "Select all" to context menu in FF
8897
+ if (!cm.somethingSelected()) { te.value = input.prevInput = " " }
8898
+ input.contextMenuPending = true
8899
+ display.selForContextMenu = cm.doc.sel
8900
+ clearTimeout(display.detectingSelectAll)
8901
+
8902
+ // Select-all will be greyed out if there's nothing to select, so
8903
+ // this adds a zero-width space so that we can later check whether
8904
+ // it got selected.
8905
+ function prepareSelectAllHack() {
8906
+ if (te.selectionStart != null) {
8907
+ var selected = cm.somethingSelected()
8908
+ var extval = "\u200b" + (selected ? te.value : "")
8909
+ te.value = "\u21da" // Used to catch context-menu undo
8910
+ te.value = extval
8911
+ input.prevInput = selected ? "" : "\u200b"
8912
+ te.selectionStart = 1; te.selectionEnd = extval.length
8913
+ // Re-set this, in case some other handler touched the
8914
+ // selection in the meantime.
8915
+ display.selForContextMenu = cm.doc.sel
8916
+ }
8917
+ }
8918
+ function rehide() {
8919
+ input.contextMenuPending = false
8920
+ input.wrapper.style.cssText = oldWrapperCSS
8921
+ te.style.cssText = oldCSS
8922
+ if (ie && ie_version < 9) { display.scrollbars.setScrollTop(display.scroller.scrollTop = scrollPos) }
8923
+
8924
+ // Try to detect the user choosing select-all
8925
+ if (te.selectionStart != null) {
8926
+ if (!ie || (ie && ie_version < 9)) { prepareSelectAllHack() }
8927
+ var i = 0, poll = function () {
8928
+ if (display.selForContextMenu == cm.doc.sel && te.selectionStart == 0 &&
8929
+ te.selectionEnd > 0 && input.prevInput == "\u200b")
8930
+ { operation(cm, selectAll)(cm) }
8931
+ else if (i++ < 10) { display.detectingSelectAll = setTimeout(poll, 500) }
8932
+ else { display.input.reset() }
8933
+ }
8934
+ display.detectingSelectAll = setTimeout(poll, 200)
8935
+ }
8936
+ }
8937
+
8938
+ if (ie && ie_version >= 9) { prepareSelectAllHack() }
8939
+ if (captureRightClick) {
8940
+ e_stop(e)
8941
+ var mouseup = function () {
8942
+ off(window, "mouseup", mouseup)
8943
+ setTimeout(rehide, 20)
8944
+ }
8945
+ on(window, "mouseup", mouseup)
8946
+ } else {
8947
+ setTimeout(rehide, 50)
8948
+ }
8949
+ };
8950
+
8951
+ TextareaInput.prototype.readOnlyChanged = function readOnlyChanged (val) {
8952
+ if (!val) { this.reset() }
8953
+ };
8954
+
8955
+ TextareaInput.prototype.setUneditable = function setUneditable () {};
8956
+
8957
+ TextareaInput.prototype.needsContentAttribute = false
8958
+
8959
+ function fromTextArea(textarea, options) {
8960
+ options = options ? copyObj(options) : {}
8961
+ options.value = textarea.value
8962
+ if (!options.tabindex && textarea.tabIndex)
8963
+ { options.tabindex = textarea.tabIndex }
8964
+ if (!options.placeholder && textarea.placeholder)
8965
+ { options.placeholder = textarea.placeholder }
8966
+ // Set autofocus to true if this textarea is focused, or if it has
8967
+ // autofocus and no other element is focused.
8968
+ if (options.autofocus == null) {
8969
+ var hasFocus = activeElt()
8970
+ options.autofocus = hasFocus == textarea ||
8971
+ textarea.getAttribute("autofocus") != null && hasFocus == document.body
8972
+ }
8973
+
8974
+ function save() {textarea.value = cm.getValue()}
8975
+
8976
+ var realSubmit
8977
+ if (textarea.form) {
8978
+ on(textarea.form, "submit", save)
8979
+ // Deplorable hack to make the submit method do the right thing.
8980
+ if (!options.leaveSubmitMethodAlone) {
8981
+ var form = textarea.form
8982
+ realSubmit = form.submit
8983
+ try {
8984
+ var wrappedSubmit = form.submit = function () {
8985
+ save()
8986
+ form.submit = realSubmit
8987
+ form.submit()
8988
+ form.submit = wrappedSubmit
8989
+ }
8990
+ } catch(e) {}
8991
+ }
8992
+ }
8993
+
8994
+ options.finishInit = function (cm) {
8995
+ cm.save = save
8996
+ cm.getTextArea = function () { return textarea; }
8997
+ cm.toTextArea = function () {
8998
+ cm.toTextArea = isNaN // Prevent this from being ran twice
8999
+ save()
9000
+ textarea.parentNode.removeChild(cm.getWrapperElement())
9001
+ textarea.style.display = ""
9002
+ if (textarea.form) {
9003
+ off(textarea.form, "submit", save)
9004
+ if (typeof textarea.form.submit == "function")
9005
+ { textarea.form.submit = realSubmit }
9006
+ }
9007
+ }
9008
+ }
9009
+
9010
+ textarea.style.display = "none"
9011
+ var cm = CodeMirror$1(function (node) { return textarea.parentNode.insertBefore(node, textarea.nextSibling); },
9012
+ options)
9013
+ return cm
9014
+ }
9015
+
9016
+ function addLegacyProps(CodeMirror) {
9017
+ CodeMirror.off = off
9018
+ CodeMirror.on = on
9019
+ CodeMirror.wheelEventPixels = wheelEventPixels
9020
+ CodeMirror.Doc = Doc
9021
+ CodeMirror.splitLines = splitLinesAuto
9022
+ CodeMirror.countColumn = countColumn
9023
+ CodeMirror.findColumn = findColumn
9024
+ CodeMirror.isWordChar = isWordCharBasic
9025
+ CodeMirror.Pass = Pass
9026
+ CodeMirror.signal = signal
9027
+ CodeMirror.Line = Line
9028
+ CodeMirror.changeEnd = changeEnd
9029
+ CodeMirror.scrollbarModel = scrollbarModel
9030
+ CodeMirror.Pos = Pos
9031
+ CodeMirror.cmpPos = cmp
9032
+ CodeMirror.modes = modes
9033
+ CodeMirror.mimeModes = mimeModes
9034
+ CodeMirror.resolveMode = resolveMode
9035
+ CodeMirror.getMode = getMode
9036
+ CodeMirror.modeExtensions = modeExtensions
9037
+ CodeMirror.extendMode = extendMode
9038
+ CodeMirror.copyState = copyState
9039
+ CodeMirror.startState = startState
9040
+ CodeMirror.innerMode = innerMode
9041
+ CodeMirror.commands = commands
9042
+ CodeMirror.keyMap = keyMap
9043
+ CodeMirror.keyName = keyName
9044
+ CodeMirror.isModifierKey = isModifierKey
9045
+ CodeMirror.lookupKey = lookupKey
9046
+ CodeMirror.normalizeKeyMap = normalizeKeyMap
9047
+ CodeMirror.StringStream = StringStream
9048
+ CodeMirror.SharedTextMarker = SharedTextMarker
9049
+ CodeMirror.TextMarker = TextMarker
9050
+ CodeMirror.LineWidget = LineWidget
9051
+ CodeMirror.e_preventDefault = e_preventDefault
9052
+ CodeMirror.e_stopPropagation = e_stopPropagation
9053
+ CodeMirror.e_stop = e_stop
9054
+ CodeMirror.addClass = addClass
9055
+ CodeMirror.contains = contains
9056
+ CodeMirror.rmClass = rmClass
9057
+ CodeMirror.keyNames = keyNames
9058
+ }
9059
+
9060
+ // EDITOR CONSTRUCTOR
9061
+
9062
+ defineOptions(CodeMirror$1)
9063
+
9064
+ addEditorMethods(CodeMirror$1)
9065
+
9066
+ // Set up methods on CodeMirror's prototype to redirect to the editor's document.
9067
+ var dontDelegate = "iter insert remove copy getEditor constructor".split(" ")
9068
+ for (var prop in Doc.prototype) { if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0)
9069
+ { CodeMirror$1.prototype[prop] = (function(method) {
9070
+ return function() {return method.apply(this.doc, arguments)}
9071
+ })(Doc.prototype[prop]) } }
9072
+
9073
+ eventMixin(Doc)
9074
+
9075
+ // INPUT HANDLING
9076
+
9077
+ CodeMirror$1.inputStyles = {"textarea": TextareaInput, "contenteditable": ContentEditableInput}
9078
+
9079
+ // MODE DEFINITION AND QUERYING
9080
+
9081
+ // Extra arguments are stored as the mode's dependencies, which is
9082
+ // used by (legacy) mechanisms like loadmode.js to automatically
9083
+ // load a mode. (Preferred mechanism is the require/define calls.)
9084
+ CodeMirror$1.defineMode = function(name/*, mode, …*/) {
9085
+ if (!CodeMirror$1.defaults.mode && name != "null") { CodeMirror$1.defaults.mode = name }
9086
+ defineMode.apply(this, arguments)
9087
+ }
9088
+
9089
+ CodeMirror$1.defineMIME = defineMIME
9090
+
9091
+ // Minimal default mode.
9092
+ CodeMirror$1.defineMode("null", function () { return ({token: function (stream) { return stream.skipToEnd(); }}); })
9093
+ CodeMirror$1.defineMIME("text/plain", "null")
9094
+
9095
+ // EXTENSIONS
9096
+
9097
+ CodeMirror$1.defineExtension = function (name, func) {
9098
+ CodeMirror$1.prototype[name] = func
9099
+ }
9100
+ CodeMirror$1.defineDocExtension = function (name, func) {
9101
+ Doc.prototype[name] = func
9102
+ }
9103
+
9104
+ CodeMirror$1.fromTextArea = fromTextArea
9105
+
9106
+ addLegacyProps(CodeMirror$1)
9107
+
9108
+ CodeMirror$1.version = "5.22.0"
9109
+
9110
+ return CodeMirror$1;
9111
+
9112
+ })));
readme.txt CHANGED
@@ -3,7 +3,7 @@ Contributors: maxfoundry, emartin24, AlanP57
3
  Tags: paginate, pagination, navigation, page, wp-paginate, comments, rtl, seo, usability
4
  Requires at least: 2.6.0 (2.7.0 for comments pagination)
5
  Tested up to: 4.7
6
- Stable tag: 1.3.2
7
  License: GPLv2 or later
8
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
9
 
@@ -16,6 +16,8 @@ WP-Paginate is a simple and flexible pagination plugin which provides users with
16
 
17
  In addition to increasing the user experience for your visitors, it has also been widely reported that pagination increases the SEO of your site by providing more links to your content.
18
 
 
 
19
  Starting in version 1.1, WP-Paginate can also be used to paginate post comments!
20
 
21
  Translations: http://plugins.svn.wordpress.org/wp-paginate/I18n (check the version number for the correct file)
@@ -154,7 +156,11 @@ Example (also applies to `wp_paginate_comments()`):
154
 
155
  = How can I style the comments pagination differently than the posts pagination? =
156
 
157
- When calling `wp_paginate_comments()`, WP-Paginate adds an extra class to the `ol` element, `wp-paginate-comments`.== Changelog ==
 
 
 
 
158
 
159
  = 1.3.2 =
160
  * Tested with WordPress 4.7
3
  Tags: paginate, pagination, navigation, page, wp-paginate, comments, rtl, seo, usability
4
  Requires at least: 2.6.0 (2.7.0 for comments pagination)
5
  Tested up to: 4.7
6
+ Stable tag: 1.3.3
7
  License: GPLv2 or later
8
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
9
 
16
 
17
  In addition to increasing the user experience for your visitors, it has also been widely reported that pagination increases the SEO of your site by providing more links to your content.
18
 
19
+ You can add custom CSS for your pagination links with the Custom CSS tab in WP-Paginate Settings.
20
+
21
  Starting in version 1.1, WP-Paginate can also be used to paginate post comments!
22
 
23
  Translations: http://plugins.svn.wordpress.org/wp-paginate/I18n (check the version number for the correct file)
156
 
157
  = How can I style the comments pagination differently than the posts pagination? =
158
 
159
+ When calling `wp_paginate_comments()`, WP-Paginate adds an extra class to the `ol` element, `wp-paginate-comments`.
160
+
161
+ == Changelog ==
162
+ = 1.3.3 =
163
+ * Added settings tab for entering custom CSS code
164
 
165
  = 1.3.2 =
166
  * Tested with WordPress 4.7
wp-paginate.php CHANGED
@@ -3,7 +3,7 @@
3
  Plugin Name: WP-Paginate
4
  Plugin URI: https://wordpress.org/plugins/wp-paginate/
5
  Description: A simple and flexible pagination plugin for WordPress posts and comments.
6
- Version: 1.3.2
7
  Author: Max Foundry
8
  Author URI: http://maxfoundry.com
9
  */
@@ -91,11 +91,30 @@ if (!class_exists('WPPaginate')) {
91
 
92
  //Actions
93
  add_action('admin_menu', array(&$this, 'admin_menu_link'));
 
 
94
 
95
  if ($this->options['css'])
96
  add_action('wp_print_styles', array(&$this, 'wp_paginate_css'));
97
  }
98
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99
  /**
100
  * Pagination based on options/args
101
  */
@@ -299,7 +318,14 @@ if (!class_exists('WPPaginate')) {
299
 
300
  <div class="wrap">
301
  <div class="icon32" id="icon-options-general"><br/></div>
302
- <h2>WP-Paginate</h2>
 
 
 
 
 
 
 
303
  <form method="post" id="wp_paginate_options">
304
  <?php wp_nonce_field('wp-paginate-update-options'); ?>
305
  <table class="form-table">
@@ -374,9 +400,42 @@ if (!class_exists('WPPaginate')) {
374
  </tr>
375
  </table>
376
  <p class="submit">
377
- <input type="submit" value="Save Changes" name="wp_paginate_save" class="button-primary" />
378
  </p>
379
  </form>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
380
  <h2><?php _e('Need Support?', $this->localizationDomain); ?></h2>
381
  <p><?php printf(__('For questions, issues or feature requests, please post them in the %s and make sure to tag the post with wp-paginate.', $this->localizationDomain), '<a href="https://wordpress.org/support/plugin/wp-paginate">WordPress Forum</a>'); ?></p>
382
  <h2><?php _e('Want To Contribute?', $this->localizationDomain); ?></h2>
@@ -396,7 +455,136 @@ if (!class_exists('WPPaginate')) {
396
  </div>
397
 
398
  <?php
399
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
400
  }
401
  }
402
 
@@ -423,6 +611,7 @@ function wp_paginate_comments($args = false) {
423
  return $wp_paginate->paginate($args);
424
  }
425
 
 
426
  /*
427
  * The format of this plugin is based on the following plugin template:
428
  * http://pressography.com/plugins/wordpress-plugin-template/
3
  Plugin Name: WP-Paginate
4
  Plugin URI: https://wordpress.org/plugins/wp-paginate/
5
  Description: A simple and flexible pagination plugin for WordPress posts and comments.
6
+ Version: 1.3.3
7
  Author: Max Foundry
8
  Author URI: http://maxfoundry.com
9
  */
91
 
92
  //Actions
93
  add_action('admin_menu', array(&$this, 'admin_menu_link'));
94
+ add_action('admin_enqueue_scripts', array($this, 'wpp_admin_head'));
95
+ add_action('wp_enqueue_scripts', array($this, 'wpp_enqueue_custom_css'), 20);
96
 
97
  if ($this->options['css'])
98
  add_action('wp_print_styles', array(&$this, 'wp_paginate_css'));
99
  }
100
+
101
+ function wpp_admin_head($hook) {
102
+ //error_log("wpp_admin_head $hook " . $_REQUEST['page'] . " " . $_GET['action']);
103
+ if ( isset( $_REQUEST['page'] ) && 'wp-paginate.php' == $_REQUEST['page'] ) {
104
+ if ( isset( $_GET['action'] ) && 'custom_css' == $_GET['action'] )
105
+ $this->wpp_include_codemirror();
106
+ }
107
+ }
108
+
109
+ function wpp_enqueue_custom_css() {
110
+ $wpp_custom_css_active = get_option('wpp_custom_css_active', 'off');
111
+ if($wpp_custom_css_active === 'on') {
112
+ $upload_dir = wp_upload_dir();
113
+ $wpp_css_file = $upload_dir['baseurl'] . '/wpp-custom-code/wpp-custom-code.css';
114
+ wp_enqueue_style( 'wpp-custom-style', $wpp_css_file, array('wp-paginate') );
115
+ }
116
+ }
117
+
118
  /**
119
  * Pagination based on options/args
120
  */
318
 
319
  <div class="wrap">
320
  <div class="icon32" id="icon-options-general"><br/></div>
321
+ <h1>WP-Paginate</h1>
322
+ <h2 class="nav-tab-wrapper">
323
+ <a class="nav-tab<?php if ( ! isset( $_GET['action'] ) ) echo ' nav-tab-active'; ?>" href="options-general.php?page=wp-paginate.php"><?php _e( 'Settings', $this->localizationDomain ); ?></a>
324
+ <a class="nav-tab <?php if ( isset( $_GET['action'] ) && 'custom_css' == $_GET['action'] ) echo ' nav-tab-active'; ?>" href="options-general.php?page=wp-paginate.php&amp;action=custom_css"><?php _e( 'Custom CSS', $this->localizationDomain ); ?></a>
325
+ </h2>
326
+
327
+ <?php if ( ! isset( $_GET['action'] ) || $_GET['action'] == 'appearance' ) { ?>
328
+
329
  <form method="post" id="wp_paginate_options">
330
  <?php wp_nonce_field('wp-paginate-update-options'); ?>
331
  <table class="form-table">
400
  </tr>
401
  </table>
402
  <p class="submit">
403
+ <input type="submit" id="wpp-save-changes" value="Save Changes" name="wp_paginate_save" class="button-primary" />
404
  </p>
405
  </form>
406
+ <script>
407
+ jQuery(document).ready(function(){
408
+
409
+ jQuery("#wpp-save-changes").click(function(){
410
+ unsaved = false;
411
+ });
412
+
413
+ var unsaved = false;
414
+
415
+ jQuery(":input").change(function(){ //trigers change in all input fields including text type
416
+ unsaved = true;
417
+ });
418
+
419
+ function unloadPage(){
420
+ if(unsaved){
421
+ return "Do you want to leave this page and discard your changes or stay on this page?";
422
+ }
423
+ }
424
+
425
+ window.onbeforeunload = unloadPage;
426
+
427
+ });
428
+
429
+
430
+ </script>
431
+
432
+
433
+
434
+ <?php } elseif ( 'custom_css' == $_GET['action'] ) { ?>
435
+ <h2>Custom CSS</h2>
436
+ <?php $this->wpp_custom_code_tab(); ?>
437
+ <?php } ?>
438
+
439
  <h2><?php _e('Need Support?', $this->localizationDomain); ?></h2>
440
  <p><?php printf(__('For questions, issues or feature requests, please post them in the %s and make sure to tag the post with wp-paginate.', $this->localizationDomain), '<a href="https://wordpress.org/support/plugin/wp-paginate">WordPress Forum</a>'); ?></p>
441
  <h2><?php _e('Want To Contribute?', $this->localizationDomain); ?></h2>
455
  </div>
456
 
457
  <?php
458
+ }
459
+
460
+ function wpp_include_codemirror() {
461
+ wp_enqueue_style( 'codemirror.css', plugins_url( 'lib/codemirror.css', __FILE__ ) );
462
+ wp_enqueue_script( 'codemirror.js', plugins_url( 'lib/codemirror.js', __FILE__ ), array( 'jquery' ) );
463
+ }
464
+
465
+ function wpp_custom_code_tab() {
466
+ if ( ! current_user_can( 'edit_plugins' ) )
467
+ wp_die( __( 'You do not have sufficient permissions to edit plugins for this site.', $this->localizationDomain ) );
468
+
469
+ $message = $content = '';
470
+ $extension = 'css';
471
+ $wpp_custom_css_active = get_option('wpp_custom_css_active', 'off');
472
+
473
+ $upload_dir = wp_upload_dir();
474
+ $folder = $upload_dir['basedir'] . '/wpp-custom-code';
475
+ if ( ! $upload_dir["error"] ) {
476
+ if ( ! is_dir( $folder ) )
477
+ wp_mkdir_p( $folder, 0755 );
478
+
479
+ $index_file = $upload_dir['basedir'] . '/wpp-custom-code/index.php';
480
+ if ( ! file_exists( $index_file ) ) {
481
+ if ( $f = fopen( $index_file, 'w+' ) )
482
+ fclose( $f );
483
+ }
484
+ }
485
+
486
+ $css_file = 'wpp-custom-code.css';
487
+ $real_css_file = $folder . '/' . $css_file;
488
+
489
+ if ( isset( $_REQUEST['wpp_update_custom_code'] ) && check_admin_referer( 'wpp_update_nonce' . $css_file ) ) {
490
+
491
+ /* CSS */
492
+ $newcontent_css = wp_unslash( $_POST['wpp_newcontent_css'] );
493
+ if ( $f = fopen( $real_css_file, 'w+' ) ) {
494
+ fwrite( $f, $newcontent_css );
495
+ fclose( $f );
496
+ $message .= sprintf( __( 'File %s edited successfully.', $this->localizationDomain ), '<i>' . $css_file . '</i>' ) . ' ';
497
+ } else {
498
+ $error .= __( 'Not enough permissions to create or update the file', $this->localizationDomain ) . ' ' . $real_css_file . '. ';
499
+ }
500
+
501
+ if(isset($_REQUEST['wpp_custom_css_active']))
502
+ $wpp_custom_css_active = 'on';
503
+ else
504
+ $wpp_custom_css_active = 'off';
505
+
506
+ update_option('wpp_custom_css_active', $wpp_custom_css_active, true );
507
+
508
+ if ( ! empty( $error ) )
509
+ $error .= ' <a href="https://codex.wordpress.org/Changing_File_Permissions" target="_blank">' . __( 'Learn more', $this->localizationDomain ) . '</a>';
510
+ }
511
+
512
+ if ( file_exists( $real_css_file ) ) {
513
+ update_recently_edited( $real_css_file );
514
+ $content_css = esc_textarea( file_get_contents( $real_css_file ) );
515
+ //$is_css_active = true;
516
+ }
517
+ else
518
+ $content_css = "";
519
+
520
+ if ( ! empty( $message ) ) { ?>
521
+ <div id="message" class="below-h2 updated notice is-dismissible"><p><?php echo $message; ?></p></div>
522
+ <?php } ?>
523
+ <form action="" method="post">
524
+ <p>
525
+ <?php _e( 'These styles will be added to the header on all pages of your site.', $this->localizationDomain ); ?>
526
+ </p>
527
+ <p><big>
528
+ <?php if ( ! file_exists( $real_css_file ) || ( is_writeable( $real_css_file ) ) ) {
529
+ echo __( 'Editing', $this->localizationDomain ) . ' <strong>' . $css_file . '</strong>';
530
+ } else {
531
+ echo __( 'Browsing', $this->localizationDomain ) . ' <strong>' . $css_file . '</strong>';
532
+ } ?>
533
+ </big></p>
534
+ <p><label><input type="checkbox" name="wpp_custom_css_active" value="1" <?php checked( $wpp_custom_css_active, 'on'); ?> /> <?php _e( 'Activate', $this->localizationDomain ); ?></label></p>
535
+ <textarea cols="70" rows="25" name="wpp_newcontent_css" id="wpp_newcontent_css"><?php if ( isset( $content_css ) ) echo $content_css; ?></textarea>
536
+ <?php if ( ! file_exists( $real_css_file ) || is_writeable( $real_css_file )) { ?>
537
+ <p class="submit">
538
+ <input type="hidden" name="wpp_update_custom_code" value="submit" />
539
+ <?php submit_button( __( 'Save Changes', $this->localizationDomain ), 'primary', 'submit', false );
540
+ wp_nonce_field( 'wpp_update_nonce' . $css_file ); ?>
541
+ </p>
542
+ <?php } else { ?>
543
+ <p><em><?php printf( __( 'You need to make this files writable before you can save your changes. See %s the Codex %s for more information.', $this->localizationDomain ),
544
+ '<a href="https://codex.wordpress.org/Changing_File_Permissions" target="_blank">',
545
+ '</a>' ); ?></em></p>
546
+ <?php } ?>
547
+ </form>
548
+ <script>
549
+ jQuery(document).ready(function(){
550
+
551
+ if ( typeof CodeMirror == 'function' ) {
552
+ if ( jQuery( '#wpp_newcontent_css' ).length > 0 ) {
553
+ var editor = CodeMirror.fromTextArea( document.getElementById( 'wpp_newcontent_css' ), {
554
+ mode: "css",
555
+ theme: "default",
556
+ styleActiveLine: true,
557
+ matchBrackets: true,
558
+ lineNumbers: true,
559
+ });
560
+ }
561
+ }
562
+
563
+ jQuery("#submit").click(function(){
564
+ unsaved = false;
565
+ });
566
+
567
+ var unsaved = false;
568
+
569
+ jQuery(":input").change(function(){ //trigers change in all input fields including text type
570
+ unsaved = true;
571
+ });
572
+
573
+ function unloadPage(){
574
+ if(unsaved){
575
+ return "Do you want to leave this page and discard your changes or stay on this page?";
576
+ }
577
+ }
578
+
579
+ window.onbeforeunload = unloadPage;
580
+
581
+ });
582
+
583
+
584
+ </script>
585
+ <?php }
586
+
587
+
588
  }
589
  }
590
 
611
  return $wp_paginate->paginate($args);
612
  }
613
 
614
+
615
  /*
616
  * The format of this plugin is based on the following plugin template:
617
  * http://pressography.com/plugins/wordpress-plugin-template/