Ditty News Ticker - Version 1.2.1

Version Description

  • Modifed to allow multiline text
  • Updated custom css field to css editor
Download this release

Release Info

Developer metaphorcreations
Plugin Icon 128x128 Ditty News Ticker
Version 1.2.1
Comparing to
See all releases

Code changes from version 1.2.0 to 1.2.1

assets/css/codemirror.css ADDED
@@ -0,0 +1,263 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* BASICS */
2
+
3
+ .CodeMirror {
4
+ /* Set height, width, borders, and global font properties here */
5
+ font-family: monospace;
6
+ height: 300px;
7
+ }
8
+ .CodeMirror-scroll {
9
+ /* Set scrolling behaviour here */
10
+ overflow: auto;
11
+ }
12
+
13
+ /* PADDING */
14
+
15
+ .CodeMirror-lines {
16
+ padding: 4px 0; /* Vertical padding around content */
17
+ }
18
+ .CodeMirror pre {
19
+ padding: 0 4px; /* Horizontal padding of content */
20
+ }
21
+
22
+ .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
23
+ background-color: white; /* The little square between H and V scrollbars */
24
+ }
25
+
26
+ /* GUTTER */
27
+
28
+ .CodeMirror-gutters {
29
+ border-right: 1px solid #ddd;
30
+ background-color: #f7f7f7;
31
+ white-space: nowrap;
32
+ }
33
+ .CodeMirror-linenumbers {}
34
+ .CodeMirror-linenumber {
35
+ padding: 0 3px 0 5px;
36
+ min-width: 20px;
37
+ text-align: right;
38
+ color: #999;
39
+ }
40
+
41
+ /* CURSOR */
42
+
43
+ .CodeMirror div.CodeMirror-cursor {
44
+ border-left: 1px solid black;
45
+ z-index: 3;
46
+ }
47
+ /* Shown when moving in bi-directional text */
48
+ .CodeMirror div.CodeMirror-secondarycursor {
49
+ border-left: 1px solid silver;
50
+ }
51
+ .CodeMirror.cm-keymap-fat-cursor div.CodeMirror-cursor {
52
+ width: auto;
53
+ border: 0;
54
+ background: #7e7;
55
+ z-index: 1;
56
+ }
57
+ /* Can style cursor different in overwrite (non-insert) mode */
58
+ .CodeMirror div.CodeMirror-cursor.CodeMirror-overwrite {}
59
+
60
+ .cm-tab { display: inline-block; }
61
+
62
+ /* DEFAULT THEME */
63
+
64
+ .cm-s-default .cm-keyword {color: #708;}
65
+ .cm-s-default .cm-atom {color: #219;}
66
+ .cm-s-default .cm-number {color: #164;}
67
+ .cm-s-default .cm-def {color: #00f;}
68
+ .cm-s-default .cm-variable {color: black;}
69
+ .cm-s-default .cm-variable-2 {color: #05a;}
70
+ .cm-s-default .cm-variable-3 {color: #085;}
71
+ .cm-s-default .cm-property {color: black;}
72
+ .cm-s-default .cm-operator {color: black;}
73
+ .cm-s-default .cm-comment {color: #a50;}
74
+ .cm-s-default .cm-string {color: #a11;}
75
+ .cm-s-default .cm-string-2 {color: #f50;}
76
+ .cm-s-default .cm-meta {color: #555;}
77
+ .cm-s-default .cm-error {color: #f00;}
78
+ .cm-s-default .cm-qualifier {color: #555;}
79
+ .cm-s-default .cm-builtin {color: #30a;}
80
+ .cm-s-default .cm-bracket {color: #997;}
81
+ .cm-s-default .cm-tag {color: #170;}
82
+ .cm-s-default .cm-attribute {color: #00c;}
83
+ .cm-s-default .cm-header {color: blue;}
84
+ .cm-s-default .cm-quote {color: #090;}
85
+ .cm-s-default .cm-hr {color: #999;}
86
+ .cm-s-default .cm-link {color: #00c;}
87
+
88
+ .cm-negative {color: #d44;}
89
+ .cm-positive {color: #292;}
90
+ .cm-header, .cm-strong {font-weight: bold;}
91
+ .cm-em {font-style: italic;}
92
+ .cm-link {text-decoration: underline;}
93
+
94
+ .cm-invalidchar {color: #f00;}
95
+
96
+ div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
97
+ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
98
+ .CodeMirror-activeline-background {background: #e8f2ff;}
99
+
100
+ /* STOP */
101
+
102
+ /* The rest of this file contains styles related to the mechanics of
103
+ the editor. You probably shouldn't touch them. */
104
+
105
+ .CodeMirror {
106
+ line-height: 1;
107
+ position: relative;
108
+ overflow: hidden;
109
+ background: white;
110
+ color: black;
111
+ }
112
+
113
+ .CodeMirror-scroll {
114
+ /* 30px is the magic margin used to hide the element's real scrollbars */
115
+ /* See overflow: hidden in .CodeMirror */
116
+ margin-bottom: -30px; margin-right: -30px;
117
+ padding-bottom: 30px; padding-right: 30px;
118
+ height: 100%;
119
+ outline: none; /* Prevent dragging from highlighting the element */
120
+ position: relative;
121
+ -moz-box-sizing: content-box;
122
+ box-sizing: content-box;
123
+ }
124
+ .CodeMirror-sizer {
125
+ position: relative;
126
+ }
127
+
128
+ /* The fake, visible scrollbars. Used to force redraw during scrolling
129
+ before actuall scrolling happens, thus preventing shaking and
130
+ flickering artifacts. */
131
+ .CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
132
+ position: absolute;
133
+ z-index: 6;
134
+ display: none;
135
+ }
136
+ .CodeMirror-vscrollbar {
137
+ right: 0; top: 0;
138
+ overflow-x: hidden;
139
+ overflow-y: scroll;
140
+ }
141
+ .CodeMirror-hscrollbar {
142
+ bottom: 0; left: 0;
143
+ overflow-y: hidden;
144
+ overflow-x: scroll;
145
+ }
146
+ .CodeMirror-scrollbar-filler {
147
+ right: 0; bottom: 0;
148
+ }
149
+ .CodeMirror-gutter-filler {
150
+ left: 0; bottom: 0;
151
+ }
152
+
153
+ .CodeMirror-gutters {
154
+ position: absolute; left: 0; top: 0;
155
+ padding-bottom: 30px;
156
+ z-index: 3;
157
+ }
158
+ .CodeMirror-gutter {
159
+ white-space: normal;
160
+ height: 100%;
161
+ -moz-box-sizing: content-box;
162
+ box-sizing: content-box;
163
+ padding-bottom: 30px;
164
+ margin-bottom: -32px;
165
+ display: inline-block;
166
+ /* Hack to make IE7 behave */
167
+ *zoom:1;
168
+ *display:inline;
169
+ }
170
+ .CodeMirror-gutter-elt {
171
+ position: absolute;
172
+ cursor: default;
173
+ z-index: 4;
174
+ }
175
+
176
+ .CodeMirror-lines {
177
+ cursor: text;
178
+ }
179
+ .CodeMirror pre {
180
+ /* Reset some styles that the rest of the page might have set */
181
+ -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0;
182
+ border-width: 0;
183
+ background: transparent;
184
+ font-family: inherit;
185
+ font-size: inherit;
186
+ margin: 0;
187
+ white-space: pre;
188
+ word-wrap: normal;
189
+ line-height: inherit;
190
+ color: inherit;
191
+ z-index: 2;
192
+ position: relative;
193
+ overflow: visible;
194
+ }
195
+ .CodeMirror-wrap pre {
196
+ word-wrap: break-word;
197
+ white-space: pre-wrap;
198
+ word-break: normal;
199
+ }
200
+ .CodeMirror-code pre {
201
+ border-right: 30px solid transparent;
202
+ width: -webkit-fit-content;
203
+ width: -moz-fit-content;
204
+ width: fit-content;
205
+ }
206
+ .CodeMirror-wrap .CodeMirror-code pre {
207
+ border-right: none;
208
+ width: auto;
209
+ }
210
+ .CodeMirror-linebackground {
211
+ position: absolute;
212
+ left: 0; right: 0; top: 0; bottom: 0;
213
+ z-index: 0;
214
+ }
215
+
216
+ .CodeMirror-linewidget {
217
+ position: relative;
218
+ z-index: 2;
219
+ overflow: auto;
220
+ }
221
+
222
+ .CodeMirror-widget {}
223
+
224
+ .CodeMirror-wrap .CodeMirror-scroll {
225
+ overflow-x: hidden;
226
+ }
227
+
228
+ .CodeMirror-measure {
229
+ position: absolute;
230
+ width: 100%;
231
+ height: 0;
232
+ overflow: hidden;
233
+ visibility: hidden;
234
+ }
235
+ .CodeMirror-measure pre { position: static; }
236
+
237
+ .CodeMirror div.CodeMirror-cursor {
238
+ position: absolute;
239
+ visibility: hidden;
240
+ border-right: none;
241
+ width: 0;
242
+ }
243
+ .CodeMirror-focused div.CodeMirror-cursor {
244
+ visibility: visible;
245
+ }
246
+
247
+ .CodeMirror-selected { background: #d9d9d9; }
248
+ .CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
249
+
250
+ .cm-searching {
251
+ background: #ffa;
252
+ background: rgba(255, 255, 0, .4);
253
+ }
254
+
255
+ /* IE7 hack to prevent it from returning funny offsetTops on the spans */
256
+ .CodeMirror span { *vertical-align: text-bottom; }
257
+
258
+ @media print {
259
+ /* Hide the cursor when printing */
260
+ .CodeMirror div.CodeMirror-cursor {
261
+ visibility: hidden;
262
+ }
263
+ }
assets/css/style-admin.css CHANGED
@@ -45,4 +45,77 @@
45
 
46
  .mtphr-dnt-metaboxer_mtphr_dnt_ticks-tick .wp-editor-wrap {
47
  margin-bottom: 10px;
48
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
 
46
  .mtphr-dnt-metaboxer_mtphr_dnt_ticks-tick .wp-editor-wrap {
47
  margin-bottom: 10px;
48
+ }
49
+
50
+ /* --------------------------------------------------------- */
51
+ /* !Settings pages */
52
+ /* --------------------------------------------------------- */
53
+
54
+ .ditty_news_ticker_page_mtphr_dnt_settings .form-table {
55
+ max-width: 1000px;
56
+ border: 1px solid #dfdfdf;
57
+ margin-top: 0;
58
+ margin-bottom: 10px;
59
+ -webkit-border-radius: 5px;
60
+ -moz-border-radius: 5px;
61
+ border-radius: 5px;
62
+ }
63
+ .ditty_news_ticker_page_mtphr_dnt_settings .form-table > tbody > tr > th {
64
+ background: #fefefe;
65
+ border-right: 1px solid #dfdfdf;
66
+ border-bottom: 1px solid #f7f7f7;
67
+ }
68
+ .ditty_news_ticker_page_mtphr_dnt_settings .form-table > tbody > tr > td {
69
+ border-bottom: 1px solid #f7f7f7;
70
+ }
71
+ .ditty_news_ticker_page_mtphr_dnt_settings .form-table > tbody > tr:last-child th,
72
+ .ditty_news_ticker_page_mtphr_dnt_settings .form-table > tbody > tr:last-child td {
73
+ border-bottom: none;
74
+ }
75
+ .ditty_news_ticker_page_mtphr_dnt_settings #wpbody-content h3 {
76
+ -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box;
77
+ max-width: 1000px;
78
+ background: #fafafa;
79
+ border: 1px solid #dfdfdf;
80
+ border-bottom-width: 0;
81
+ padding: 10px;
82
+ margin-bottom: 0;
83
+
84
+ -webkit-border-top-left-radius: 5px;
85
+ -webkit-border-top-right-radius: 5px;
86
+ -moz-border-radius-topleft: 5px;
87
+ -moz-border-radius-topright: 5px;
88
+ border-top-left-radius: 5px;
89
+ border-top-right-radius: 5px;
90
+ }
91
+ .ditty_news_ticker_page_mtphr_dnt_settings #wpbody-content h3 input {
92
+ float: right;
93
+ font-weight: normal;
94
+ margin-top: -4px;
95
+ }
96
+ .ditty_news_ticker_page_mtphr_dnt_settings #wpbody-content h3 + p {
97
+ -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box;
98
+ max-width: 1000px;
99
+ background: #fafafa;
100
+ border: 1px solid #dfdfdf;
101
+ border-top-width: 0;
102
+ border-bottom-width: 0;
103
+ padding: 0 10px 10px;
104
+ margin: 0;
105
+ }
106
+ .ditty_news_ticker_page_mtphr_dnt_settings textarea {
107
+ width: 100%;
108
+ }
109
+
110
+
111
+ /* --------------------------------------------------------- */
112
+ /* !CodeMirror */
113
+ /* --------------------------------------------------------- */
114
+
115
+ .mtphr-dnt-custom-css {
116
+ border: 1px solid #dfdfdf;
117
+ -webkit-border-radius: 3px;
118
+ -moz-border-radius: 3px;
119
+ border-radius: 3px;
120
+ overflow: hidden;
121
+ }
assets/js/codemirror.js ADDED
@@ -0,0 +1,5887 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // CodeMirror version 3.18
2
+ //
3
+ // CodeMirror is the only global var we claim
4
+ window.CodeMirror = (function() {
5
+ "use strict";
6
+
7
+ // BROWSER SNIFFING
8
+
9
+ // Crude, but necessary to handle a number of hard-to-feature-detect
10
+ // bugs and behavior differences.
11
+ var gecko = /gecko\/\d/i.test(navigator.userAgent);
12
+ var ie = /MSIE \d/.test(navigator.userAgent);
13
+ var ie_lt8 = ie && (document.documentMode == null || document.documentMode < 8);
14
+ var ie_lt9 = ie && (document.documentMode == null || document.documentMode < 9);
15
+ var webkit = /WebKit\//.test(navigator.userAgent);
16
+ var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(navigator.userAgent);
17
+ var chrome = /Chrome\//.test(navigator.userAgent);
18
+ var opera = /Opera\//.test(navigator.userAgent);
19
+ var safari = /Apple Computer/.test(navigator.vendor);
20
+ var khtml = /KHTML\//.test(navigator.userAgent);
21
+ var mac_geLion = /Mac OS X 1\d\D([7-9]|\d\d)\D/.test(navigator.userAgent);
22
+ var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(navigator.userAgent);
23
+ var phantom = /PhantomJS/.test(navigator.userAgent);
24
+
25
+ var ios = /AppleWebKit/.test(navigator.userAgent) && /Mobile\/\w+/.test(navigator.userAgent);
26
+ // This is woefully incomplete. Suggestions for alternative methods welcome.
27
+ var mobile = ios || /Android|webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(navigator.userAgent);
28
+ var mac = ios || /Mac/.test(navigator.platform);
29
+ var windows = /win/i.test(navigator.platform);
30
+
31
+ var opera_version = opera && navigator.userAgent.match(/Version\/(\d*\.\d*)/);
32
+ if (opera_version) opera_version = Number(opera_version[1]);
33
+ if (opera_version && opera_version >= 15) { opera = false; webkit = true; }
34
+ // Some browsers use the wrong event properties to signal cmd/ctrl on OS X
35
+ var flipCtrlCmd = mac && (qtwebkit || opera && (opera_version == null || opera_version < 12.11));
36
+ var captureMiddleClick = gecko || (ie && !ie_lt9);
37
+
38
+ // Optimize some code when these features are not used
39
+ var sawReadOnlySpans = false, sawCollapsedSpans = false;
40
+
41
+ // CONSTRUCTOR
42
+
43
+ function CodeMirror(place, options) {
44
+ if (!(this instanceof CodeMirror)) return new CodeMirror(place, options);
45
+
46
+ this.options = options = options || {};
47
+ // Determine effective options based on given values and defaults.
48
+ for (var opt in defaults) if (!options.hasOwnProperty(opt) && defaults.hasOwnProperty(opt))
49
+ options[opt] = defaults[opt];
50
+ setGuttersForLineNumbers(options);
51
+
52
+ var docStart = typeof options.value == "string" ? 0 : options.value.first;
53
+ var display = this.display = makeDisplay(place, docStart);
54
+ display.wrapper.CodeMirror = this;
55
+ updateGutters(this);
56
+ if (options.autofocus && !mobile) focusInput(this);
57
+
58
+ this.state = {keyMaps: [],
59
+ overlays: [],
60
+ modeGen: 0,
61
+ overwrite: false, focused: false,
62
+ suppressEdits: false, pasteIncoming: false,
63
+ draggingText: false,
64
+ highlight: new Delayed()};
65
+
66
+ themeChanged(this);
67
+ if (options.lineWrapping)
68
+ this.display.wrapper.className += " CodeMirror-wrap";
69
+
70
+ var doc = options.value;
71
+ if (typeof doc == "string") doc = new Doc(options.value, options.mode);
72
+ operation(this, attachDoc)(this, doc);
73
+
74
+ // Override magic textarea content restore that IE sometimes does
75
+ // on our hidden textarea on reload
76
+ if (ie) setTimeout(bind(resetInput, this, true), 20);
77
+
78
+ registerEventHandlers(this);
79
+ // IE throws unspecified error in certain cases, when
80
+ // trying to access activeElement before onload
81
+ var hasFocus; try { hasFocus = (document.activeElement == display.input); } catch(e) { }
82
+ if (hasFocus || (options.autofocus && !mobile)) setTimeout(bind(onFocus, this), 20);
83
+ else onBlur(this);
84
+
85
+ operation(this, function() {
86
+ for (var opt in optionHandlers)
87
+ if (optionHandlers.propertyIsEnumerable(opt))
88
+ optionHandlers[opt](this, options[opt], Init);
89
+ for (var i = 0; i < initHooks.length; ++i) initHooks[i](this);
90
+ })();
91
+ }
92
+
93
+ // DISPLAY CONSTRUCTOR
94
+
95
+ function makeDisplay(place, docStart) {
96
+ var d = {};
97
+
98
+ var input = d.input = elt("textarea", null, null, "position: absolute; padding: 0; width: 1px; height: 1em; outline: none; font-size: 4px;");
99
+ if (webkit) input.style.width = "1000px";
100
+ else input.setAttribute("wrap", "off");
101
+ // if border: 0; -- iOS fails to open keyboard (issue #1287)
102
+ if (ios) input.style.border = "1px solid black";
103
+ input.setAttribute("autocorrect", "off"); input.setAttribute("autocapitalize", "off"); input.setAttribute("spellcheck", "false");
104
+
105
+ // Wraps and hides input textarea
106
+ d.inputDiv = elt("div", [input], null, "overflow: hidden; position: relative; width: 3px; height: 0px;");
107
+ // The actual fake scrollbars.
108
+ d.scrollbarH = elt("div", [elt("div", null, null, "height: 1px")], "CodeMirror-hscrollbar");
109
+ d.scrollbarV = elt("div", [elt("div", null, null, "width: 1px")], "CodeMirror-vscrollbar");
110
+ d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler");
111
+ d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler");
112
+ // DIVs containing the selection and the actual code
113
+ d.lineDiv = elt("div", null, "CodeMirror-code");
114
+ d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1");
115
+ // Blinky cursor, and element used to ensure cursor fits at the end of a line
116
+ d.cursor = elt("div", "\u00a0", "CodeMirror-cursor");
117
+ // Secondary cursor, shown when on a 'jump' in bi-directional text
118
+ d.otherCursor = elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor");
119
+ // Used to measure text size
120
+ d.measure = elt("div", null, "CodeMirror-measure");
121
+ // Wraps everything that needs to exist inside the vertically-padded coordinate system
122
+ d.lineSpace = elt("div", [d.measure, d.selectionDiv, d.lineDiv, d.cursor, d.otherCursor],
123
+ null, "position: relative; outline: none");
124
+ // Moved around its parent to cover visible view
125
+ d.mover = elt("div", [elt("div", [d.lineSpace], "CodeMirror-lines")], null, "position: relative");
126
+ // Set to the height of the text, causes scrolling
127
+ d.sizer = elt("div", [d.mover], "CodeMirror-sizer");
128
+ // D is needed because behavior of elts with overflow: auto and padding is inconsistent across browsers
129
+ d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerCutOff + "px; width: 1px;");
130
+ // Will contain the gutters, if any
131
+ d.gutters = elt("div", null, "CodeMirror-gutters");
132
+ d.lineGutter = null;
133
+ // Provides scrolling
134
+ d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-scroll");
135
+ d.scroller.setAttribute("tabIndex", "-1");
136
+ // The element in which the editor lives.
137
+ d.wrapper = elt("div", [d.inputDiv, d.scrollbarH, d.scrollbarV,
138
+ d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror");
139
+ // Work around IE7 z-index bug
140
+ if (ie_lt8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; }
141
+ if (place.appendChild) place.appendChild(d.wrapper); else place(d.wrapper);
142
+
143
+ // Needed to hide big blue blinking cursor on Mobile Safari
144
+ if (ios) input.style.width = "0px";
145
+ if (!webkit) d.scroller.draggable = true;
146
+ // Needed to handle Tab key in KHTML
147
+ if (khtml) { d.inputDiv.style.height = "1px"; d.inputDiv.style.position = "absolute"; }
148
+ // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
149
+ else if (ie_lt8) d.scrollbarH.style.minWidth = d.scrollbarV.style.minWidth = "18px";
150
+
151
+ // Current visible range (may be bigger than the view window).
152
+ d.viewOffset = d.lastSizeC = 0;
153
+ d.showingFrom = d.showingTo = docStart;
154
+
155
+ // Used to only resize the line number gutter when necessary (when
156
+ // the amount of lines crosses a boundary that makes its width change)
157
+ d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null;
158
+ // See readInput and resetInput
159
+ d.prevInput = "";
160
+ // Set to true when a non-horizontal-scrolling widget is added. As
161
+ // an optimization, widget aligning is skipped when d is false.
162
+ d.alignWidgets = false;
163
+ // Flag that indicates whether we currently expect input to appear
164
+ // (after some event like 'keypress' or 'input') and are polling
165
+ // intensively.
166
+ d.pollingFast = false;
167
+ // Self-resetting timeout for the poller
168
+ d.poll = new Delayed();
169
+
170
+ d.cachedCharWidth = d.cachedTextHeight = null;
171
+ d.measureLineCache = [];
172
+ d.measureLineCachePos = 0;
173
+
174
+ // Tracks when resetInput has punted to just putting a short
175
+ // string instead of the (large) selection.
176
+ d.inaccurateSelection = false;
177
+
178
+ // Tracks the maximum line length so that the horizontal scrollbar
179
+ // can be kept static when scrolling.
180
+ d.maxLine = null;
181
+ d.maxLineLength = 0;
182
+ d.maxLineChanged = false;
183
+
184
+ // Used for measuring wheel scrolling granularity
185
+ d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null;
186
+
187
+ return d;
188
+ }
189
+
190
+ // STATE UPDATES
191
+
192
+ // Used to get the editor into a consistent state again when options change.
193
+
194
+ function loadMode(cm) {
195
+ cm.doc.mode = CodeMirror.getMode(cm.options, cm.doc.modeOption);
196
+ cm.doc.iter(function(line) {
197
+ if (line.stateAfter) line.stateAfter = null;
198
+ if (line.styles) line.styles = null;
199
+ });
200
+ cm.doc.frontier = cm.doc.first;
201
+ startWorker(cm, 100);
202
+ cm.state.modeGen++;
203
+ if (cm.curOp) regChange(cm);
204
+ }
205
+
206
+ function wrappingChanged(cm) {
207
+ if (cm.options.lineWrapping) {
208
+ cm.display.wrapper.className += " CodeMirror-wrap";
209
+ cm.display.sizer.style.minWidth = "";
210
+ } else {
211
+ cm.display.wrapper.className = cm.display.wrapper.className.replace(" CodeMirror-wrap", "");
212
+ computeMaxLength(cm);
213
+ }
214
+ estimateLineHeights(cm);
215
+ regChange(cm);
216
+ clearCaches(cm);
217
+ setTimeout(function(){updateScrollbars(cm);}, 100);
218
+ }
219
+
220
+ function estimateHeight(cm) {
221
+ var th = textHeight(cm.display), wrapping = cm.options.lineWrapping;
222
+ var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3);
223
+ return function(line) {
224
+ if (lineIsHidden(cm.doc, line))
225
+ return 0;
226
+ else if (wrapping)
227
+ return (Math.ceil(line.text.length / perLine) || 1) * th;
228
+ else
229
+ return th;
230
+ };
231
+ }
232
+
233
+ function estimateLineHeights(cm) {
234
+ var doc = cm.doc, est = estimateHeight(cm);
235
+ doc.iter(function(line) {
236
+ var estHeight = est(line);
237
+ if (estHeight != line.height) updateLineHeight(line, estHeight);
238
+ });
239
+ }
240
+
241
+ function keyMapChanged(cm) {
242
+ var map = keyMap[cm.options.keyMap], style = map.style;
243
+ cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-keymap-\S+/g, "") +
244
+ (style ? " cm-keymap-" + style : "");
245
+ cm.state.disableInput = map.disableInput;
246
+ }
247
+
248
+ function themeChanged(cm) {
249
+ cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") +
250
+ cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-");
251
+ clearCaches(cm);
252
+ }
253
+
254
+ function guttersChanged(cm) {
255
+ updateGutters(cm);
256
+ regChange(cm);
257
+ setTimeout(function(){alignHorizontally(cm);}, 20);
258
+ }
259
+
260
+ function updateGutters(cm) {
261
+ var gutters = cm.display.gutters, specs = cm.options.gutters;
262
+ removeChildren(gutters);
263
+ for (var i = 0; i < specs.length; ++i) {
264
+ var gutterClass = specs[i];
265
+ var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + gutterClass));
266
+ if (gutterClass == "CodeMirror-linenumbers") {
267
+ cm.display.lineGutter = gElt;
268
+ gElt.style.width = (cm.display.lineNumWidth || 1) + "px";
269
+ }
270
+ }
271
+ gutters.style.display = i ? "" : "none";
272
+ }
273
+
274
+ function lineLength(doc, line) {
275
+ if (line.height == 0) return 0;
276
+ var len = line.text.length, merged, cur = line;
277
+ while (merged = collapsedSpanAtStart(cur)) {
278
+ var found = merged.find();
279
+ cur = getLine(doc, found.from.line);
280
+ len += found.from.ch - found.to.ch;
281
+ }
282
+ cur = line;
283
+ while (merged = collapsedSpanAtEnd(cur)) {
284
+ var found = merged.find();
285
+ len -= cur.text.length - found.from.ch;
286
+ cur = getLine(doc, found.to.line);
287
+ len += cur.text.length - found.to.ch;
288
+ }
289
+ return len;
290
+ }
291
+
292
+ function computeMaxLength(cm) {
293
+ var d = cm.display, doc = cm.doc;
294
+ d.maxLine = getLine(doc, doc.first);
295
+ d.maxLineLength = lineLength(doc, d.maxLine);
296
+ d.maxLineChanged = true;
297
+ doc.iter(function(line) {
298
+ var len = lineLength(doc, line);
299
+ if (len > d.maxLineLength) {
300
+ d.maxLineLength = len;
301
+ d.maxLine = line;
302
+ }
303
+ });
304
+ }
305
+
306
+ // Make sure the gutters options contains the element
307
+ // "CodeMirror-linenumbers" when the lineNumbers option is true.
308
+ function setGuttersForLineNumbers(options) {
309
+ var found = indexOf(options.gutters, "CodeMirror-linenumbers");
310
+ if (found == -1 && options.lineNumbers) {
311
+ options.gutters = options.gutters.concat(["CodeMirror-linenumbers"]);
312
+ } else if (found > -1 && !options.lineNumbers) {
313
+ options.gutters = options.gutters.slice(0);
314
+ options.gutters.splice(found, 1);
315
+ }
316
+ }
317
+
318
+ // SCROLLBARS
319
+
320
+ // Re-synchronize the fake scrollbars with the actual size of the
321
+ // content. Optionally force a scrollTop.
322
+ function updateScrollbars(cm) {
323
+ var d = cm.display, docHeight = cm.doc.height;
324
+ var totalHeight = docHeight + paddingVert(d);
325
+ d.sizer.style.minHeight = d.heightForcer.style.top = totalHeight + "px";
326
+ d.gutters.style.height = Math.max(totalHeight, d.scroller.clientHeight - scrollerCutOff) + "px";
327
+ var scrollHeight = Math.max(totalHeight, d.scroller.scrollHeight);
328
+ var needsH = d.scroller.scrollWidth > (d.scroller.clientWidth + 1);
329
+ var needsV = scrollHeight > (d.scroller.clientHeight + 1);
330
+ if (needsV) {
331
+ d.scrollbarV.style.display = "block";
332
+ d.scrollbarV.style.bottom = needsH ? scrollbarWidth(d.measure) + "px" : "0";
333
+ d.scrollbarV.firstChild.style.height =
334
+ (scrollHeight - d.scroller.clientHeight + d.scrollbarV.clientHeight) + "px";
335
+ } else {
336
+ d.scrollbarV.style.display = "";
337
+ d.scrollbarV.firstChild.style.height = "0";
338
+ }
339
+ if (needsH) {
340
+ d.scrollbarH.style.display = "block";
341
+ d.scrollbarH.style.right = needsV ? scrollbarWidth(d.measure) + "px" : "0";
342
+ d.scrollbarH.firstChild.style.width =
343
+ (d.scroller.scrollWidth - d.scroller.clientWidth + d.scrollbarH.clientWidth) + "px";
344
+ } else {
345
+ d.scrollbarH.style.display = "";
346
+ d.scrollbarH.firstChild.style.width = "0";
347
+ }
348
+ if (needsH && needsV) {
349
+ d.scrollbarFiller.style.display = "block";
350
+ d.scrollbarFiller.style.height = d.scrollbarFiller.style.width = scrollbarWidth(d.measure) + "px";
351
+ } else d.scrollbarFiller.style.display = "";
352
+ if (needsH && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) {
353
+ d.gutterFiller.style.display = "block";
354
+ d.gutterFiller.style.height = scrollbarWidth(d.measure) + "px";
355
+ d.gutterFiller.style.width = d.gutters.offsetWidth + "px";
356
+ } else d.gutterFiller.style.display = "";
357
+
358
+ if (mac_geLion && scrollbarWidth(d.measure) === 0)
359
+ d.scrollbarV.style.minWidth = d.scrollbarH.style.minHeight = mac_geMountainLion ? "18px" : "12px";
360
+ }
361
+
362
+ function visibleLines(display, doc, viewPort) {
363
+ var top = display.scroller.scrollTop, height = display.wrapper.clientHeight;
364
+ if (typeof viewPort == "number") top = viewPort;
365
+ else if (viewPort) {top = viewPort.top; height = viewPort.bottom - viewPort.top;}
366
+ top = Math.floor(top - paddingTop(display));
367
+ var bottom = Math.ceil(top + height);
368
+ return {from: lineAtHeight(doc, top), to: lineAtHeight(doc, bottom)};
369
+ }
370
+
371
+ // LINE NUMBERS
372
+
373
+ function alignHorizontally(cm) {
374
+ var display = cm.display;
375
+ if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) return;
376
+ var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft;
377
+ var gutterW = display.gutters.offsetWidth, l = comp + "px";
378
+ for (var n = display.lineDiv.firstChild; n; n = n.nextSibling) if (n.alignable) {
379
+ for (var i = 0, a = n.alignable; i < a.length; ++i) a[i].style.left = l;
380
+ }
381
+ if (cm.options.fixedGutter)
382
+ display.gutters.style.left = (comp + gutterW) + "px";
383
+ }
384
+
385
+ function maybeUpdateLineNumberWidth(cm) {
386
+ if (!cm.options.lineNumbers) return false;
387
+ var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display;
388
+ if (last.length != display.lineNumChars) {
389
+ var test = display.measure.appendChild(elt("div", [elt("div", last)],
390
+ "CodeMirror-linenumber CodeMirror-gutter-elt"));
391
+ var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW;
392
+ display.lineGutter.style.width = "";
393
+ display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding);
394
+ display.lineNumWidth = display.lineNumInnerWidth + padding;
395
+ display.lineNumChars = display.lineNumInnerWidth ? last.length : -1;
396
+ display.lineGutter.style.width = display.lineNumWidth + "px";
397
+ return true;
398
+ }
399
+ return false;
400
+ }
401
+
402
+ function lineNumberFor(options, i) {
403
+ return String(options.lineNumberFormatter(i + options.firstLineNumber));
404
+ }
405
+ function compensateForHScroll(display) {
406
+ return getRect(display.scroller).left - getRect(display.sizer).left;
407
+ }
408
+
409
+ // DISPLAY DRAWING
410
+
411
+ function updateDisplay(cm, changes, viewPort, forced) {
412
+ var oldFrom = cm.display.showingFrom, oldTo = cm.display.showingTo, updated;
413
+ var visible = visibleLines(cm.display, cm.doc, viewPort);
414
+ for (var first = true;; first = false) {
415
+ var oldWidth = cm.display.scroller.clientWidth;
416
+ if (!updateDisplayInner(cm, changes, visible, forced)) break;
417
+ updated = true;
418
+ changes = [];
419
+ updateSelection(cm);
420
+ updateScrollbars(cm);
421
+ if (first && cm.options.lineWrapping && oldWidth != cm.display.scroller.clientWidth) {
422
+ forced = true;
423
+ continue;
424
+ }
425
+ forced = false;
426
+
427
+ // Clip forced viewport to actual scrollable area
428
+ if (viewPort)
429
+ viewPort = Math.min(cm.display.scroller.scrollHeight - cm.display.scroller.clientHeight,
430
+ typeof viewPort == "number" ? viewPort : viewPort.top);
431
+ visible = visibleLines(cm.display, cm.doc, viewPort);
432
+ if (visible.from >= cm.display.showingFrom && visible.to <= cm.display.showingTo)
433
+ break;
434
+ }
435
+
436
+ if (updated) {
437
+ signalLater(cm, "update", cm);
438
+ if (cm.display.showingFrom != oldFrom || cm.display.showingTo != oldTo)
439
+ signalLater(cm, "viewportChange", cm, cm.display.showingFrom, cm.display.showingTo);
440
+ }
441
+ return updated;
442
+ }
443
+
444
+ // Uses a set of changes plus the current scroll position to
445
+ // determine which DOM updates have to be made, and makes the
446
+ // updates.
447
+ function updateDisplayInner(cm, changes, visible, forced) {
448
+ var display = cm.display, doc = cm.doc;
449
+ if (!display.wrapper.clientWidth) {
450
+ display.showingFrom = display.showingTo = doc.first;
451
+ display.viewOffset = 0;
452
+ return;
453
+ }
454
+
455
+ // Bail out if the visible area is already rendered and nothing changed.
456
+ if (!forced && changes.length == 0 &&
457
+ visible.from > display.showingFrom && visible.to < display.showingTo)
458
+ return;
459
+
460
+ if (maybeUpdateLineNumberWidth(cm))
461
+ changes = [{from: doc.first, to: doc.first + doc.size}];
462
+ var gutterW = display.sizer.style.marginLeft = display.gutters.offsetWidth + "px";
463
+ display.scrollbarH.style.left = cm.options.fixedGutter ? gutterW : "0";
464
+
465
+ // Used to determine which lines need their line numbers updated
466
+ var positionsChangedFrom = Infinity;
467
+ if (cm.options.lineNumbers)
468
+ for (var i = 0; i < changes.length; ++i)
469
+ if (changes[i].diff && changes[i].from < positionsChangedFrom) { positionsChangedFrom = changes[i].from; }
470
+
471
+ var end = doc.first + doc.size;
472
+ var from = Math.max(visible.from - cm.options.viewportMargin, doc.first);
473
+ var to = Math.min(end, visible.to + cm.options.viewportMargin);
474
+ if (display.showingFrom < from && from - display.showingFrom < 20) from = Math.max(doc.first, display.showingFrom);
475
+ if (display.showingTo > to && display.showingTo - to < 20) to = Math.min(end, display.showingTo);
476
+ if (sawCollapsedSpans) {
477
+ from = lineNo(visualLine(doc, getLine(doc, from)));
478
+ while (to < end && lineIsHidden(doc, getLine(doc, to))) ++to;
479
+ }
480
+
481
+ // Create a range of theoretically intact lines, and punch holes
482
+ // in that using the change info.
483
+ var intact = [{from: Math.max(display.showingFrom, doc.first),
484
+ to: Math.min(display.showingTo, end)}];
485
+ if (intact[0].from >= intact[0].to) intact = [];
486
+ else intact = computeIntact(intact, changes);
487
+ // When merged lines are present, we might have to reduce the
488
+ // intact ranges because changes in continued fragments of the
489
+ // intact lines do require the lines to be redrawn.
490
+ if (sawCollapsedSpans)
491
+ for (var i = 0; i < intact.length; ++i) {
492
+ var range = intact[i], merged;
493
+ while (merged = collapsedSpanAtEnd(getLine(doc, range.to - 1))) {
494
+ var newTo = merged.find().from.line;
495
+ if (newTo > range.from) range.to = newTo;
496
+ else { intact.splice(i--, 1); break; }
497
+ }
498
+ }
499
+
500
+ // Clip off the parts that won't be visible
501
+ var intactLines = 0;
502
+ for (var i = 0; i < intact.length; ++i) {
503
+ var range = intact[i];
504
+ if (range.from < from) range.from = from;
505
+ if (range.to > to) range.to = to;
506
+ if (range.from >= range.to) intact.splice(i--, 1);
507
+ else intactLines += range.to - range.from;
508
+ }
509
+ if (!forced && intactLines == to - from && from == display.showingFrom && to == display.showingTo) {
510
+ updateViewOffset(cm);
511
+ return;
512
+ }
513
+ intact.sort(function(a, b) {return a.from - b.from;});
514
+
515
+ // Avoid crashing on IE's "unspecified error" when in iframes
516
+ try {
517
+ var focused = document.activeElement;
518
+ } catch(e) {}
519
+ if (intactLines < (to - from) * .7) display.lineDiv.style.display = "none";
520
+ patchDisplay(cm, from, to, intact, positionsChangedFrom);
521
+ display.lineDiv.style.display = "";
522
+ if (focused && document.activeElement != focused && focused.offsetHeight) focused.focus();
523
+
524
+ var different = from != display.showingFrom || to != display.showingTo ||
525
+ display.lastSizeC != display.wrapper.clientHeight;
526
+ // This is just a bogus formula that detects when the editor is
527
+ // resized or the font size changes.
528
+ if (different) {
529
+ display.lastSizeC = display.wrapper.clientHeight;
530
+ startWorker(cm, 400);
531
+ }
532
+ display.showingFrom = from; display.showingTo = to;
533
+
534
+ updateHeightsInViewport(cm);
535
+ updateViewOffset(cm);
536
+
537
+ return true;
538
+ }
539
+
540
+ function updateHeightsInViewport(cm) {
541
+ var display = cm.display;
542
+ var prevBottom = display.lineDiv.offsetTop;
543
+ for (var node = display.lineDiv.firstChild, height; node; node = node.nextSibling) if (node.lineObj) {
544
+ if (ie_lt8) {
545
+ var bot = node.offsetTop + node.offsetHeight;
546
+ height = bot - prevBottom;
547
+ prevBottom = bot;
548
+ } else {
549
+ var box = getRect(node);
550
+ height = box.bottom - box.top;
551
+ }
552
+ var diff = node.lineObj.height - height;
553
+ if (height < 2) height = textHeight(display);
554
+ if (diff > .001 || diff < -.001) {
555
+ updateLineHeight(node.lineObj, height);
556
+ var widgets = node.lineObj.widgets;
557
+ if (widgets) for (var i = 0; i < widgets.length; ++i)
558
+ widgets[i].height = widgets[i].node.offsetHeight;
559
+ }
560
+ }
561
+ }
562
+
563
+ function updateViewOffset(cm) {
564
+ var off = cm.display.viewOffset = heightAtLine(cm, getLine(cm.doc, cm.display.showingFrom));
565
+ // Position the mover div to align with the current virtual scroll position
566
+ cm.display.mover.style.top = off + "px";
567
+ }
568
+
569
+ function computeIntact(intact, changes) {
570
+ for (var i = 0, l = changes.length || 0; i < l; ++i) {
571
+ var change = changes[i], intact2 = [], diff = change.diff || 0;
572
+ for (var j = 0, l2 = intact.length; j < l2; ++j) {
573
+ var range = intact[j];
574
+ if (change.to <= range.from && change.diff) {
575
+ intact2.push({from: range.from + diff, to: range.to + diff});
576
+ } else if (change.to <= range.from || change.from >= range.to) {
577
+ intact2.push(range);
578
+ } else {
579
+ if (change.from > range.from)
580
+ intact2.push({from: range.from, to: change.from});
581
+ if (change.to < range.to)
582
+ intact2.push({from: change.to + diff, to: range.to + diff});
583
+ }
584
+ }
585
+ intact = intact2;
586
+ }
587
+ return intact;
588
+ }
589
+
590
+ function getDimensions(cm) {
591
+ var d = cm.display, left = {}, width = {};
592
+ for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) {
593
+ left[cm.options.gutters[i]] = n.offsetLeft;
594
+ width[cm.options.gutters[i]] = n.offsetWidth;
595
+ }
596
+ return {fixedPos: compensateForHScroll(d),
597
+ gutterTotalWidth: d.gutters.offsetWidth,
598
+ gutterLeft: left,
599
+ gutterWidth: width,
600
+ wrapperWidth: d.wrapper.clientWidth};
601
+ }
602
+
603
+ function patchDisplay(cm, from, to, intact, updateNumbersFrom) {
604
+ var dims = getDimensions(cm);
605
+ var display = cm.display, lineNumbers = cm.options.lineNumbers;
606
+ if (!intact.length && (!webkit || !cm.display.currentWheelTarget))
607
+ removeChildren(display.lineDiv);
608
+ var container = display.lineDiv, cur = container.firstChild;
609
+
610
+ function rm(node) {
611
+ var next = node.nextSibling;
612
+ if (webkit && mac && cm.display.currentWheelTarget == node) {
613
+ node.style.display = "none";
614
+ node.lineObj = null;
615
+ } else {
616
+ node.parentNode.removeChild(node);
617
+ }
618
+ return next;
619
+ }
620
+
621
+ var nextIntact = intact.shift(), lineN = from;
622
+ cm.doc.iter(from, to, function(line) {
623
+ if (nextIntact && nextIntact.to == lineN) nextIntact = intact.shift();
624
+ if (lineIsHidden(cm.doc, line)) {
625
+ if (line.height != 0) updateLineHeight(line, 0);
626
+ if (line.widgets && cur && cur.previousSibling) for (var i = 0; i < line.widgets.length; ++i) {
627
+ var w = line.widgets[i];
628
+ if (w.showIfHidden) {
629
+ var prev = cur.previousSibling;
630
+ if (/pre/i.test(prev.nodeName)) {
631
+ var wrap = elt("div", null, null, "position: relative");
632
+ prev.parentNode.replaceChild(wrap, prev);
633
+ wrap.appendChild(prev);
634
+ prev = wrap;
635
+ }
636
+ var wnode = prev.appendChild(elt("div", [w.node], "CodeMirror-linewidget"));
637
+ if (!w.handleMouseEvents) wnode.ignoreEvents = true;
638
+ positionLineWidget(w, wnode, prev, dims);
639
+ }
640
+ }
641
+ } else if (nextIntact && nextIntact.from <= lineN && nextIntact.to > lineN) {
642
+ // This line is intact. Skip to the actual node. Update its
643
+ // line number if needed.
644
+ while (cur.lineObj != line) cur = rm(cur);
645
+ if (lineNumbers && updateNumbersFrom <= lineN && cur.lineNumber)
646
+ setTextContent(cur.lineNumber, lineNumberFor(cm.options, lineN));
647
+ cur = cur.nextSibling;
648
+ } else {
649
+ // For lines with widgets, make an attempt to find and reuse
650
+ // the existing element, so that widgets aren't needlessly
651
+ // removed and re-inserted into the dom
652
+ if (line.widgets) for (var j = 0, search = cur, reuse; search && j < 20; ++j, search = search.nextSibling)
653
+ if (search.lineObj == line && /div/i.test(search.nodeName)) { reuse = search; break; }
654
+ // This line needs to be generated.
655
+ var lineNode = buildLineElement(cm, line, lineN, dims, reuse);
656
+ if (lineNode != reuse) {
657
+ container.insertBefore(lineNode, cur);
658
+ } else {
659
+ while (cur != reuse) cur = rm(cur);
660
+ cur = cur.nextSibling;
661
+ }
662
+
663
+ lineNode.lineObj = line;
664
+ }
665
+ ++lineN;
666
+ });
667
+ while (cur) cur = rm(cur);
668
+ }
669
+
670
+ function buildLineElement(cm, line, lineNo, dims, reuse) {
671
+ var built = buildLineContent(cm, line), lineElement = built.pre;
672
+ var markers = line.gutterMarkers, display = cm.display, wrap;
673
+
674
+ var bgClass = built.bgClass ? built.bgClass + " " + (line.bgClass || "") : line.bgClass;
675
+ if (!cm.options.lineNumbers && !markers && !bgClass && !line.wrapClass && !line.widgets)
676
+ return lineElement;
677
+
678
+ // Lines with gutter elements, widgets or a background class need
679
+ // to be wrapped again, and have the extra elements added to the
680
+ // wrapper div
681
+
682
+ if (reuse) {
683
+ reuse.alignable = null;
684
+ var isOk = true, widgetsSeen = 0, insertBefore = null;
685
+ for (var n = reuse.firstChild, next; n; n = next) {
686
+ next = n.nextSibling;
687
+ if (!/\bCodeMirror-linewidget\b/.test(n.className)) {
688
+ reuse.removeChild(n);
689
+ } else {
690
+ for (var i = 0; i < line.widgets.length; ++i) {
691
+ var widget = line.widgets[i];
692
+ if (widget.node == n.firstChild) {
693
+ if (!widget.above && !insertBefore) insertBefore = n;
694
+ positionLineWidget(widget, n, reuse, dims);
695
+ ++widgetsSeen;
696
+ break;
697
+ }
698
+ }
699
+ if (i == line.widgets.length) { isOk = false; break; }
700
+ }
701
+ }
702
+ reuse.insertBefore(lineElement, insertBefore);
703
+ if (isOk && widgetsSeen == line.widgets.length) {
704
+ wrap = reuse;
705
+ reuse.className = line.wrapClass || "";
706
+ }
707
+ }
708
+ if (!wrap) {
709
+ wrap = elt("div", null, line.wrapClass, "position: relative");
710
+ wrap.appendChild(lineElement);
711
+ }
712
+ // Kludge to make sure the styled element lies behind the selection (by z-index)
713
+ if (bgClass)
714
+ wrap.insertBefore(elt("div", null, bgClass + " CodeMirror-linebackground"), wrap.firstChild);
715
+ if (cm.options.lineNumbers || markers) {
716
+ var gutterWrap = wrap.insertBefore(elt("div", null, null, "position: absolute; left: " +
717
+ (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px"),
718
+ wrap.firstChild);
719
+ if (cm.options.fixedGutter) (wrap.alignable || (wrap.alignable = [])).push(gutterWrap);
720
+ if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"]))
721
+ wrap.lineNumber = gutterWrap.appendChild(
722
+ elt("div", lineNumberFor(cm.options, lineNo),
723
+ "CodeMirror-linenumber CodeMirror-gutter-elt",
724
+ "left: " + dims.gutterLeft["CodeMirror-linenumbers"] + "px; width: "
725
+ + display.lineNumInnerWidth + "px"));
726
+ if (markers)
727
+ for (var k = 0; k < cm.options.gutters.length; ++k) {
728
+ var id = cm.options.gutters[k], found = markers.hasOwnProperty(id) && markers[id];
729
+ if (found)
730
+ gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt", "left: " +
731
+ dims.gutterLeft[id] + "px; width: " + dims.gutterWidth[id] + "px"));
732
+ }
733
+ }
734
+ if (ie_lt8) wrap.style.zIndex = 2;
735
+ if (line.widgets && wrap != reuse) for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
736
+ var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget");
737
+ if (!widget.handleMouseEvents) node.ignoreEvents = true;
738
+ positionLineWidget(widget, node, wrap, dims);
739
+ if (widget.above)
740
+ wrap.insertBefore(node, cm.options.lineNumbers && line.height != 0 ? gutterWrap : lineElement);
741
+ else
742
+ wrap.appendChild(node);
743
+ signalLater(widget, "redraw");
744
+ }
745
+ return wrap;
746
+ }
747
+
748
+ function positionLineWidget(widget, node, wrap, dims) {
749
+ if (widget.noHScroll) {
750
+ (wrap.alignable || (wrap.alignable = [])).push(node);
751
+ var width = dims.wrapperWidth;
752
+ node.style.left = dims.fixedPos + "px";
753
+ if (!widget.coverGutter) {
754
+ width -= dims.gutterTotalWidth;
755
+ node.style.paddingLeft = dims.gutterTotalWidth + "px";
756
+ }
757
+ node.style.width = width + "px";
758
+ }
759
+ if (widget.coverGutter) {
760
+ node.style.zIndex = 5;
761
+ node.style.position = "relative";
762
+ if (!widget.noHScroll) node.style.marginLeft = -dims.gutterTotalWidth + "px";
763
+ }
764
+ }
765
+
766
+ // SELECTION / CURSOR
767
+
768
+ function updateSelection(cm) {
769
+ var display = cm.display;
770
+ var collapsed = posEq(cm.doc.sel.from, cm.doc.sel.to);
771
+ if (collapsed || cm.options.showCursorWhenSelecting)
772
+ updateSelectionCursor(cm);
773
+ else
774
+ display.cursor.style.display = display.otherCursor.style.display = "none";
775
+ if (!collapsed)
776
+ updateSelectionRange(cm);
777
+ else
778
+ display.selectionDiv.style.display = "none";
779
+
780
+ // Move the hidden textarea near the cursor to prevent scrolling artifacts
781
+ if (cm.options.moveInputWithCursor) {
782
+ var headPos = cursorCoords(cm, cm.doc.sel.head, "div");
783
+ var wrapOff = getRect(display.wrapper), lineOff = getRect(display.lineDiv);
784
+ display.inputDiv.style.top = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
785
+ headPos.top + lineOff.top - wrapOff.top)) + "px";
786
+ display.inputDiv.style.left = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
787
+ headPos.left + lineOff.left - wrapOff.left)) + "px";
788
+ }
789
+ }
790
+
791
+ // No selection, plain cursor
792
+ function updateSelectionCursor(cm) {
793
+ var display = cm.display, pos = cursorCoords(cm, cm.doc.sel.head, "div");
794
+ display.cursor.style.left = pos.left + "px";
795
+ display.cursor.style.top = pos.top + "px";
796
+ display.cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px";
797
+ display.cursor.style.display = "";
798
+
799
+ if (pos.other) {
800
+ display.otherCursor.style.display = "";
801
+ display.otherCursor.style.left = pos.other.left + "px";
802
+ display.otherCursor.style.top = pos.other.top + "px";
803
+ display.otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px";
804
+ } else { display.otherCursor.style.display = "none"; }
805
+ }
806
+
807
+ // Highlight selection
808
+ function updateSelectionRange(cm) {
809
+ var display = cm.display, doc = cm.doc, sel = cm.doc.sel;
810
+ var fragment = document.createDocumentFragment();
811
+ var clientWidth = display.lineSpace.offsetWidth, pl = paddingLeft(cm.display);
812
+
813
+ function add(left, top, width, bottom) {
814
+ if (top < 0) top = 0;
815
+ fragment.appendChild(elt("div", null, "CodeMirror-selected", "position: absolute; left: " + left +
816
+ "px; top: " + top + "px; width: " + (width == null ? clientWidth - left : width) +
817
+ "px; height: " + (bottom - top) + "px"));
818
+ }
819
+
820
+ function drawForLine(line, fromArg, toArg) {
821
+ var lineObj = getLine(doc, line);
822
+ var lineLen = lineObj.text.length;
823
+ var start, end;
824
+ function coords(ch, bias) {
825
+ return charCoords(cm, Pos(line, ch), "div", lineObj, bias);
826
+ }
827
+
828
+ iterateBidiSections(getOrder(lineObj), fromArg || 0, toArg == null ? lineLen : toArg, function(from, to, dir) {
829
+ var leftPos = coords(from, "left"), rightPos, left, right;
830
+ if (from == to) {
831
+ rightPos = leftPos;
832
+ left = right = leftPos.left;
833
+ } else {
834
+ rightPos = coords(to - 1, "right");
835
+ if (dir == "rtl") { var tmp = leftPos; leftPos = rightPos; rightPos = tmp; }
836
+ left = leftPos.left;
837
+ right = rightPos.right;
838
+ }
839
+ if (fromArg == null && from == 0) left = pl;
840
+ if (rightPos.top - leftPos.top > 3) { // Different lines, draw top part
841
+ add(left, leftPos.top, null, leftPos.bottom);
842
+ left = pl;
843
+ if (leftPos.bottom < rightPos.top) add(left, leftPos.bottom, null, rightPos.top);
844
+ }
845
+ if (toArg == null && to == lineLen) right = clientWidth;
846
+ if (!start || leftPos.top < start.top || leftPos.top == start.top && leftPos.left < start.left)
847
+ start = leftPos;
848
+ if (!end || rightPos.bottom > end.bottom || rightPos.bottom == end.bottom && rightPos.right > end.right)
849
+ end = rightPos;
850
+ if (left < pl + 1) left = pl;
851
+ add(left, rightPos.top, right - left, rightPos.bottom);
852
+ });
853
+ return {start: start, end: end};
854
+ }
855
+
856
+ if (sel.from.line == sel.to.line) {
857
+ drawForLine(sel.from.line, sel.from.ch, sel.to.ch);
858
+ } else {
859
+ var fromLine = getLine(doc, sel.from.line), toLine = getLine(doc, sel.to.line);
860
+ var singleVLine = visualLine(doc, fromLine) == visualLine(doc, toLine);
861
+ var leftEnd = drawForLine(sel.from.line, sel.from.ch, singleVLine ? fromLine.text.length : null).end;
862
+ var rightStart = drawForLine(sel.to.line, singleVLine ? 0 : null, sel.to.ch).start;
863
+ if (singleVLine) {
864
+ if (leftEnd.top < rightStart.top - 2) {
865
+ add(leftEnd.right, leftEnd.top, null, leftEnd.bottom);
866
+ add(pl, rightStart.top, rightStart.left, rightStart.bottom);
867
+ } else {
868
+ add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom);
869
+ }
870
+ }
871
+ if (leftEnd.bottom < rightStart.top)
872
+ add(pl, leftEnd.bottom, null, rightStart.top);
873
+ }
874
+
875
+ removeChildrenAndAdd(display.selectionDiv, fragment);
876
+ display.selectionDiv.style.display = "";
877
+ }
878
+
879
+ // Cursor-blinking
880
+ function restartBlink(cm) {
881
+ if (!cm.state.focused) return;
882
+ var display = cm.display;
883
+ clearInterval(display.blinker);
884
+ var on = true;
885
+ display.cursor.style.visibility = display.otherCursor.style.visibility = "";
886
+ if (cm.options.cursorBlinkRate > 0)
887
+ display.blinker = setInterval(function() {
888
+ display.cursor.style.visibility = display.otherCursor.style.visibility = (on = !on) ? "" : "hidden";
889
+ }, cm.options.cursorBlinkRate);
890
+ }
891
+
892
+ // HIGHLIGHT WORKER
893
+
894
+ function startWorker(cm, time) {
895
+ if (cm.doc.mode.startState && cm.doc.frontier < cm.display.showingTo)
896
+ cm.state.highlight.set(time, bind(highlightWorker, cm));
897
+ }
898
+
899
+ function highlightWorker(cm) {
900
+ var doc = cm.doc;
901
+ if (doc.frontier < doc.first) doc.frontier = doc.first;
902
+ if (doc.frontier >= cm.display.showingTo) return;
903
+ var end = +new Date + cm.options.workTime;
904
+ var state = copyState(doc.mode, getStateBefore(cm, doc.frontier));
905
+ var changed = [], prevChange;
906
+ doc.iter(doc.frontier, Math.min(doc.first + doc.size, cm.display.showingTo + 500), function(line) {
907
+ if (doc.frontier >= cm.display.showingFrom) { // Visible
908
+ var oldStyles = line.styles;
909
+ line.styles = highlightLine(cm, line, state);
910
+ var ischange = !oldStyles || oldStyles.length != line.styles.length;
911
+ for (var i = 0; !ischange && i < oldStyles.length; ++i) ischange = oldStyles[i] != line.styles[i];
912
+ if (ischange) {
913
+ if (prevChange && prevChange.end == doc.frontier) prevChange.end++;
914
+ else changed.push(prevChange = {start: doc.frontier, end: doc.frontier + 1});
915
+ }
916
+ line.stateAfter = copyState(doc.mode, state);
917
+ } else {
918
+ processLine(cm, line, state);
919
+ line.stateAfter = doc.frontier % 5 == 0 ? copyState(doc.mode, state) : null;
920
+ }
921
+ ++doc.frontier;
922
+ if (+new Date > end) {
923
+ startWorker(cm, cm.options.workDelay);
924
+ return true;
925
+ }
926
+ });
927
+ if (changed.length)
928
+ operation(cm, function() {
929
+ for (var i = 0; i < changed.length; ++i)
930
+ regChange(this, changed[i].start, changed[i].end);
931
+ })();
932
+ }
933
+
934
+ // Finds the line to start with when starting a parse. Tries to
935
+ // find a line with a stateAfter, so that it can start with a
936
+ // valid state. If that fails, it returns the line with the
937
+ // smallest indentation, which tends to need the least context to
938
+ // parse correctly.
939
+ function findStartLine(cm, n, precise) {
940
+ var minindent, minline, doc = cm.doc, maxScan = cm.doc.mode.innerMode ? 1000 : 100;
941
+ for (var search = n, lim = n - maxScan; search > lim; --search) {
942
+ if (search <= doc.first) return doc.first;
943
+ var line = getLine(doc, search - 1);
944
+ if (line.stateAfter && (!precise || search <= doc.frontier)) return search;
945
+ var indented = countColumn(line.text, null, cm.options.tabSize);
946
+ if (minline == null || minindent > indented) {
947
+ minline = search - 1;
948
+ minindent = indented;
949
+ }
950
+ }
951
+ return minline;
952
+ }
953
+
954
+ function getStateBefore(cm, n, precise) {
955
+ var doc = cm.doc, display = cm.display;
956
+ if (!doc.mode.startState) return true;
957
+ var pos = findStartLine(cm, n, precise), state = pos > doc.first && getLine(doc, pos-1).stateAfter;
958
+ if (!state) state = startState(doc.mode);
959
+ else state = copyState(doc.mode, state);
960
+ doc.iter(pos, n, function(line) {
961
+ processLine(cm, line, state);
962
+ var save = pos == n - 1 || pos % 5 == 0 || pos >= display.showingFrom && pos < display.showingTo;
963
+ line.stateAfter = save ? copyState(doc.mode, state) : null;
964
+ ++pos;
965
+ });
966
+ return state;
967
+ }
968
+
969
+ // POSITION MEASUREMENT
970
+
971
+ function paddingTop(display) {return display.lineSpace.offsetTop;}
972
+ function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight;}
973
+ function paddingLeft(display) {
974
+ var e = removeChildrenAndAdd(display.measure, elt("pre", null, null, "text-align: left")).appendChild(elt("span", "x"));
975
+ return e.offsetLeft;
976
+ }
977
+
978
+ function measureChar(cm, line, ch, data, bias) {
979
+ var dir = -1;
980
+ data = data || measureLine(cm, line);
981
+ if (data.crude) {
982
+ var left = data.left + ch * data.width;
983
+ return {left: left, right: left + data.width, top: data.top, bottom: data.bottom};
984
+ }
985
+
986
+ for (var pos = ch;; pos += dir) {
987
+ var r = data[pos];
988
+ if (r) break;
989
+ if (dir < 0 && pos == 0) dir = 1;
990
+ }
991
+ bias = pos > ch ? "left" : pos < ch ? "right" : bias;
992
+ if (bias == "left" && r.leftSide) r = r.leftSide;
993
+ else if (bias == "right" && r.rightSide) r = r.rightSide;
994
+ return {left: pos < ch ? r.right : r.left,
995
+ right: pos > ch ? r.left : r.right,
996
+ top: r.top,
997
+ bottom: r.bottom};
998
+ }
999
+
1000
+ function findCachedMeasurement(cm, line) {
1001
+ var cache = cm.display.measureLineCache;
1002
+ for (var i = 0; i < cache.length; ++i) {
1003
+ var memo = cache[i];
1004
+ if (memo.text == line.text && memo.markedSpans == line.markedSpans &&
1005
+ cm.display.scroller.clientWidth == memo.width &&
1006
+ memo.classes == line.textClass + "|" + line.wrapClass)
1007
+ return memo;
1008
+ }
1009
+ }
1010
+
1011
+ function clearCachedMeasurement(cm, line) {
1012
+ var exists = findCachedMeasurement(cm, line);
1013
+ if (exists) exists.text = exists.measure = exists.markedSpans = null;
1014
+ }
1015
+
1016
+ function measureLine(cm, line) {
1017
+ // First look in the cache
1018
+ var cached = findCachedMeasurement(cm, line);
1019
+ if (cached) return cached.measure;
1020
+
1021
+ // Failing that, recompute and store result in cache
1022
+ var measure = measureLineInner(cm, line);
1023
+ var cache = cm.display.measureLineCache;
1024
+ var memo = {text: line.text, width: cm.display.scroller.clientWidth,
1025
+ markedSpans: line.markedSpans, measure: measure,
1026
+ classes: line.textClass + "|" + line.wrapClass};
1027
+ if (cache.length == 16) cache[++cm.display.measureLineCachePos % 16] = memo;
1028
+ else cache.push(memo);
1029
+ return measure;
1030
+ }
1031
+
1032
+ function measureLineInner(cm, line) {
1033
+ if (!cm.options.lineWrapping && line.text.length >= cm.options.crudeMeasuringFrom)
1034
+ return crudelyMeasureLine(cm, line);
1035
+
1036
+ var display = cm.display, measure = emptyArray(line.text.length);
1037
+ var pre = buildLineContent(cm, line, measure, true).pre;
1038
+
1039
+ // IE does not cache element positions of inline elements between
1040
+ // calls to getBoundingClientRect. This makes the loop below,
1041
+ // which gathers the positions of all the characters on the line,
1042
+ // do an amount of layout work quadratic to the number of
1043
+ // characters. When line wrapping is off, we try to improve things
1044
+ // by first subdividing the line into a bunch of inline blocks, so
1045
+ // that IE can reuse most of the layout information from caches
1046
+ // for those blocks. This does interfere with line wrapping, so it
1047
+ // doesn't work when wrapping is on, but in that case the
1048
+ // situation is slightly better, since IE does cache line-wrapping
1049
+ // information and only recomputes per-line.
1050
+ if (ie && !ie_lt8 && !cm.options.lineWrapping && pre.childNodes.length > 100) {
1051
+ var fragment = document.createDocumentFragment();
1052
+ var chunk = 10, n = pre.childNodes.length;
1053
+ for (var i = 0, chunks = Math.ceil(n / chunk); i < chunks; ++i) {
1054
+ var wrap = elt("div", null, null, "display: inline-block");
1055
+ for (var j = 0; j < chunk && n; ++j) {
1056
+ wrap.appendChild(pre.firstChild);
1057
+ --n;
1058
+ }
1059
+ fragment.appendChild(wrap);
1060
+ }
1061
+ pre.appendChild(fragment);
1062
+ }
1063
+
1064
+ removeChildrenAndAdd(display.measure, pre);
1065
+
1066
+ var outer = getRect(display.lineDiv);
1067
+ var vranges = [], data = emptyArray(line.text.length), maxBot = pre.offsetHeight;
1068
+ // Work around an IE7/8 bug where it will sometimes have randomly
1069
+ // replaced our pre with a clone at this point.
1070
+ if (ie_lt9 && display.measure.first != pre)
1071
+ removeChildrenAndAdd(display.measure, pre);
1072
+
1073
+ function measureRect(rect) {
1074
+ var top = rect.top - outer.top, bot = rect.bottom - outer.top;
1075
+ if (bot > maxBot) bot = maxBot;
1076
+ if (top < 0) top = 0;
1077
+ for (var i = vranges.length - 2; i >= 0; i -= 2) {
1078
+ var rtop = vranges[i], rbot = vranges[i+1];
1079
+ if (rtop > bot || rbot < top) continue;
1080
+ if (rtop <= top && rbot >= bot ||
1081
+ top <= rtop && bot >= rbot ||
1082
+ Math.min(bot, rbot) - Math.max(top, rtop) >= (bot - top) >> 1) {
1083
+ vranges[i] = Math.min(top, rtop);
1084
+ vranges[i+1] = Math.max(bot, rbot);
1085
+ break;
1086
+ }
1087
+ }
1088
+ if (i < 0) { i = vranges.length; vranges.push(top, bot); }
1089
+ return {left: rect.left - outer.left,
1090
+ right: rect.right - outer.left,
1091
+ top: i, bottom: null};
1092
+ }
1093
+ function finishRect(rect) {
1094
+ rect.bottom = vranges[rect.top+1];
1095
+ rect.top = vranges[rect.top];
1096
+ }
1097
+
1098
+ for (var i = 0, cur; i < measure.length; ++i) if (cur = measure[i]) {
1099
+ var node = cur, rect = null;
1100
+ // A widget might wrap, needs special care
1101
+ if (/\bCodeMirror-widget\b/.test(cur.className) && cur.getClientRects) {
1102
+ if (cur.firstChild.nodeType == 1) node = cur.firstChild;
1103
+ var rects = node.getClientRects();
1104
+ if (rects.length > 1) {
1105
+ rect = data[i] = measureRect(rects[0]);
1106
+ rect.rightSide = measureRect(rects[rects.length - 1]);
1107
+ }
1108
+ }
1109
+ if (!rect) rect = data[i] = measureRect(getRect(node));
1110
+ if (cur.measureRight) rect.right = getRect(cur.measureRight).left;
1111
+ if (cur.leftSide) rect.leftSide = measureRect(getRect(cur.leftSide));
1112
+ }
1113
+ removeChildren(cm.display.measure);
1114
+ for (var i = 0, cur; i < data.length; ++i) if (cur = data[i]) {
1115
+ finishRect(cur);
1116
+ if (cur.leftSide) finishRect(cur.leftSide);
1117
+ if (cur.rightSide) finishRect(cur.rightSide);
1118
+ }
1119
+ return data;
1120
+ }
1121
+
1122
+ function crudelyMeasureLine(cm, line) {
1123
+ var copy = new Line(line.text.slice(0, 100), null);
1124
+ if (line.textClass) copy.textClass = line.textClass;
1125
+ var measure = measureLineInner(cm, copy);
1126
+ var left = measureChar(cm, copy, 0, measure, "left");
1127
+ var right = measureChar(cm, copy, 99, measure, "right");
1128
+ return {crude: true, top: left.top, left: left.left, bottom: left.bottom, width: (right.right - left.left) / 100};
1129
+ }
1130
+
1131
+ function measureLineWidth(cm, line) {
1132
+ var hasBadSpan = false;
1133
+ if (line.markedSpans) for (var i = 0; i < line.markedSpans; ++i) {
1134
+ var sp = line.markedSpans[i];
1135
+ if (sp.collapsed && (sp.to == null || sp.to == line.text.length)) hasBadSpan = true;
1136
+ }
1137
+ var cached = !hasBadSpan && findCachedMeasurement(cm, line);
1138
+ if (cached || line.text.length >= cm.options.crudeMeasuringFrom)
1139
+ return measureChar(cm, line, line.text.length, cached && cached.measure, "right").right;
1140
+
1141
+ var pre = buildLineContent(cm, line, null, true).pre;
1142
+ var end = pre.appendChild(zeroWidthElement(cm.display.measure));
1143
+ removeChildrenAndAdd(cm.display.measure, pre);
1144
+ return getRect(end).right - getRect(cm.display.lineDiv).left;
1145
+ }
1146
+
1147
+ function clearCaches(cm) {
1148
+ cm.display.measureLineCache.length = cm.display.measureLineCachePos = 0;
1149
+ cm.display.cachedCharWidth = cm.display.cachedTextHeight = null;
1150
+ if (!cm.options.lineWrapping) cm.display.maxLineChanged = true;
1151
+ cm.display.lineNumChars = null;
1152
+ }
1153
+
1154
+ function pageScrollX() { return window.pageXOffset || (document.documentElement || document.body).scrollLeft; }
1155
+ function pageScrollY() { return window.pageYOffset || (document.documentElement || document.body).scrollTop; }
1156
+
1157
+ // Context is one of "line", "div" (display.lineDiv), "local"/null (editor), or "page"
1158
+ function intoCoordSystem(cm, lineObj, rect, context) {
1159
+ if (lineObj.widgets) for (var i = 0; i < lineObj.widgets.length; ++i) if (lineObj.widgets[i].above) {
1160
+ var size = widgetHeight(lineObj.widgets[i]);
1161
+ rect.top += size; rect.bottom += size;
1162
+ }
1163
+ if (context == "line") return rect;
1164
+ if (!context) context = "local";
1165
+ var yOff = heightAtLine(cm, lineObj);
1166
+ if (context == "local") yOff += paddingTop(cm.display);
1167
+ else yOff -= cm.display.viewOffset;
1168
+ if (context == "page" || context == "window") {
1169
+ var lOff = getRect(cm.display.lineSpace);
1170
+ yOff += lOff.top + (context == "window" ? 0 : pageScrollY());
1171
+ var xOff = lOff.left + (context == "window" ? 0 : pageScrollX());
1172
+ rect.left += xOff; rect.right += xOff;
1173
+ }
1174
+ rect.top += yOff; rect.bottom += yOff;
1175
+ return rect;
1176
+ }
1177
+
1178
+ // Context may be "window", "page", "div", or "local"/null
1179
+ // Result is in "div" coords
1180
+ function fromCoordSystem(cm, coords, context) {
1181
+ if (context == "div") return coords;
1182
+ var left = coords.left, top = coords.top;
1183
+ // First move into "page" coordinate system
1184
+ if (context == "page") {
1185
+ left -= pageScrollX();
1186
+ top -= pageScrollY();
1187
+ } else if (context == "local" || !context) {
1188
+ var localBox = getRect(cm.display.sizer);
1189
+ left += localBox.left;
1190
+ top += localBox.top;
1191
+ }
1192
+
1193
+ var lineSpaceBox = getRect(cm.display.lineSpace);
1194
+ return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top};
1195
+ }
1196
+
1197
+ function charCoords(cm, pos, context, lineObj, bias) {
1198
+ if (!lineObj) lineObj = getLine(cm.doc, pos.line);
1199
+ return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, null, bias), context);
1200
+ }
1201
+
1202
+ function cursorCoords(cm, pos, context, lineObj, measurement) {
1203
+ lineObj = lineObj || getLine(cm.doc, pos.line);
1204
+ if (!measurement) measurement = measureLine(cm, lineObj);
1205
+ function get(ch, right) {
1206
+ var m = measureChar(cm, lineObj, ch, measurement, right ? "right" : "left");
1207
+ if (right) m.left = m.right; else m.right = m.left;
1208
+ return intoCoordSystem(cm, lineObj, m, context);
1209
+ }
1210
+ function getBidi(ch, partPos) {
1211
+ var part = order[partPos], right = part.level % 2;
1212
+ if (ch == bidiLeft(part) && partPos && part.level < order[partPos - 1].level) {
1213
+ part = order[--partPos];
1214
+ ch = bidiRight(part) - (part.level % 2 ? 0 : 1);
1215
+ right = true;
1216
+ } else if (ch == bidiRight(part) && partPos < order.length - 1 && part.level < order[partPos + 1].level) {
1217
+ part = order[++partPos];
1218
+ ch = bidiLeft(part) - part.level % 2;
1219
+ right = false;
1220
+ }
1221
+ if (right && ch == part.to && ch > part.from) return get(ch - 1);
1222
+ return get(ch, right);
1223
+ }
1224
+ var order = getOrder(lineObj), ch = pos.ch;
1225
+ if (!order) return get(ch);
1226
+ var partPos = getBidiPartAt(order, ch);
1227
+ var val = getBidi(ch, partPos);
1228
+ if (bidiOther != null) val.other = getBidi(ch, bidiOther);
1229
+ return val;
1230
+ }
1231
+
1232
+ function PosWithInfo(line, ch, outside, xRel) {
1233
+ var pos = new Pos(line, ch);
1234
+ pos.xRel = xRel;
1235
+ if (outside) pos.outside = true;
1236
+ return pos;
1237
+ }
1238
+
1239
+ // Coords must be lineSpace-local
1240
+ function coordsChar(cm, x, y) {
1241
+ var doc = cm.doc;
1242
+ y += cm.display.viewOffset;
1243
+ if (y < 0) return PosWithInfo(doc.first, 0, true, -1);
1244
+ var lineNo = lineAtHeight(doc, y), last = doc.first + doc.size - 1;
1245
+ if (lineNo > last)
1246
+ return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, true, 1);
1247
+ if (x < 0) x = 0;
1248
+
1249
+ for (;;) {
1250
+ var lineObj = getLine(doc, lineNo);
1251
+ var found = coordsCharInner(cm, lineObj, lineNo, x, y);
1252
+ var merged = collapsedSpanAtEnd(lineObj);
1253
+ var mergedPos = merged && merged.find();
1254
+ if (merged && (found.ch > mergedPos.from.ch || found.ch == mergedPos.from.ch && found.xRel > 0))
1255
+ lineNo = mergedPos.to.line;
1256
+ else
1257
+ return found;
1258
+ }
1259
+ }
1260
+
1261
+ function coordsCharInner(cm, lineObj, lineNo, x, y) {
1262
+ var innerOff = y - heightAtLine(cm, lineObj);
1263
+ var wrongLine = false, adjust = 2 * cm.display.wrapper.clientWidth;
1264
+ var measurement = measureLine(cm, lineObj);
1265
+
1266
+ function getX(ch) {
1267
+ var sp = cursorCoords(cm, Pos(lineNo, ch), "line",
1268
+ lineObj, measurement);
1269
+ wrongLine = true;
1270
+ if (innerOff > sp.bottom) return sp.left - adjust;
1271
+ else if (innerOff < sp.top) return sp.left + adjust;
1272
+ else wrongLine = false;
1273
+ return sp.left;
1274
+ }
1275
+
1276
+ var bidi = getOrder(lineObj), dist = lineObj.text.length;
1277
+ var from = lineLeft(lineObj), to = lineRight(lineObj);
1278
+ var fromX = getX(from), fromOutside = wrongLine, toX = getX(to), toOutside = wrongLine;
1279
+
1280
+ if (x > toX) return PosWithInfo(lineNo, to, toOutside, 1);
1281
+ // Do a binary search between these bounds.
1282
+ for (;;) {
1283
+ if (bidi ? to == from || to == moveVisually(lineObj, from, 1) : to - from <= 1) {
1284
+ var ch = x < fromX || x - fromX <= toX - x ? from : to;
1285
+ var xDiff = x - (ch == from ? fromX : toX);
1286
+ while (isExtendingChar.test(lineObj.text.charAt(ch))) ++ch;
1287
+ var pos = PosWithInfo(lineNo, ch, ch == from ? fromOutside : toOutside,
1288
+ xDiff < 0 ? -1 : xDiff ? 1 : 0);
1289
+ return pos;
1290
+ }
1291
+ var step = Math.ceil(dist / 2), middle = from + step;
1292
+ if (bidi) {
1293
+ middle = from;
1294
+ for (var i = 0; i < step; ++i) middle = moveVisually(lineObj, middle, 1);
1295
+ }
1296
+ var middleX = getX(middle);
1297
+ if (middleX > x) {to = middle; toX = middleX; if (toOutside = wrongLine) toX += 1000; dist = step;}
1298
+ else {from = middle; fromX = middleX; fromOutside = wrongLine; dist -= step;}
1299
+ }
1300
+ }
1301
+
1302
+ var measureText;
1303
+ function textHeight(display) {
1304
+ if (display.cachedTextHeight != null) return display.cachedTextHeight;
1305
+ if (measureText == null) {
1306
+ measureText = elt("pre");
1307
+ // Measure a bunch of lines, for browsers that compute
1308
+ // fractional heights.
1309
+ for (var i = 0; i < 49; ++i) {
1310
+ measureText.appendChild(document.createTextNode("x"));
1311
+ measureText.appendChild(elt("br"));
1312
+ }
1313
+ measureText.appendChild(document.createTextNode("x"));
1314
+ }
1315
+ removeChildrenAndAdd(display.measure, measureText);
1316
+ var height = measureText.offsetHeight / 50;
1317
+ if (height > 3) display.cachedTextHeight = height;
1318
+ removeChildren(display.measure);
1319
+ return height || 1;
1320
+ }
1321
+
1322
+ function charWidth(display) {
1323
+ if (display.cachedCharWidth != null) return display.cachedCharWidth;
1324
+ var anchor = elt("span", "x");
1325
+ var pre = elt("pre", [anchor]);
1326
+ removeChildrenAndAdd(display.measure, pre);
1327
+ var width = anchor.offsetWidth;
1328
+ if (width > 2) display.cachedCharWidth = width;
1329
+ return width || 10;
1330
+ }
1331
+
1332
+ // OPERATIONS
1333
+
1334
+ // Operations are used to wrap changes in such a way that each
1335
+ // change won't have to update the cursor and display (which would
1336
+ // be awkward, slow, and error-prone), but instead updates are
1337
+ // batched and then all combined and executed at once.
1338
+
1339
+ var nextOpId = 0;
1340
+ function startOperation(cm) {
1341
+ cm.curOp = {
1342
+ // An array of ranges of lines that have to be updated. See
1343
+ // updateDisplay.
1344
+ changes: [],
1345
+ forceUpdate: false,
1346
+ updateInput: null,
1347
+ userSelChange: null,
1348
+ textChanged: null,
1349
+ selectionChanged: false,
1350
+ cursorActivity: false,
1351
+ updateMaxLine: false,
1352
+ updateScrollPos: false,
1353
+ id: ++nextOpId
1354
+ };
1355
+ if (!delayedCallbackDepth++) delayedCallbacks = [];
1356
+ }
1357
+
1358
+ function endOperation(cm) {
1359
+ var op = cm.curOp, doc = cm.doc, display = cm.display;
1360
+ cm.curOp = null;
1361
+
1362
+ if (op.updateMaxLine) computeMaxLength(cm);
1363
+ if (display.maxLineChanged && !cm.options.lineWrapping && display.maxLine) {
1364
+ var width = measureLineWidth(cm, display.maxLine);
1365
+ display.sizer.style.minWidth = Math.max(0, width + 3 + scrollerCutOff) + "px";
1366
+ display.maxLineChanged = false;
1367
+ var maxScrollLeft = Math.max(0, display.sizer.offsetLeft + display.sizer.offsetWidth - display.scroller.clientWidth);
1368
+ if (maxScrollLeft < doc.scrollLeft && !op.updateScrollPos)
1369
+ setScrollLeft(cm, Math.min(display.scroller.scrollLeft, maxScrollLeft), true);
1370
+ }
1371
+ var newScrollPos, updated;
1372
+ if (op.updateScrollPos) {
1373
+ newScrollPos = op.updateScrollPos;
1374
+ } else if (op.selectionChanged && display.scroller.clientHeight) { // don't rescroll if not visible
1375
+ var coords = cursorCoords(cm, doc.sel.head);
1376
+ newScrollPos = calculateScrollPos(cm, coords.left, coords.top, coords.left, coords.bottom);
1377
+ }
1378
+ if (op.changes.length || op.forceUpdate || newScrollPos && newScrollPos.scrollTop != null) {
1379
+ updated = updateDisplay(cm, op.changes, newScrollPos && newScrollPos.scrollTop, op.forceUpdate);
1380
+ if (cm.display.scroller.offsetHeight) cm.doc.scrollTop = cm.display.scroller.scrollTop;
1381
+ }
1382
+ if (!updated && op.selectionChanged) updateSelection(cm);
1383
+ if (op.updateScrollPos) {
1384
+ display.scroller.scrollTop = display.scrollbarV.scrollTop = doc.scrollTop = newScrollPos.scrollTop;
1385
+ display.scroller.scrollLeft = display.scrollbarH.scrollLeft = doc.scrollLeft = newScrollPos.scrollLeft;
1386
+ alignHorizontally(cm);
1387
+ if (op.scrollToPos)
1388
+ scrollPosIntoView(cm, clipPos(cm.doc, op.scrollToPos), op.scrollToPosMargin);
1389
+ } else if (newScrollPos) {
1390
+ scrollCursorIntoView(cm);
1391
+ }
1392
+ if (op.selectionChanged) restartBlink(cm);
1393
+
1394
+ if (cm.state.focused && op.updateInput)
1395
+ resetInput(cm, op.userSelChange);
1396
+
1397
+ var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers;
1398
+ if (hidden) for (var i = 0; i < hidden.length; ++i)
1399
+ if (!hidden[i].lines.length) signal(hidden[i], "hide");
1400
+ if (unhidden) for (var i = 0; i < unhidden.length; ++i)
1401
+ if (unhidden[i].lines.length) signal(unhidden[i], "unhide");
1402
+
1403
+ var delayed;
1404
+ if (!--delayedCallbackDepth) {
1405
+ delayed = delayedCallbacks;
1406
+ delayedCallbacks = null;
1407
+ }
1408
+ if (op.textChanged)
1409
+ signal(cm, "change", cm, op.textChanged);
1410
+ if (op.cursorActivity) signal(cm, "cursorActivity", cm);
1411
+ if (delayed) for (var i = 0; i < delayed.length; ++i) delayed[i]();
1412
+ }
1413
+
1414
+ // Wraps a function in an operation. Returns the wrapped function.
1415
+ function operation(cm1, f) {
1416
+ return function() {
1417
+ var cm = cm1 || this, withOp = !cm.curOp;
1418
+ if (withOp) startOperation(cm);
1419
+ try { var result = f.apply(cm, arguments); }
1420
+ finally { if (withOp) endOperation(cm); }
1421
+ return result;
1422
+ };
1423
+ }
1424
+ function docOperation(f) {
1425
+ return function() {
1426
+ var withOp = this.cm && !this.cm.curOp, result;
1427
+ if (withOp) startOperation(this.cm);
1428
+ try { result = f.apply(this, arguments); }
1429
+ finally { if (withOp) endOperation(this.cm); }
1430
+ return result;
1431
+ };
1432
+ }
1433
+ function runInOp(cm, f) {
1434
+ var withOp = !cm.curOp, result;
1435
+ if (withOp) startOperation(cm);
1436
+ try { result = f(); }
1437
+ finally { if (withOp) endOperation(cm); }
1438
+ return result;
1439
+ }
1440
+
1441
+ function regChange(cm, from, to, lendiff) {
1442
+ if (from == null) from = cm.doc.first;
1443
+ if (to == null) to = cm.doc.first + cm.doc.size;
1444
+ cm.curOp.changes.push({from: from, to: to, diff: lendiff});
1445
+ }
1446
+
1447
+ // INPUT HANDLING
1448
+
1449
+ function slowPoll(cm) {
1450
+ if (cm.display.pollingFast) return;
1451
+ cm.display.poll.set(cm.options.pollInterval, function() {
1452
+ readInput(cm);
1453
+ if (cm.state.focused) slowPoll(cm);
1454
+ });
1455
+ }
1456
+
1457
+ function fastPoll(cm) {
1458
+ var missed = false;
1459
+ cm.display.pollingFast = true;
1460
+ function p() {
1461
+ var changed = readInput(cm);
1462
+ if (!changed && !missed) {missed = true; cm.display.poll.set(60, p);}
1463
+ else {cm.display.pollingFast = false; slowPoll(cm);}
1464
+ }
1465
+ cm.display.poll.set(20, p);
1466
+ }
1467
+
1468
+ // prevInput is a hack to work with IME. If we reset the textarea
1469
+ // on every change, that breaks IME. So we look for changes
1470
+ // compared to the previous content instead. (Modern browsers have
1471
+ // events that indicate IME taking place, but these are not widely
1472
+ // supported or compatible enough yet to rely on.)
1473
+ function readInput(cm) {
1474
+ var input = cm.display.input, prevInput = cm.display.prevInput, doc = cm.doc, sel = doc.sel;
1475
+ if (!cm.state.focused || hasSelection(input) || isReadOnly(cm) || cm.state.disableInput) return false;
1476
+ if (cm.state.pasteIncoming && cm.state.fakedLastChar) {
1477
+ input.value = input.value.substring(0, input.value.length - 1);
1478
+ cm.state.fakedLastChar = false;
1479
+ }
1480
+ var text = input.value;
1481
+ if (text == prevInput && posEq(sel.from, sel.to)) return false;
1482
+ if (ie && !ie_lt9 && cm.display.inputHasSelection === text) {
1483
+ resetInput(cm, true);
1484
+ return false;
1485
+ }
1486
+
1487
+ var withOp = !cm.curOp;
1488
+ if (withOp) startOperation(cm);
1489
+ sel.shift = false;
1490
+ var same = 0, l = Math.min(prevInput.length, text.length);
1491
+ while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) ++same;
1492
+ var from = sel.from, to = sel.to;
1493
+ if (same < prevInput.length)
1494
+ from = Pos(from.line, from.ch - (prevInput.length - same));
1495
+ else if (cm.state.overwrite && posEq(from, to) && !cm.state.pasteIncoming)
1496
+ to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + (text.length - same)));
1497
+
1498
+ var updateInput = cm.curOp.updateInput;
1499
+ var changeEvent = {from: from, to: to, text: splitLines(text.slice(same)),
1500
+ origin: cm.state.pasteIncoming ? "paste" : "+input"};
1501
+ makeChange(cm.doc, changeEvent, "end");
1502
+ cm.curOp.updateInput = updateInput;
1503
+ signalLater(cm, "inputRead", cm, changeEvent);
1504
+
1505
+ if (text.length > 1000 || text.indexOf("\n") > -1) input.value = cm.display.prevInput = "";
1506
+ else cm.display.prevInput = text;
1507
+ if (withOp) endOperation(cm);
1508
+ cm.state.pasteIncoming = false;
1509
+ return true;
1510
+ }
1511
+
1512
+ function resetInput(cm, user) {
1513
+ var minimal, selected, doc = cm.doc;
1514
+ if (!posEq(doc.sel.from, doc.sel.to)) {
1515
+ cm.display.prevInput = "";
1516
+ minimal = hasCopyEvent &&
1517
+ (doc.sel.to.line - doc.sel.from.line > 100 || (selected = cm.getSelection()).length > 1000);
1518
+ var content = minimal ? "-" : selected || cm.getSelection();
1519
+ cm.display.input.value = content;
1520
+ if (cm.state.focused) selectInput(cm.display.input);
1521
+ if (ie && !ie_lt9) cm.display.inputHasSelection = content;
1522
+ } else if (user) {
1523
+ cm.display.prevInput = cm.display.input.value = "";
1524
+ if (ie && !ie_lt9) cm.display.inputHasSelection = null;
1525
+ }
1526
+ cm.display.inaccurateSelection = minimal;
1527
+ }
1528
+
1529
+ function focusInput(cm) {
1530
+ if (cm.options.readOnly != "nocursor" && (!mobile || document.activeElement != cm.display.input))
1531
+ cm.display.input.focus();
1532
+ }
1533
+
1534
+ function isReadOnly(cm) {
1535
+ return cm.options.readOnly || cm.doc.cantEdit;
1536
+ }
1537
+
1538
+ // EVENT HANDLERS
1539
+
1540
+ function registerEventHandlers(cm) {
1541
+ var d = cm.display;
1542
+ on(d.scroller, "mousedown", operation(cm, onMouseDown));
1543
+ if (ie)
1544
+ on(d.scroller, "dblclick", operation(cm, function(e) {
1545
+ if (signalDOMEvent(cm, e)) return;
1546
+ var pos = posFromMouse(cm, e);
1547
+ if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) return;
1548
+ e_preventDefault(e);
1549
+ var word = findWordAt(getLine(cm.doc, pos.line).text, pos);
1550
+ extendSelection(cm.doc, word.from, word.to);
1551
+ }));
1552
+ else
1553
+ on(d.scroller, "dblclick", function(e) { signalDOMEvent(cm, e) || e_preventDefault(e); });
1554
+ on(d.lineSpace, "selectstart", function(e) {
1555
+ if (!eventInWidget(d, e)) e_preventDefault(e);
1556
+ });
1557
+ // Gecko browsers fire contextmenu *after* opening the menu, at
1558
+ // which point we can't mess with it anymore. Context menu is
1559
+ // handled in onMouseDown for Gecko.
1560
+ if (!captureMiddleClick) on(d.scroller, "contextmenu", function(e) {onContextMenu(cm, e);});
1561
+
1562
+ on(d.scroller, "scroll", function() {
1563
+ if (d.scroller.clientHeight) {
1564
+ setScrollTop(cm, d.scroller.scrollTop);
1565
+ setScrollLeft(cm, d.scroller.scrollLeft, true);
1566
+ signal(cm, "scroll", cm);
1567
+ }
1568
+ });
1569
+ on(d.scrollbarV, "scroll", function() {
1570
+ if (d.scroller.clientHeight) setScrollTop(cm, d.scrollbarV.scrollTop);
1571
+ });
1572
+ on(d.scrollbarH, "scroll", function() {
1573
+ if (d.scroller.clientHeight) setScrollLeft(cm, d.scrollbarH.scrollLeft);
1574
+ });
1575
+
1576
+ on(d.scroller, "mousewheel", function(e){onScrollWheel(cm, e);});
1577
+ on(d.scroller, "DOMMouseScroll", function(e){onScrollWheel(cm, e);});
1578
+
1579
+ function reFocus() { if (cm.state.focused) setTimeout(bind(focusInput, cm), 0); }
1580
+ on(d.scrollbarH, "mousedown", reFocus);
1581
+ on(d.scrollbarV, "mousedown", reFocus);
1582
+ // Prevent wrapper from ever scrolling
1583
+ on(d.wrapper, "scroll", function() { d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; });
1584
+
1585
+ var resizeTimer;
1586
+ function onResize() {
1587
+ if (resizeTimer == null) resizeTimer = setTimeout(function() {
1588
+ resizeTimer = null;
1589
+ // Might be a text scaling operation, clear size caches.
1590
+ d.cachedCharWidth = d.cachedTextHeight = knownScrollbarWidth = null;
1591
+ clearCaches(cm);
1592
+ runInOp(cm, bind(regChange, cm));
1593
+ }, 100);
1594
+ }
1595
+ on(window, "resize", onResize);
1596
+ // Above handler holds on to the editor and its data structures.
1597
+ // Here we poll to unregister it when the editor is no longer in
1598
+ // the document, so that it can be garbage-collected.
1599
+ function unregister() {
1600
+ for (var p = d.wrapper.parentNode; p && p != document.body; p = p.parentNode) {}
1601
+ if (p) setTimeout(unregister, 5000);
1602
+ else off(window, "resize", onResize);
1603
+ }
1604
+ setTimeout(unregister, 5000);
1605
+
1606
+ on(d.input, "keyup", operation(cm, function(e) {
1607
+ if (signalDOMEvent(cm, e) || cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
1608
+ if (e.keyCode == 16) cm.doc.sel.shift = false;
1609
+ }));
1610
+ on(d.input, "input", function() {
1611
+ if (ie && !ie_lt9 && cm.display.inputHasSelection) cm.display.inputHasSelection = null;
1612
+ fastPoll(cm);
1613
+ });
1614
+ on(d.input, "keydown", operation(cm, onKeyDown));
1615
+ on(d.input, "keypress", operation(cm, onKeyPress));
1616
+ on(d.input, "focus", bind(onFocus, cm));
1617
+ on(d.input, "blur", bind(onBlur, cm));
1618
+
1619
+ function drag_(e) {
1620
+ if (signalDOMEvent(cm, e) || cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e))) return;
1621
+ e_stop(e);
1622
+ }
1623
+ if (cm.options.dragDrop) {
1624
+ on(d.scroller, "dragstart", function(e){onDragStart(cm, e);});
1625
+ on(d.scroller, "dragenter", drag_);
1626
+ on(d.scroller, "dragover", drag_);
1627
+ on(d.scroller, "drop", operation(cm, onDrop));
1628
+ }
1629
+ on(d.scroller, "paste", function(e) {
1630
+ if (eventInWidget(d, e)) return;
1631
+ focusInput(cm);
1632
+ fastPoll(cm);
1633
+ });
1634
+ on(d.input, "paste", function() {
1635
+ // Workaround for webkit bug https://bugs.webkit.org/show_bug.cgi?id=90206
1636
+ // Add a char to the end of textarea before paste occur so that
1637
+ // selection doesn't span to the end of textarea.
1638
+ if (webkit && !cm.state.fakedLastChar && !(new Date - cm.state.lastMiddleDown < 200)) {
1639
+ var start = d.input.selectionStart, end = d.input.selectionEnd;
1640
+ d.input.value += "$";
1641
+ d.input.selectionStart = start;
1642
+ d.input.selectionEnd = end;
1643
+ cm.state.fakedLastChar = true;
1644
+ }
1645
+ cm.state.pasteIncoming = true;
1646
+ fastPoll(cm);
1647
+ });
1648
+
1649
+ function prepareCopy() {
1650
+ if (d.inaccurateSelection) {
1651
+ d.prevInput = "";
1652
+ d.inaccurateSelection = false;
1653
+ d.input.value = cm.getSelection();
1654
+ selectInput(d.input);
1655
+ }
1656
+ }
1657
+ on(d.input, "cut", prepareCopy);
1658
+ on(d.input, "copy", prepareCopy);
1659
+
1660
+ // Needed to handle Tab key in KHTML
1661
+ if (khtml) on(d.sizer, "mouseup", function() {
1662
+ if (document.activeElement == d.input) d.input.blur();
1663
+ focusInput(cm);
1664
+ });
1665
+ }
1666
+
1667
+ function eventInWidget(display, e) {
1668
+ for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {
1669
+ if (!n || n.ignoreEvents || n.parentNode == display.sizer && n != display.mover) return true;
1670
+ }
1671
+ }
1672
+
1673
+ function posFromMouse(cm, e, liberal) {
1674
+ var display = cm.display;
1675
+ if (!liberal) {
1676
+ var target = e_target(e);
1677
+ if (target == display.scrollbarH || target == display.scrollbarH.firstChild ||
1678
+ target == display.scrollbarV || target == display.scrollbarV.firstChild ||
1679
+ target == display.scrollbarFiller || target == display.gutterFiller) return null;
1680
+ }
1681
+ var x, y, space = getRect(display.lineSpace);
1682
+ // Fails unpredictably on IE[67] when mouse is dragged around quickly.
1683
+ try { x = e.clientX; y = e.clientY; } catch (e) { return null; }
1684
+ return coordsChar(cm, x - space.left, y - space.top);
1685
+ }
1686
+
1687
+ var lastClick, lastDoubleClick;
1688
+ function onMouseDown(e) {
1689
+ if (signalDOMEvent(this, e)) return;
1690
+ var cm = this, display = cm.display, doc = cm.doc, sel = doc.sel;
1691
+ sel.shift = e.shiftKey;
1692
+
1693
+ if (eventInWidget(display, e)) {
1694
+ if (!webkit) {
1695
+ display.scroller.draggable = false;
1696
+ setTimeout(function(){display.scroller.draggable = true;}, 100);
1697
+ }
1698
+ return;
1699
+ }
1700
+ if (clickInGutter(cm, e)) return;
1701
+ var start = posFromMouse(cm, e);
1702
+
1703
+ switch (e_button(e)) {
1704
+ case 3:
1705
+ if (captureMiddleClick) onContextMenu.call(cm, cm, e);
1706
+ return;
1707
+ case 2:
1708
+ if (webkit) cm.state.lastMiddleDown = +new Date;
1709
+ if (start) extendSelection(cm.doc, start);
1710
+ setTimeout(bind(focusInput, cm), 20);
1711
+ e_preventDefault(e);
1712
+ return;
1713
+ }
1714
+ // For button 1, if it was clicked inside the editor
1715
+ // (posFromMouse returning non-null), we have to adjust the
1716
+ // selection.
1717
+ if (!start) {if (e_target(e) == display.scroller) e_preventDefault(e); return;}
1718
+
1719
+ if (!cm.state.focused) onFocus(cm);
1720
+
1721
+ var now = +new Date, type = "single";
1722
+ if (lastDoubleClick && lastDoubleClick.time > now - 400 && posEq(lastDoubleClick.pos, start)) {
1723
+ type = "triple";
1724
+ e_preventDefault(e);
1725
+ setTimeout(bind(focusInput, cm), 20);
1726
+ selectLine(cm, start.line);
1727
+ } else if (lastClick && lastClick.time > now - 400 && posEq(lastClick.pos, start)) {
1728
+ type = "double";
1729
+ lastDoubleClick = {time: now, pos: start};
1730
+ e_preventDefault(e);
1731
+ var word = findWordAt(getLine(doc, start.line).text, start);
1732
+ extendSelection(cm.doc, word.from, word.to);
1733
+ } else { lastClick = {time: now, pos: start}; }
1734
+
1735
+ var last = start;
1736
+ if (cm.options.dragDrop && dragAndDrop && !isReadOnly(cm) && !posEq(sel.from, sel.to) &&
1737
+ !posLess(start, sel.from) && !posLess(sel.to, start) && type == "single") {
1738
+ var dragEnd = operation(cm, function(e2) {
1739
+ if (webkit) display.scroller.draggable = false;
1740
+ cm.state.draggingText = false;
1741
+ off(document, "mouseup", dragEnd);
1742
+ off(display.scroller, "drop", dragEnd);
1743
+ if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) {
1744
+ e_preventDefault(e2);
1745
+ extendSelection(cm.doc, start);
1746
+ focusInput(cm);
1747
+ }
1748
+ });
1749
+ // Let the drag handler handle this.
1750
+ if (webkit) display.scroller.draggable = true;
1751
+ cm.state.draggingText = dragEnd;
1752
+ // IE's approach to draggable
1753
+ if (display.scroller.dragDrop) display.scroller.dragDrop();
1754
+ on(document, "mouseup", dragEnd);
1755
+ on(display.scroller, "drop", dragEnd);
1756
+ return;
1757
+ }
1758
+ e_preventDefault(e);
1759
+ if (type == "single") extendSelection(cm.doc, clipPos(doc, start));
1760
+
1761
+ var startstart = sel.from, startend = sel.to, lastPos = start;
1762
+
1763
+ function doSelect(cur) {
1764
+ if (posEq(lastPos, cur)) return;
1765
+ lastPos = cur;
1766
+
1767
+ if (type == "single") {
1768
+ extendSelection(cm.doc, clipPos(doc, start), cur);
1769
+ return;
1770
+ }
1771
+
1772
+ startstart = clipPos(doc, startstart);
1773
+ startend = clipPos(doc, startend);
1774
+ if (type == "double") {
1775
+ var word = findWordAt(getLine(doc, cur.line).text, cur);
1776
+ if (posLess(cur, startstart)) extendSelection(cm.doc, word.from, startend);
1777
+ else extendSelection(cm.doc, startstart, word.to);
1778
+ } else if (type == "triple") {
1779
+ if (posLess(cur, startstart)) extendSelection(cm.doc, startend, clipPos(doc, Pos(cur.line, 0)));
1780
+ else extendSelection(cm.doc, startstart, clipPos(doc, Pos(cur.line + 1, 0)));
1781
+ }
1782
+ }
1783
+
1784
+ var editorSize = getRect(display.wrapper);
1785
+ // Used to ensure timeout re-tries don't fire when another extend
1786
+ // happened in the meantime (clearTimeout isn't reliable -- at
1787
+ // least on Chrome, the timeouts still happen even when cleared,
1788
+ // if the clear happens after their scheduled firing time).
1789
+ var counter = 0;
1790
+
1791
+ function extend(e) {
1792
+ var curCount = ++counter;
1793
+ var cur = posFromMouse(cm, e, true);
1794
+ if (!cur) return;
1795
+ if (!posEq(cur, last)) {
1796
+ if (!cm.state.focused) onFocus(cm);
1797
+ last = cur;
1798
+ doSelect(cur);
1799
+ var visible = visibleLines(display, doc);
1800
+ if (cur.line >= visible.to || cur.line < visible.from)
1801
+ setTimeout(operation(cm, function(){if (counter == curCount) extend(e);}), 150);
1802
+ } else {
1803
+ var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0;
1804
+ if (outside) setTimeout(operation(cm, function() {
1805
+ if (counter != curCount) return;
1806
+ display.scroller.scrollTop += outside;
1807
+ extend(e);
1808
+ }), 50);
1809
+ }
1810
+ }
1811
+
1812
+ function done(e) {
1813
+ counter = Infinity;
1814
+ e_preventDefault(e);
1815
+ focusInput(cm);
1816
+ off(document, "mousemove", move);
1817
+ off(document, "mouseup", up);
1818
+ }
1819
+
1820
+ var move = operation(cm, function(e) {
1821
+ if (!ie && !e_button(e)) done(e);
1822
+ else extend(e);
1823
+ });
1824
+ var up = operation(cm, done);
1825
+ on(document, "mousemove", move);
1826
+ on(document, "mouseup", up);
1827
+ }
1828
+
1829
+ function gutterEvent(cm, e, type, prevent, signalfn) {
1830
+ try { var mX = e.clientX, mY = e.clientY; }
1831
+ catch(e) { return false; }
1832
+ if (mX >= Math.floor(getRect(cm.display.gutters).right)) return false;
1833
+ if (prevent) e_preventDefault(e);
1834
+
1835
+ var display = cm.display;
1836
+ var lineBox = getRect(display.lineDiv);
1837
+
1838
+ if (mY > lineBox.bottom || !hasHandler(cm, type)) return e_defaultPrevented(e);
1839
+ mY -= lineBox.top - display.viewOffset;
1840
+
1841
+ for (var i = 0; i < cm.options.gutters.length; ++i) {
1842
+ var g = display.gutters.childNodes[i];
1843
+ if (g && getRect(g).right >= mX) {
1844
+ var line = lineAtHeight(cm.doc, mY);
1845
+ var gutter = cm.options.gutters[i];
1846
+ signalfn(cm, type, cm, line, gutter, e);
1847
+ return e_defaultPrevented(e);
1848
+ }
1849
+ }
1850
+ }
1851
+
1852
+ function contextMenuInGutter(cm, e) {
1853
+ if (!hasHandler(cm, "gutterContextMenu")) return false;
1854
+ return gutterEvent(cm, e, "gutterContextMenu", false, signal);
1855
+ }
1856
+
1857
+ function clickInGutter(cm, e) {
1858
+ return gutterEvent(cm, e, "gutterClick", true, signalLater);
1859
+ }
1860
+
1861
+ // Kludge to work around strange IE behavior where it'll sometimes
1862
+ // re-fire a series of drag-related events right after the drop (#1551)
1863
+ var lastDrop = 0;
1864
+
1865
+ function onDrop(e) {
1866
+ var cm = this;
1867
+ if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e) || (cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e))))
1868
+ return;
1869
+ e_preventDefault(e);
1870
+ if (ie) lastDrop = +new Date;
1871
+ var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files;
1872
+ if (!pos || isReadOnly(cm)) return;
1873
+ if (files && files.length && window.FileReader && window.File) {
1874
+ var n = files.length, text = Array(n), read = 0;
1875
+ var loadFile = function(file, i) {
1876
+ var reader = new FileReader;
1877
+ reader.onload = function() {
1878
+ text[i] = reader.result;
1879
+ if (++read == n) {
1880
+ pos = clipPos(cm.doc, pos);
1881
+ makeChange(cm.doc, {from: pos, to: pos, text: splitLines(text.join("\n")), origin: "paste"}, "around");
1882
+ }
1883
+ };
1884
+ reader.readAsText(file);
1885
+ };
1886
+ for (var i = 0; i < n; ++i) loadFile(files[i], i);
1887
+ } else {
1888
+ // Don't do a replace if the drop happened inside of the selected text.
1889
+ if (cm.state.draggingText && !(posLess(pos, cm.doc.sel.from) || posLess(cm.doc.sel.to, pos))) {
1890
+ cm.state.draggingText(e);
1891
+ // Ensure the editor is re-focused
1892
+ setTimeout(bind(focusInput, cm), 20);
1893
+ return;
1894
+ }
1895
+ try {
1896
+ var text = e.dataTransfer.getData("Text");
1897
+ if (text) {
1898
+ var curFrom = cm.doc.sel.from, curTo = cm.doc.sel.to;
1899
+ setSelection(cm.doc, pos, pos);
1900
+ if (cm.state.draggingText) replaceRange(cm.doc, "", curFrom, curTo, "paste");
1901
+ cm.replaceSelection(text, null, "paste");
1902
+ focusInput(cm);
1903
+ onFocus(cm);
1904
+ }
1905
+ }
1906
+ catch(e){}
1907
+ }
1908
+ }
1909
+
1910
+ function onDragStart(cm, e) {
1911
+ if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return; }
1912
+ if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) return;
1913
+
1914
+ var txt = cm.getSelection();
1915
+ e.dataTransfer.setData("Text", txt);
1916
+
1917
+ // Use dummy image instead of default browsers image.
1918
+ // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
1919
+ if (e.dataTransfer.setDragImage && !safari) {
1920
+ var img = elt("img", null, null, "position: fixed; left: 0; top: 0;");
1921
+ img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
1922
+ if (opera) {
1923
+ img.width = img.height = 1;
1924
+ cm.display.wrapper.appendChild(img);
1925
+ // Force a relayout, or Opera won't use our image for some obscure reason
1926
+ img._top = img.offsetTop;
1927
+ }
1928
+ e.dataTransfer.setDragImage(img, 0, 0);
1929
+ if (opera) img.parentNode.removeChild(img);
1930
+ }
1931
+ }
1932
+
1933
+ function setScrollTop(cm, val) {
1934
+ if (Math.abs(cm.doc.scrollTop - val) < 2) return;
1935
+ cm.doc.scrollTop = val;
1936
+ if (!gecko) updateDisplay(cm, [], val);
1937
+ if (cm.display.scroller.scrollTop != val) cm.display.scroller.scrollTop = val;
1938
+ if (cm.display.scrollbarV.scrollTop != val) cm.display.scrollbarV.scrollTop = val;
1939
+ if (gecko) updateDisplay(cm, []);
1940
+ startWorker(cm, 100);
1941
+ }
1942
+ function setScrollLeft(cm, val, isScroller) {
1943
+ if (isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) return;
1944
+ val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth);
1945
+ cm.doc.scrollLeft = val;
1946
+ alignHorizontally(cm);
1947
+ if (cm.display.scroller.scrollLeft != val) cm.display.scroller.scrollLeft = val;
1948
+ if (cm.display.scrollbarH.scrollLeft != val) cm.display.scrollbarH.scrollLeft = val;
1949
+ }
1950
+
1951
+ // Since the delta values reported on mouse wheel events are
1952
+ // unstandardized between browsers and even browser versions, and
1953
+ // generally horribly unpredictable, this code starts by measuring
1954
+ // the scroll effect that the first few mouse wheel events have,
1955
+ // and, from that, detects the way it can convert deltas to pixel
1956
+ // offsets afterwards.
1957
+ //
1958
+ // The reason we want to know the amount a wheel event will scroll
1959
+ // is that it gives us a chance to update the display before the
1960
+ // actual scrolling happens, reducing flickering.
1961
+
1962
+ var wheelSamples = 0, wheelPixelsPerUnit = null;
1963
+ // Fill in a browser-detected starting value on browsers where we
1964
+ // know one. These don't have to be accurate -- the result of them
1965
+ // being wrong would just be a slight flicker on the first wheel
1966
+ // scroll (if it is large enough).
1967
+ if (ie) wheelPixelsPerUnit = -.53;
1968
+ else if (gecko) wheelPixelsPerUnit = 15;
1969
+ else if (chrome) wheelPixelsPerUnit = -.7;
1970
+ else if (safari) wheelPixelsPerUnit = -1/3;
1971
+
1972
+ function onScrollWheel(cm, e) {
1973
+ var dx = e.wheelDeltaX, dy = e.wheelDeltaY;
1974
+ if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) dx = e.detail;
1975
+ if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) dy = e.detail;
1976
+ else if (dy == null) dy = e.wheelDelta;
1977
+
1978
+ var display = cm.display, scroll = display.scroller;
1979
+ // Quit if there's nothing to scroll here
1980
+ if (!(dx && scroll.scrollWidth > scroll.clientWidth ||
1981
+ dy && scroll.scrollHeight > scroll.clientHeight)) return;
1982
+
1983
+ // Webkit browsers on OS X abort momentum scrolls when the target
1984
+ // of the scroll event is removed from the scrollable element.
1985
+ // This hack (see related code in patchDisplay) makes sure the
1986
+ // element is kept around.
1987
+ if (dy && mac && webkit) {
1988
+ for (var cur = e.target; cur != scroll; cur = cur.parentNode) {
1989
+ if (cur.lineObj) {
1990
+ cm.display.currentWheelTarget = cur;
1991
+ break;
1992
+ }
1993
+ }
1994
+ }
1995
+
1996
+ // On some browsers, horizontal scrolling will cause redraws to
1997
+ // happen before the gutter has been realigned, causing it to
1998
+ // wriggle around in a most unseemly way. When we have an
1999
+ // estimated pixels/delta value, we just handle horizontal
2000
+ // scrolling entirely here. It'll be slightly off from native, but
2001
+ // better than glitching out.
2002
+ if (dx && !gecko && !opera && wheelPixelsPerUnit != null) {
2003
+ if (dy)
2004
+ setScrollTop(cm, Math.max(0, Math.min(scroll.scrollTop + dy * wheelPixelsPerUnit, scroll.scrollHeight - scroll.clientHeight)));
2005
+ setScrollLeft(cm, Math.max(0, Math.min(scroll.scrollLeft + dx * wheelPixelsPerUnit, scroll.scrollWidth - scroll.clientWidth)));
2006
+ e_preventDefault(e);
2007
+ display.wheelStartX = null; // Abort measurement, if in progress
2008
+ return;
2009
+ }
2010
+
2011
+ if (dy && wheelPixelsPerUnit != null) {
2012
+ var pixels = dy * wheelPixelsPerUnit;
2013
+ var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight;
2014
+ if (pixels < 0) top = Math.max(0, top + pixels - 50);
2015
+ else bot = Math.min(cm.doc.height, bot + pixels + 50);
2016
+ updateDisplay(cm, [], {top: top, bottom: bot});
2017
+ }
2018
+
2019
+ if (wheelSamples < 20) {
2020
+ if (display.wheelStartX == null) {
2021
+ display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop;
2022
+ display.wheelDX = dx; display.wheelDY = dy;
2023
+ setTimeout(function() {
2024
+ if (display.wheelStartX == null) return;
2025
+ var movedX = scroll.scrollLeft - display.wheelStartX;
2026
+ var movedY = scroll.scrollTop - display.wheelStartY;
2027
+ var sample = (movedY && display.wheelDY && movedY / display.wheelDY) ||
2028
+ (movedX && display.wheelDX && movedX / display.wheelDX);
2029
+ display.wheelStartX = display.wheelStartY = null;
2030
+ if (!sample) return;
2031
+ wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1);
2032
+ ++wheelSamples;
2033
+ }, 200);
2034
+ } else {
2035
+ display.wheelDX += dx; display.wheelDY += dy;
2036
+ }
2037
+ }
2038
+ }
2039
+
2040
+ function doHandleBinding(cm, bound, dropShift) {
2041
+ if (typeof bound == "string") {
2042
+ bound = commands[bound];
2043
+ if (!bound) return false;
2044
+ }
2045
+ // Ensure previous input has been read, so that the handler sees a
2046
+ // consistent view of the document
2047
+ if (cm.display.pollingFast && readInput(cm)) cm.display.pollingFast = false;
2048
+ var doc = cm.doc, prevShift = doc.sel.shift, done = false;
2049
+ try {
2050
+ if (isReadOnly(cm)) cm.state.suppressEdits = true;
2051
+ if (dropShift) doc.sel.shift = false;
2052
+ done = bound(cm) != Pass;
2053
+ } finally {
2054
+ doc.sel.shift = prevShift;
2055
+ cm.state.suppressEdits = false;
2056
+ }
2057
+ return done;
2058
+ }
2059
+
2060
+ function allKeyMaps(cm) {
2061
+ var maps = cm.state.keyMaps.slice(0);
2062
+ if (cm.options.extraKeys) maps.push(cm.options.extraKeys);
2063
+ maps.push(cm.options.keyMap);
2064
+ return maps;
2065
+ }
2066
+
2067
+ var maybeTransition;
2068
+ function handleKeyBinding(cm, e) {
2069
+ // Handle auto keymap transitions
2070
+ var startMap = getKeyMap(cm.options.keyMap), next = startMap.auto;
2071
+ clearTimeout(maybeTransition);
2072
+ if (next && !isModifierKey(e)) maybeTransition = setTimeout(function() {
2073
+ if (getKeyMap(cm.options.keyMap) == startMap) {
2074
+ cm.options.keyMap = (next.call ? next.call(null, cm) : next);
2075
+ keyMapChanged(cm);
2076
+ }
2077
+ }, 50);
2078
+
2079
+ var name = keyName(e, true), handled = false;
2080
+ if (!name) return false;
2081
+ var keymaps = allKeyMaps(cm);
2082
+
2083
+ if (e.shiftKey) {
2084
+ // First try to resolve full name (including 'Shift-'). Failing
2085
+ // that, see if there is a cursor-motion command (starting with
2086
+ // 'go') bound to the keyname without 'Shift-'.
2087
+ handled = lookupKey("Shift-" + name, keymaps, function(b) {return doHandleBinding(cm, b, true);})
2088
+ || lookupKey(name, keymaps, function(b) {
2089
+ if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion)
2090
+ return doHandleBinding(cm, b);
2091
+ });
2092
+ } else {
2093
+ handled = lookupKey(name, keymaps, function(b) { return doHandleBinding(cm, b); });
2094
+ }
2095
+
2096
+ if (handled) {
2097
+ e_preventDefault(e);
2098
+ restartBlink(cm);
2099
+ if (ie_lt9) { e.oldKeyCode = e.keyCode; e.keyCode = 0; }
2100
+ signalLater(cm, "keyHandled", cm, name, e);
2101
+ }
2102
+ return handled;
2103
+ }
2104
+
2105
+ function handleCharBinding(cm, e, ch) {
2106
+ var handled = lookupKey("'" + ch + "'", allKeyMaps(cm),
2107
+ function(b) { return doHandleBinding(cm, b, true); });
2108
+ if (handled) {
2109
+ e_preventDefault(e);
2110
+ restartBlink(cm);
2111
+ signalLater(cm, "keyHandled", cm, "'" + ch + "'", e);
2112
+ }
2113
+ return handled;
2114
+ }
2115
+
2116
+ var lastStoppedKey = null;
2117
+ function onKeyDown(e) {
2118
+ var cm = this;
2119
+ if (!cm.state.focused) onFocus(cm);
2120
+ if (signalDOMEvent(cm, e) || cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
2121
+ if (ie && e.keyCode == 27) e.returnValue = false;
2122
+ var code = e.keyCode;
2123
+ // IE does strange things with escape.
2124
+ cm.doc.sel.shift = code == 16 || e.shiftKey;
2125
+ // First give onKeyEvent option a chance to handle this.
2126
+ var handled = handleKeyBinding(cm, e);
2127
+ if (opera) {
2128
+ lastStoppedKey = handled ? code : null;
2129
+ // Opera has no cut event... we try to at least catch the key combo
2130
+ if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey))
2131
+ cm.replaceSelection("");
2132
+ }
2133
+ }
2134
+
2135
+ function onKeyPress(e) {
2136
+ var cm = this;
2137
+ if (signalDOMEvent(cm, e) || cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
2138
+ var keyCode = e.keyCode, charCode = e.charCode;
2139
+ if (opera && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;}
2140
+ if (((opera && (!e.which || e.which < 10)) || khtml) && handleKeyBinding(cm, e)) return;
2141
+ var ch = String.fromCharCode(charCode == null ? keyCode : charCode);
2142
+ if (this.options.electricChars && this.doc.mode.electricChars &&
2143
+ this.options.smartIndent && !isReadOnly(this) &&
2144
+ this.doc.mode.electricChars.indexOf(ch) > -1)
2145
+ setTimeout(operation(cm, function() {indentLine(cm, cm.doc.sel.to.line, "smart");}), 75);
2146
+ if (handleCharBinding(cm, e, ch)) return;
2147
+ if (ie && !ie_lt9) cm.display.inputHasSelection = null;
2148
+ fastPoll(cm);
2149
+ }
2150
+
2151
+ function onFocus(cm) {
2152
+ if (cm.options.readOnly == "nocursor") return;
2153
+ if (!cm.state.focused) {
2154
+ signal(cm, "focus", cm);
2155
+ cm.state.focused = true;
2156
+ if (cm.display.wrapper.className.search(/\bCodeMirror-focused\b/) == -1)
2157
+ cm.display.wrapper.className += " CodeMirror-focused";
2158
+ if (!cm.curOp) {
2159
+ resetInput(cm, true);
2160
+ if (webkit) setTimeout(bind(resetInput, cm, true), 0); // Issue #1730
2161
+ }
2162
+ }
2163
+ slowPoll(cm);
2164
+ restartBlink(cm);
2165
+ }
2166
+ function onBlur(cm) {
2167
+ if (cm.state.focused) {
2168
+ signal(cm, "blur", cm);
2169
+ cm.state.focused = false;
2170
+ cm.display.wrapper.className = cm.display.wrapper.className.replace(" CodeMirror-focused", "");
2171
+ }
2172
+ clearInterval(cm.display.blinker);
2173
+ setTimeout(function() {if (!cm.state.focused) cm.doc.sel.shift = false;}, 150);
2174
+ }
2175
+
2176
+ var detectingSelectAll;
2177
+ function onContextMenu(cm, e) {
2178
+ if (signalDOMEvent(cm, e, "contextmenu")) return;
2179
+ var display = cm.display, sel = cm.doc.sel;
2180
+ if (eventInWidget(display, e) || contextMenuInGutter(cm, e)) return;
2181
+
2182
+ var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop;
2183
+ if (!pos || opera) return; // Opera is difficult.
2184
+ if (posEq(sel.from, sel.to) || posLess(pos, sel.from) || !posLess(pos, sel.to))
2185
+ operation(cm, setSelection)(cm.doc, pos, pos);
2186
+
2187
+ var oldCSS = display.input.style.cssText;
2188
+ display.inputDiv.style.position = "absolute";
2189
+ display.input.style.cssText = "position: fixed; width: 30px; height: 30px; top: " + (e.clientY - 5) +
2190
+ "px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: white; outline: none;" +
2191
+ "border-width: 0; outline: none; overflow: hidden; opacity: .05; -ms-opacity: .05; filter: alpha(opacity=5);";
2192
+ focusInput(cm);
2193
+ resetInput(cm, true);
2194
+ // Adds "Select all" to context menu in FF
2195
+ if (posEq(sel.from, sel.to)) display.input.value = display.prevInput = " ";
2196
+
2197
+ function prepareSelectAllHack() {
2198
+ if (display.input.selectionStart != null) {
2199
+ var extval = display.input.value = "\u200b" + (posEq(sel.from, sel.to) ? "" : display.input.value);
2200
+ display.prevInput = "\u200b";
2201
+ display.input.selectionStart = 1; display.input.selectionEnd = extval.length;
2202
+ }
2203
+ }
2204
+ function rehide() {
2205
+ display.inputDiv.style.position = "relative";
2206
+ display.input.style.cssText = oldCSS;
2207
+ if (ie_lt9) display.scrollbarV.scrollTop = display.scroller.scrollTop = scrollPos;
2208
+ slowPoll(cm);
2209
+
2210
+ // Try to detect the user choosing select-all
2211
+ if (display.input.selectionStart != null) {
2212
+ if (!ie || ie_lt9) prepareSelectAllHack();
2213
+ clearTimeout(detectingSelectAll);
2214
+ var i = 0, poll = function(){
2215
+ if (display.prevInput == " " && display.input.selectionStart == 0)
2216
+ operation(cm, commands.selectAll)(cm);
2217
+ else if (i++ < 10) detectingSelectAll = setTimeout(poll, 500);
2218
+ else resetInput(cm);
2219
+ };
2220
+ detectingSelectAll = setTimeout(poll, 200);
2221
+ }
2222
+ }
2223
+
2224
+ if (ie && !ie_lt9) prepareSelectAllHack();
2225
+ if (captureMiddleClick) {
2226
+ e_stop(e);
2227
+ var mouseup = function() {
2228
+ off(window, "mouseup", mouseup);
2229
+ setTimeout(rehide, 20);
2230
+ };
2231
+ on(window, "mouseup", mouseup);
2232
+ } else {
2233
+ setTimeout(rehide, 50);
2234
+ }
2235
+ }
2236
+
2237
+ // UPDATING
2238
+
2239
+ var changeEnd = CodeMirror.changeEnd = function(change) {
2240
+ if (!change.text) return change.to;
2241
+ return Pos(change.from.line + change.text.length - 1,
2242
+ lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0));
2243
+ };
2244
+
2245
+ // Make sure a position will be valid after the given change.
2246
+ function clipPostChange(doc, change, pos) {
2247
+ if (!posLess(change.from, pos)) return clipPos(doc, pos);
2248
+ var diff = (change.text.length - 1) - (change.to.line - change.from.line);
2249
+ if (pos.line > change.to.line + diff) {
2250
+ var preLine = pos.line - diff, lastLine = doc.first + doc.size - 1;
2251
+ if (preLine > lastLine) return Pos(lastLine, getLine(doc, lastLine).text.length);
2252
+ return clipToLen(pos, getLine(doc, preLine).text.length);
2253
+ }
2254
+ if (pos.line == change.to.line + diff)
2255
+ return clipToLen(pos, lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0) +
2256
+ getLine(doc, change.to.line).text.length - change.to.ch);
2257
+ var inside = pos.line - change.from.line;
2258
+ return clipToLen(pos, change.text[inside].length + (inside ? 0 : change.from.ch));
2259
+ }
2260
+
2261
+ // Hint can be null|"end"|"start"|"around"|{anchor,head}
2262
+ function computeSelAfterChange(doc, change, hint) {
2263
+ if (hint && typeof hint == "object") // Assumed to be {anchor, head} object
2264
+ return {anchor: clipPostChange(doc, change, hint.anchor),
2265
+ head: clipPostChange(doc, change, hint.head)};
2266
+
2267
+ if (hint == "start") return {anchor: change.from, head: change.from};
2268
+
2269
+ var end = changeEnd(change);
2270
+ if (hint == "around") return {anchor: change.from, head: end};
2271
+ if (hint == "end") return {anchor: end, head: end};
2272
+
2273
+ // hint is null, leave the selection alone as much as possible
2274
+ var adjustPos = function(pos) {
2275
+ if (posLess(pos, change.from)) return pos;
2276
+ if (!posLess(change.to, pos)) return end;
2277
+
2278
+ var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch;
2279
+ if (pos.line == change.to.line) ch += end.ch - change.to.ch;
2280
+ return Pos(line, ch);
2281
+ };
2282
+ return {anchor: adjustPos(doc.sel.anchor), head: adjustPos(doc.sel.head)};
2283
+ }
2284
+
2285
+ function filterChange(doc, change, update) {
2286
+ var obj = {
2287
+ canceled: false,
2288
+ from: change.from,
2289
+ to: change.to,
2290
+ text: change.text,
2291
+ origin: change.origin,
2292
+ cancel: function() { this.canceled = true; }
2293
+ };
2294
+ if (update) obj.update = function(from, to, text, origin) {
2295
+ if (from) this.from = clipPos(doc, from);
2296
+ if (to) this.to = clipPos(doc, to);
2297
+ if (text) this.text = text;
2298
+ if (origin !== undefined) this.origin = origin;
2299
+ };
2300
+ signal(doc, "beforeChange", doc, obj);
2301
+ if (doc.cm) signal(doc.cm, "beforeChange", doc.cm, obj);
2302
+
2303
+ if (obj.canceled) return null;
2304
+ return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin};
2305
+ }
2306
+
2307
+ // Replace the range from from to to by the strings in replacement.
2308
+ // change is a {from, to, text [, origin]} object
2309
+ function makeChange(doc, change, selUpdate, ignoreReadOnly) {
2310
+ if (doc.cm) {
2311
+ if (!doc.cm.curOp) return operation(doc.cm, makeChange)(doc, change, selUpdate, ignoreReadOnly);
2312
+ if (doc.cm.state.suppressEdits) return;
2313
+ }
2314
+
2315
+ if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) {
2316
+ change = filterChange(doc, change, true);
2317
+ if (!change) return;
2318
+ }
2319
+
2320
+ // Possibly split or suppress the update based on the presence
2321
+ // of read-only spans in its range.
2322
+ var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to);
2323
+ if (split) {
2324
+ for (var i = split.length - 1; i >= 1; --i)
2325
+ makeChangeNoReadonly(doc, {from: split[i].from, to: split[i].to, text: [""]});
2326
+ if (split.length)
2327
+ makeChangeNoReadonly(doc, {from: split[0].from, to: split[0].to, text: change.text}, selUpdate);
2328
+ } else {
2329
+ makeChangeNoReadonly(doc, change, selUpdate);
2330
+ }
2331
+ }
2332
+
2333
+ function makeChangeNoReadonly(doc, change, selUpdate) {
2334
+ if (change.text.length == 1 && change.text[0] == "" && posEq(change.from, change.to)) return;
2335
+ var selAfter = computeSelAfterChange(doc, change, selUpdate);
2336
+ addToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN);
2337
+
2338
+ makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change));
2339
+ var rebased = [];
2340
+
2341
+ linkedDocs(doc, function(doc, sharedHist) {
2342
+ if (!sharedHist && indexOf(rebased, doc.history) == -1) {
2343
+ rebaseHist(doc.history, change);
2344
+ rebased.push(doc.history);
2345
+ }
2346
+ makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change));
2347
+ });
2348
+ }
2349
+
2350
+ function makeChangeFromHistory(doc, type) {
2351
+ if (doc.cm && doc.cm.state.suppressEdits) return;
2352
+
2353
+ var hist = doc.history;
2354
+ var event = (type == "undo" ? hist.done : hist.undone).pop();
2355
+ if (!event) return;
2356
+
2357
+ var anti = {changes: [], anchorBefore: event.anchorAfter, headBefore: event.headAfter,
2358
+ anchorAfter: event.anchorBefore, headAfter: event.headBefore,
2359
+ generation: hist.generation};
2360
+ (type == "undo" ? hist.undone : hist.done).push(anti);
2361
+ hist.generation = event.generation || ++hist.maxGeneration;
2362
+
2363
+ var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange");
2364
+
2365
+ for (var i = event.changes.length - 1; i >= 0; --i) {
2366
+ var change = event.changes[i];
2367
+ change.origin = type;
2368
+ if (filter && !filterChange(doc, change, false)) {
2369
+ (type == "undo" ? hist.done : hist.undone).length = 0;
2370
+ return;
2371
+ }
2372
+
2373
+ anti.changes.push(historyChangeFromChange(doc, change));
2374
+
2375
+ var after = i ? computeSelAfterChange(doc, change, null)
2376
+ : {anchor: event.anchorBefore, head: event.headBefore};
2377
+ makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change));
2378
+ var rebased = [];
2379
+
2380
+ linkedDocs(doc, function(doc, sharedHist) {
2381
+ if (!sharedHist && indexOf(rebased, doc.history) == -1) {
2382
+ rebaseHist(doc.history, change);
2383
+ rebased.push(doc.history);
2384
+ }
2385
+ makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change));
2386
+ });
2387
+ }
2388
+ }
2389
+
2390
+ function shiftDoc(doc, distance) {
2391
+ function shiftPos(pos) {return Pos(pos.line + distance, pos.ch);}
2392
+ doc.first += distance;
2393
+ if (doc.cm) regChange(doc.cm, doc.first, doc.first, distance);
2394
+ doc.sel.head = shiftPos(doc.sel.head); doc.sel.anchor = shiftPos(doc.sel.anchor);
2395
+ doc.sel.from = shiftPos(doc.sel.from); doc.sel.to = shiftPos(doc.sel.to);
2396
+ }
2397
+
2398
+ function makeChangeSingleDoc(doc, change, selAfter, spans) {
2399
+ if (doc.cm && !doc.cm.curOp)
2400
+ return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans);
2401
+
2402
+ if (change.to.line < doc.first) {
2403
+ shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line));
2404
+ return;
2405
+ }
2406
+ if (change.from.line > doc.lastLine()) return;
2407
+
2408
+ // Clip the change to the size of this doc
2409
+ if (change.from.line < doc.first) {
2410
+ var shift = change.text.length - 1 - (doc.first - change.from.line);
2411
+ shiftDoc(doc, shift);
2412
+ change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch),
2413
+ text: [lst(change.text)], origin: change.origin};
2414
+ }
2415
+ var last = doc.lastLine();
2416
+ if (change.to.line > last) {
2417
+ change = {from: change.from, to: Pos(last, getLine(doc, last).text.length),
2418
+ text: [change.text[0]], origin: change.origin};
2419
+ }
2420
+
2421
+ change.removed = getBetween(doc, change.from, change.to);
2422
+
2423
+ if (!selAfter) selAfter = computeSelAfterChange(doc, change, null);
2424
+ if (doc.cm) makeChangeSingleDocInEditor(doc.cm, change, spans, selAfter);
2425
+ else updateDoc(doc, change, spans, selAfter);
2426
+ }
2427
+
2428
+ function makeChangeSingleDocInEditor(cm, change, spans, selAfter) {
2429
+ var doc = cm.doc, display = cm.display, from = change.from, to = change.to;
2430
+
2431
+ var recomputeMaxLength = false, checkWidthStart = from.line;
2432
+ if (!cm.options.lineWrapping) {
2433
+ checkWidthStart = lineNo(visualLine(doc, getLine(doc, from.line)));
2434
+ doc.iter(checkWidthStart, to.line + 1, function(line) {
2435
+ if (line == display.maxLine) {
2436
+ recomputeMaxLength = true;
2437
+ return true;
2438
+ }
2439
+ });
2440
+ }
2441
+
2442
+ if (!posLess(doc.sel.head, change.from) && !posLess(change.to, doc.sel.head))
2443
+ cm.curOp.cursorActivity = true;
2444
+
2445
+ updateDoc(doc, change, spans, selAfter, estimateHeight(cm));
2446
+
2447
+ if (!cm.options.lineWrapping) {
2448
+ doc.iter(checkWidthStart, from.line + change.text.length, function(line) {
2449
+ var len = lineLength(doc, line);
2450
+ if (len > display.maxLineLength) {
2451
+ display.maxLine = line;
2452
+ display.maxLineLength = len;
2453
+ display.maxLineChanged = true;
2454
+ recomputeMaxLength = false;
2455
+ }
2456
+ });
2457
+ if (recomputeMaxLength) cm.curOp.updateMaxLine = true;
2458
+ }
2459
+
2460
+ // Adjust frontier, schedule worker
2461
+ doc.frontier = Math.min(doc.frontier, from.line);
2462
+ startWorker(cm, 400);
2463
+
2464
+ var lendiff = change.text.length - (to.line - from.line) - 1;
2465
+ // Remember that these lines changed, for updating the display
2466
+ regChange(cm, from.line, to.line + 1, lendiff);
2467
+
2468
+ if (hasHandler(cm, "change")) {
2469
+ var changeObj = {from: from, to: to,
2470
+ text: change.text,
2471
+ removed: change.removed,
2472
+ origin: change.origin};
2473
+ if (cm.curOp.textChanged) {
2474
+ for (var cur = cm.curOp.textChanged; cur.next; cur = cur.next) {}
2475
+ cur.next = changeObj;
2476
+ } else cm.curOp.textChanged = changeObj;
2477
+ }
2478
+ }
2479
+
2480
+ function replaceRange(doc, code, from, to, origin) {
2481
+ if (!to) to = from;
2482
+ if (posLess(to, from)) { var tmp = to; to = from; from = tmp; }
2483
+ if (typeof code == "string") code = splitLines(code);
2484
+ makeChange(doc, {from: from, to: to, text: code, origin: origin}, null);
2485
+ }
2486
+
2487
+ // POSITION OBJECT
2488
+
2489
+ function Pos(line, ch) {
2490
+ if (!(this instanceof Pos)) return new Pos(line, ch);
2491
+ this.line = line; this.ch = ch;
2492
+ }
2493
+ CodeMirror.Pos = Pos;
2494
+
2495
+ function posEq(a, b) {return a.line == b.line && a.ch == b.ch;}
2496
+ function posLess(a, b) {return a.line < b.line || (a.line == b.line && a.ch < b.ch);}
2497
+ function copyPos(x) {return Pos(x.line, x.ch);}
2498
+
2499
+ // SELECTION
2500
+
2501
+ function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1));}
2502
+ function clipPos(doc, pos) {
2503
+ if (pos.line < doc.first) return Pos(doc.first, 0);
2504
+ var last = doc.first + doc.size - 1;
2505
+ if (pos.line > last) return Pos(last, getLine(doc, last).text.length);
2506
+ return clipToLen(pos, getLine(doc, pos.line).text.length);
2507
+ }
2508
+ function clipToLen(pos, linelen) {
2509
+ var ch = pos.ch;
2510
+ if (ch == null || ch > linelen) return Pos(pos.line, linelen);
2511
+ else if (ch < 0) return Pos(pos.line, 0);
2512
+ else return pos;
2513
+ }
2514
+ function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size;}
2515
+
2516
+ // If shift is held, this will move the selection anchor. Otherwise,
2517
+ // it'll set the whole selection.
2518
+ function extendSelection(doc, pos, other, bias) {
2519
+ if (doc.sel.shift || doc.sel.extend) {
2520
+ var anchor = doc.sel.anchor;
2521
+ if (other) {
2522
+ var posBefore = posLess(pos, anchor);
2523
+ if (posBefore != posLess(other, anchor)) {
2524
+ anchor = pos;
2525
+ pos = other;
2526
+ } else if (posBefore != posLess(pos, other)) {
2527
+ pos = other;
2528
+ }
2529
+ }
2530
+ setSelection(doc, anchor, pos, bias);
2531
+ } else {
2532
+ setSelection(doc, pos, other || pos, bias);
2533
+ }
2534
+ if (doc.cm) doc.cm.curOp.userSelChange = true;
2535
+ }
2536
+
2537
+ function filterSelectionChange(doc, anchor, head) {
2538
+ var obj = {anchor: anchor, head: head};
2539
+ signal(doc, "beforeSelectionChange", doc, obj);
2540
+ if (doc.cm) signal(doc.cm, "beforeSelectionChange", doc.cm, obj);
2541
+ obj.anchor = clipPos(doc, obj.anchor); obj.head = clipPos(doc, obj.head);
2542
+ return obj;
2543
+ }
2544
+
2545
+ // Update the selection. Last two args are only used by
2546
+ // updateDoc, since they have to be expressed in the line
2547
+ // numbers before the update.
2548
+ function setSelection(doc, anchor, head, bias, checkAtomic) {
2549
+ if (!checkAtomic && hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange")) {
2550
+ var filtered = filterSelectionChange(doc, anchor, head);
2551
+ head = filtered.head;
2552
+ anchor = filtered.anchor;
2553
+ }
2554
+
2555
+ var sel = doc.sel;
2556
+ sel.goalColumn = null;
2557
+ if (bias == null) bias = posLess(head, sel.head) ? -1 : 1;
2558
+ // Skip over atomic spans.
2559
+ if (checkAtomic || !posEq(anchor, sel.anchor))
2560
+ anchor = skipAtomic(doc, anchor, bias, checkAtomic != "push");
2561
+ if (checkAtomic || !posEq(head, sel.head))
2562
+ head = skipAtomic(doc, head, bias, checkAtomic != "push");
2563
+
2564
+ if (posEq(sel.anchor, anchor) && posEq(sel.head, head)) return;
2565
+
2566
+ sel.anchor = anchor; sel.head = head;
2567
+ var inv = posLess(head, anchor);
2568
+ sel.from = inv ? head : anchor;
2569
+ sel.to = inv ? anchor : head;
2570
+
2571
+ if (doc.cm)
2572
+ doc.cm.curOp.updateInput = doc.cm.curOp.selectionChanged =
2573
+ doc.cm.curOp.cursorActivity = true;
2574
+
2575
+ signalLater(doc, "cursorActivity", doc);
2576
+ }
2577
+
2578
+ function reCheckSelection(cm) {
2579
+ setSelection(cm.doc, cm.doc.sel.from, cm.doc.sel.to, null, "push");
2580
+ }
2581
+
2582
+ function skipAtomic(doc, pos, bias, mayClear) {
2583
+ var flipped = false, curPos = pos;
2584
+ var dir = bias || 1;
2585
+ doc.cantEdit = false;
2586
+ search: for (;;) {
2587
+ var line = getLine(doc, curPos.line);
2588
+ if (line.markedSpans) {
2589
+ for (var i = 0; i < line.markedSpans.length; ++i) {
2590
+ var sp = line.markedSpans[i], m = sp.marker;
2591
+ if ((sp.from == null || (m.inclusiveLeft ? sp.from <= curPos.ch : sp.from < curPos.ch)) &&
2592
+ (sp.to == null || (m.inclusiveRight ? sp.to >= curPos.ch : sp.to > curPos.ch))) {
2593
+ if (mayClear) {
2594
+ signal(m, "beforeCursorEnter");
2595
+ if (m.explicitlyCleared) {
2596
+ if (!line.markedSpans) break;
2597
+ else {--i; continue;}
2598
+ }
2599
+ }
2600
+ if (!m.atomic) continue;
2601
+ var newPos = m.find()[dir < 0 ? "from" : "to"];
2602
+ if (posEq(newPos, curPos)) {
2603
+ newPos.ch += dir;
2604
+ if (newPos.ch < 0) {
2605
+ if (newPos.line > doc.first) newPos = clipPos(doc, Pos(newPos.line - 1));
2606
+ else newPos = null;
2607
+ } else if (newPos.ch > line.text.length) {
2608
+ if (newPos.line < doc.first + doc.size - 1) newPos = Pos(newPos.line + 1, 0);
2609
+ else newPos = null;
2610
+ }
2611
+ if (!newPos) {
2612
+ if (flipped) {
2613
+ // Driven in a corner -- no valid cursor position found at all
2614
+ // -- try again *with* clearing, if we didn't already
2615
+ if (!mayClear) return skipAtomic(doc, pos, bias, true);
2616
+ // Otherwise, turn off editing until further notice, and return the start of the doc
2617
+ doc.cantEdit = true;
2618
+ return Pos(doc.first, 0);
2619
+ }
2620
+ flipped = true; newPos = pos; dir = -dir;
2621
+ }
2622
+ }
2623
+ curPos = newPos;
2624
+ continue search;
2625
+ }
2626
+ }
2627
+ }
2628
+ return curPos;
2629
+ }
2630
+ }
2631
+
2632
+ // SCROLLING
2633
+
2634
+ function scrollCursorIntoView(cm) {
2635
+ var coords = scrollPosIntoView(cm, cm.doc.sel.head, cm.options.cursorScrollMargin);
2636
+ if (!cm.state.focused) return;
2637
+ var display = cm.display, box = getRect(display.sizer), doScroll = null;
2638
+ if (coords.top + box.top < 0) doScroll = true;
2639
+ else if (coords.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false;
2640
+ if (doScroll != null && !phantom) {
2641
+ var hidden = display.cursor.style.display == "none";
2642
+ if (hidden) {
2643
+ display.cursor.style.display = "";
2644
+ display.cursor.style.left = coords.left + "px";
2645
+ display.cursor.style.top = (coords.top - display.viewOffset) + "px";
2646
+ }
2647
+ display.cursor.scrollIntoView(doScroll);
2648
+ if (hidden) display.cursor.style.display = "none";
2649
+ }
2650
+ }
2651
+
2652
+ function scrollPosIntoView(cm, pos, margin) {
2653
+ if (margin == null) margin = 0;
2654
+ for (;;) {
2655
+ var changed = false, coords = cursorCoords(cm, pos);
2656
+ var scrollPos = calculateScrollPos(cm, coords.left, coords.top - margin, coords.left, coords.bottom + margin);
2657
+ var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft;
2658
+ if (scrollPos.scrollTop != null) {
2659
+ setScrollTop(cm, scrollPos.scrollTop);
2660
+ if (Math.abs(cm.doc.scrollTop - startTop) > 1) changed = true;
2661
+ }
2662
+ if (scrollPos.scrollLeft != null) {
2663
+ setScrollLeft(cm, scrollPos.scrollLeft);
2664
+ if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) changed = true;
2665
+ }
2666
+ if (!changed) return coords;
2667
+ }
2668
+ }
2669
+
2670
+ function scrollIntoView(cm, x1, y1, x2, y2) {
2671
+ var scrollPos = calculateScrollPos(cm, x1, y1, x2, y2);
2672
+ if (scrollPos.scrollTop != null) setScrollTop(cm, scrollPos.scrollTop);
2673
+ if (scrollPos.scrollLeft != null) setScrollLeft(cm, scrollPos.scrollLeft);
2674
+ }
2675
+
2676
+ function calculateScrollPos(cm, x1, y1, x2, y2) {
2677
+ var display = cm.display, snapMargin = textHeight(cm.display);
2678
+ if (y1 < 0) y1 = 0;
2679
+ var screen = display.scroller.clientHeight - scrollerCutOff, screentop = display.scroller.scrollTop, result = {};
2680
+ var docBottom = cm.doc.height + paddingVert(display);
2681
+ var atTop = y1 < snapMargin, atBottom = y2 > docBottom - snapMargin;
2682
+ if (y1 < screentop) {
2683
+ result.scrollTop = atTop ? 0 : y1;
2684
+ } else if (y2 > screentop + screen) {
2685
+ var newTop = Math.min(y1, (atBottom ? docBottom : y2) - screen);
2686
+ if (newTop != screentop) result.scrollTop = newTop;
2687
+ }
2688
+
2689
+ var screenw = display.scroller.clientWidth - scrollerCutOff, screenleft = display.scroller.scrollLeft;
2690
+ x1 += display.gutters.offsetWidth; x2 += display.gutters.offsetWidth;
2691
+ var gutterw = display.gutters.offsetWidth;
2692
+ var atLeft = x1 < gutterw + 10;
2693
+ if (x1 < screenleft + gutterw || atLeft) {
2694
+ if (atLeft) x1 = 0;
2695
+ result.scrollLeft = Math.max(0, x1 - 10 - gutterw);
2696
+ } else if (x2 > screenw + screenleft - 3) {
2697
+ result.scrollLeft = x2 + 10 - screenw;
2698
+ }
2699
+ return result;
2700
+ }
2701
+
2702
+ function updateScrollPos(cm, left, top) {
2703
+ cm.curOp.updateScrollPos = {scrollLeft: left == null ? cm.doc.scrollLeft : left,
2704
+ scrollTop: top == null ? cm.doc.scrollTop : top};
2705
+ }
2706
+
2707
+ function addToScrollPos(cm, left, top) {
2708
+ var pos = cm.curOp.updateScrollPos || (cm.curOp.updateScrollPos = {scrollLeft: cm.doc.scrollLeft, scrollTop: cm.doc.scrollTop});
2709
+ var scroll = cm.display.scroller;
2710
+ pos.scrollTop = Math.max(0, Math.min(scroll.scrollHeight - scroll.clientHeight, pos.scrollTop + top));
2711
+ pos.scrollLeft = Math.max(0, Math.min(scroll.scrollWidth - scroll.clientWidth, pos.scrollLeft + left));
2712
+ }
2713
+
2714
+ // API UTILITIES
2715
+
2716
+ function indentLine(cm, n, how, aggressive) {
2717
+ var doc = cm.doc;
2718
+ if (how == null) how = "add";
2719
+ if (how == "smart") {
2720
+ if (!cm.doc.mode.indent) how = "prev";
2721
+ else var state = getStateBefore(cm, n);
2722
+ }
2723
+
2724
+ var tabSize = cm.options.tabSize;
2725
+ var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize);
2726
+ var curSpaceString = line.text.match(/^\s*/)[0], indentation;
2727
+ if (how == "smart") {
2728
+ indentation = cm.doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text);
2729
+ if (indentation == Pass) {
2730
+ if (!aggressive) return;
2731
+ how = "prev";
2732
+ }
2733
+ }
2734
+ if (how == "prev") {
2735
+ if (n > doc.first) indentation = countColumn(getLine(doc, n-1).text, null, tabSize);
2736
+ else indentation = 0;
2737
+ } else if (how == "add") {
2738
+ indentation = curSpace + cm.options.indentUnit;
2739
+ } else if (how == "subtract") {
2740
+ indentation = curSpace - cm.options.indentUnit;
2741
+ } else if (typeof how == "number") {
2742
+ indentation = curSpace + how;
2743
+ }
2744
+ indentation = Math.max(0, indentation);
2745
+
2746
+ var indentString = "", pos = 0;
2747
+ if (cm.options.indentWithTabs)
2748
+ for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";}
2749
+ if (pos < indentation) indentString += spaceStr(indentation - pos);
2750
+
2751
+ if (indentString != curSpaceString)
2752
+ replaceRange(cm.doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input");
2753
+ line.stateAfter = null;
2754
+ }
2755
+
2756
+ function changeLine(cm, handle, op) {
2757
+ var no = handle, line = handle, doc = cm.doc;
2758
+ if (typeof handle == "number") line = getLine(doc, clipLine(doc, handle));
2759
+ else no = lineNo(handle);
2760
+ if (no == null) return null;
2761
+ if (op(line, no)) regChange(cm, no, no + 1);
2762
+ else return null;
2763
+ return line;
2764
+ }
2765
+
2766
+ function findPosH(doc, pos, dir, unit, visually) {
2767
+ var line = pos.line, ch = pos.ch, origDir = dir;
2768
+ var lineObj = getLine(doc, line);
2769
+ var possible = true;
2770
+ function findNextLine() {
2771
+ var l = line + dir;
2772
+ if (l < doc.first || l >= doc.first + doc.size) return (possible = false);
2773
+ line = l;
2774
+ return lineObj = getLine(doc, l);
2775
+ }
2776
+ function moveOnce(boundToLine) {
2777
+ var next = (visually ? moveVisually : moveLogically)(lineObj, ch, dir, true);
2778
+ if (next == null) {
2779
+ if (!boundToLine && findNextLine()) {
2780
+ if (visually) ch = (dir < 0 ? lineRight : lineLeft)(lineObj);
2781
+ else ch = dir < 0 ? lineObj.text.length : 0;
2782
+ } else return (possible = false);
2783
+ } else ch = next;
2784
+ return true;
2785
+ }
2786
+
2787
+ if (unit == "char") moveOnce();
2788
+ else if (unit == "column") moveOnce(true);
2789
+ else if (unit == "word" || unit == "group") {
2790
+ var sawType = null, group = unit == "group";
2791
+ for (var first = true;; first = false) {
2792
+ if (dir < 0 && !moveOnce(!first)) break;
2793
+ var cur = lineObj.text.charAt(ch) || "\n";
2794
+ var type = isWordChar(cur) ? "w"
2795
+ : !group ? null
2796
+ : /\s/.test(cur) ? null
2797
+ : "p";
2798
+ if (sawType && sawType != type) {
2799
+ if (dir < 0) {dir = 1; moveOnce();}
2800
+ break;
2801
+ }
2802
+ if (type) sawType = type;
2803
+ if (dir > 0 && !moveOnce(!first)) break;
2804
+ }
2805
+ }
2806
+ var result = skipAtomic(doc, Pos(line, ch), origDir, true);
2807
+ if (!possible) result.hitSide = true;
2808
+ return result;
2809
+ }
2810
+
2811
+ function findPosV(cm, pos, dir, unit) {
2812
+ var doc = cm.doc, x = pos.left, y;
2813
+ if (unit == "page") {
2814
+ var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight);
2815
+ y = pos.top + dir * (pageSize - (dir < 0 ? 1.5 : .5) * textHeight(cm.display));
2816
+ } else if (unit == "line") {
2817
+ y = dir > 0 ? pos.bottom + 3 : pos.top - 3;
2818
+ }
2819
+ for (;;) {
2820
+ var target = coordsChar(cm, x, y);
2821
+ if (!target.outside) break;
2822
+ if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break; }
2823
+ y += dir * 5;
2824
+ }
2825
+ return target;
2826
+ }
2827
+
2828
+ function findWordAt(line, pos) {
2829
+ var start = pos.ch, end = pos.ch;
2830
+ if (line) {
2831
+ if ((pos.xRel < 0 || end == line.length) && start) --start; else ++end;
2832
+ var startChar = line.charAt(start);
2833
+ var check = isWordChar(startChar) ? isWordChar
2834
+ : /\s/.test(startChar) ? function(ch) {return /\s/.test(ch);}
2835
+ : function(ch) {return !/\s/.test(ch) && !isWordChar(ch);};
2836
+ while (start > 0 && check(line.charAt(start - 1))) --start;
2837
+ while (end < line.length && check(line.charAt(end))) ++end;
2838
+ }
2839
+ return {from: Pos(pos.line, start), to: Pos(pos.line, end)};
2840
+ }
2841
+
2842
+ function selectLine(cm, line) {
2843
+ extendSelection(cm.doc, Pos(line, 0), clipPos(cm.doc, Pos(line + 1, 0)));
2844
+ }
2845
+
2846
+ // PROTOTYPE
2847
+
2848
+ // The publicly visible API. Note that operation(null, f) means
2849
+ // 'wrap f in an operation, performed on its `this` parameter'
2850
+
2851
+ CodeMirror.prototype = {
2852
+ constructor: CodeMirror,
2853
+ focus: function(){window.focus(); focusInput(this); onFocus(this); fastPoll(this);},
2854
+
2855
+ setOption: function(option, value) {
2856
+ var options = this.options, old = options[option];
2857
+ if (options[option] == value && option != "mode") return;
2858
+ options[option] = value;
2859
+ if (optionHandlers.hasOwnProperty(option))
2860
+ operation(this, optionHandlers[option])(this, value, old);
2861
+ },
2862
+
2863
+ getOption: function(option) {return this.options[option];},
2864
+ getDoc: function() {return this.doc;},
2865
+
2866
+ addKeyMap: function(map, bottom) {
2867
+ this.state.keyMaps[bottom ? "push" : "unshift"](map);
2868
+ },
2869
+ removeKeyMap: function(map) {
2870
+ var maps = this.state.keyMaps;
2871
+ for (var i = 0; i < maps.length; ++i)
2872
+ if (maps[i] == map || (typeof maps[i] != "string" && maps[i].name == map)) {
2873
+ maps.splice(i, 1);
2874
+ return true;
2875
+ }
2876
+ },
2877
+
2878
+ addOverlay: operation(null, function(spec, options) {
2879
+ var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec);
2880
+ if (mode.startState) throw new Error("Overlays may not be stateful.");
2881
+ this.state.overlays.push({mode: mode, modeSpec: spec, opaque: options && options.opaque});
2882
+ this.state.modeGen++;
2883
+ regChange(this);
2884
+ }),
2885
+ removeOverlay: operation(null, function(spec) {
2886
+ var overlays = this.state.overlays;
2887
+ for (var i = 0; i < overlays.length; ++i) {
2888
+ var cur = overlays[i].modeSpec;
2889
+ if (cur == spec || typeof spec == "string" && cur.name == spec) {
2890
+ overlays.splice(i, 1);
2891
+ this.state.modeGen++;
2892
+ regChange(this);
2893
+ return;
2894
+ }
2895
+ }
2896
+ }),
2897
+
2898
+ indentLine: operation(null, function(n, dir, aggressive) {
2899
+ if (typeof dir != "string" && typeof dir != "number") {
2900
+ if (dir == null) dir = this.options.smartIndent ? "smart" : "prev";
2901
+ else dir = dir ? "add" : "subtract";
2902
+ }
2903
+ if (isLine(this.doc, n)) indentLine(this, n, dir, aggressive);
2904
+ }),
2905
+ indentSelection: operation(null, function(how) {
2906
+ var sel = this.doc.sel;
2907
+ if (posEq(sel.from, sel.to)) return indentLine(this, sel.from.line, how);
2908
+ var e = sel.to.line - (sel.to.ch ? 0 : 1);
2909
+ for (var i = sel.from.line; i <= e; ++i) indentLine(this, i, how);
2910
+ }),
2911
+
2912
+ // Fetch the parser token for a given character. Useful for hacks
2913
+ // that want to inspect the mode state (say, for completion).
2914
+ getTokenAt: function(pos, precise) {
2915
+ var doc = this.doc;
2916
+ pos = clipPos(doc, pos);
2917
+ var state = getStateBefore(this, pos.line, precise), mode = this.doc.mode;
2918
+ var line = getLine(doc, pos.line);
2919
+ var stream = new StringStream(line.text, this.options.tabSize);
2920
+ while (stream.pos < pos.ch && !stream.eol()) {
2921
+ stream.start = stream.pos;
2922
+ var style = mode.token(stream, state);
2923
+ }
2924
+ return {start: stream.start,
2925
+ end: stream.pos,
2926
+ string: stream.current(),
2927
+ className: style || null, // Deprecated, use 'type' instead
2928
+ type: style || null,
2929
+ state: state};
2930
+ },
2931
+
2932
+ getTokenTypeAt: function(pos) {
2933
+ pos = clipPos(this.doc, pos);
2934
+ var styles = getLineStyles(this, getLine(this.doc, pos.line));
2935
+ var before = 0, after = (styles.length - 1) / 2, ch = pos.ch;
2936
+ if (ch == 0) return styles[2];
2937
+ for (;;) {
2938
+ var mid = (before + after) >> 1;
2939
+ if ((mid ? styles[mid * 2 - 1] : 0) >= ch) after = mid;
2940
+ else if (styles[mid * 2 + 1] < ch) before = mid + 1;
2941
+ else return styles[mid * 2 + 2];
2942
+ }
2943
+ },
2944
+
2945
+ getModeAt: function(pos) {
2946
+ var mode = this.doc.mode;
2947
+ if (!mode.innerMode) return mode;
2948
+ return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode;
2949
+ },
2950
+
2951
+ getHelper: function(pos, type) {
2952
+ if (!helpers.hasOwnProperty(type)) return;
2953
+ var help = helpers[type], mode = this.getModeAt(pos);
2954
+ return mode[type] && help[mode[type]] ||
2955
+ mode.helperType && help[mode.helperType] ||
2956
+ help[mode.name];
2957
+ },
2958
+
2959
+ getStateAfter: function(line, precise) {
2960
+ var doc = this.doc;
2961
+ line = clipLine(doc, line == null ? doc.first + doc.size - 1: line);
2962
+ return getStateBefore(this, line + 1, precise);
2963
+ },
2964
+
2965
+ cursorCoords: function(start, mode) {
2966
+ var pos, sel = this.doc.sel;
2967
+ if (start == null) pos = sel.head;
2968
+ else if (typeof start == "object") pos = clipPos(this.doc, start);
2969
+ else pos = start ? sel.from : sel.to;
2970
+ return cursorCoords(this, pos, mode || "page");
2971
+ },
2972
+
2973
+ charCoords: function(pos, mode) {
2974
+ return charCoords(this, clipPos(this.doc, pos), mode || "page");
2975
+ },
2976
+
2977
+ coordsChar: function(coords, mode) {
2978
+ coords = fromCoordSystem(this, coords, mode || "page");
2979
+ return coordsChar(this, coords.left, coords.top);
2980
+ },
2981
+
2982
+ lineAtHeight: function(height, mode) {
2983
+ height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top;
2984
+ return lineAtHeight(this.doc, height + this.display.viewOffset);
2985
+ },
2986
+ heightAtLine: function(line, mode) {
2987
+ var end = false, last = this.doc.first + this.doc.size - 1;
2988
+ if (line < this.doc.first) line = this.doc.first;
2989
+ else if (line > last) { line = last; end = true; }
2990
+ var lineObj = getLine(this.doc, line);
2991
+ return intoCoordSystem(this, getLine(this.doc, line), {top: 0, left: 0}, mode || "page").top +
2992
+ (end ? lineObj.height : 0);
2993
+ },
2994
+
2995
+ defaultTextHeight: function() { return textHeight(this.display); },
2996
+ defaultCharWidth: function() { return charWidth(this.display); },
2997
+
2998
+ setGutterMarker: operation(null, function(line, gutterID, value) {
2999
+ return changeLine(this, line, function(line) {
3000
+ var markers = line.gutterMarkers || (line.gutterMarkers = {});
3001
+ markers[gutterID] = value;
3002
+ if (!value && isEmpty(markers)) line.gutterMarkers = null;
3003
+ return true;
3004
+ });
3005
+ }),
3006
+
3007
+ clearGutter: operation(null, function(gutterID) {
3008
+ var cm = this, doc = cm.doc, i = doc.first;
3009
+ doc.iter(function(line) {
3010
+ if (line.gutterMarkers && line.gutterMarkers[gutterID]) {
3011
+ line.gutterMarkers[gutterID] = null;
3012
+ regChange(cm, i, i + 1);
3013
+ if (isEmpty(line.gutterMarkers)) line.gutterMarkers = null;
3014
+ }
3015
+ ++i;
3016
+ });
3017
+ }),
3018
+
3019
+ addLineClass: operation(null, function(handle, where, cls) {
3020
+ return changeLine(this, handle, function(line) {
3021
+ var prop = where == "text" ? "textClass" : where == "background" ? "bgClass" : "wrapClass";
3022
+ if (!line[prop]) line[prop] = cls;
3023
+ else if (new RegExp("(?:^|\\s)" + cls + "(?:$|\\s)").test(line[prop])) return false;
3024
+ else line[prop] += " " + cls;
3025
+ return true;
3026
+ });
3027
+ }),
3028
+
3029
+ removeLineClass: operation(null, function(handle, where, cls) {
3030
+ return changeLine(this, handle, function(line) {
3031
+ var prop = where == "text" ? "textClass" : where == "background" ? "bgClass" : "wrapClass";
3032
+ var cur = line[prop];
3033
+ if (!cur) return false;
3034
+ else if (cls == null) line[prop] = null;
3035
+ else {
3036
+ var found = cur.match(new RegExp("(?:^|\\s+)" + cls + "(?:$|\\s+)"));
3037
+ if (!found) return false;
3038
+ var end = found.index + found[0].length;
3039
+ line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null;
3040
+ }
3041
+ return true;
3042
+ });
3043
+ }),
3044
+
3045
+ addLineWidget: operation(null, function(handle, node, options) {
3046
+ return addLineWidget(this, handle, node, options);
3047
+ }),
3048
+
3049
+ removeLineWidget: function(widget) { widget.clear(); },
3050
+
3051
+ lineInfo: function(line) {
3052
+ if (typeof line == "number") {
3053
+ if (!isLine(this.doc, line)) return null;
3054
+ var n = line;
3055
+ line = getLine(this.doc, line);
3056
+ if (!line) return null;
3057
+ } else {
3058
+ var n = lineNo(line);
3059
+ if (n == null) return null;
3060
+ }
3061
+ return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers,
3062
+ textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass,
3063
+ widgets: line.widgets};
3064
+ },
3065
+
3066
+ getViewport: function() { return {from: this.display.showingFrom, to: this.display.showingTo};},
3067
+
3068
+ addWidget: function(pos, node, scroll, vert, horiz) {
3069
+ var display = this.display;
3070
+ pos = cursorCoords(this, clipPos(this.doc, pos));
3071
+ var top = pos.bottom, left = pos.left;
3072
+ node.style.position = "absolute";
3073
+ display.sizer.appendChild(node);
3074
+ if (vert == "over") {
3075
+ top = pos.top;
3076
+ } else if (vert == "above" || vert == "near") {
3077
+ var vspace = Math.max(display.wrapper.clientHeight, this.doc.height),
3078
+ hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth);
3079
+ // Default to positioning above (if specified and possible); otherwise default to positioning below
3080
+ if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight)
3081
+ top = pos.top - node.offsetHeight;
3082
+ else if (pos.bottom + node.offsetHeight <= vspace)
3083
+ top = pos.bottom;
3084
+ if (left + node.offsetWidth > hspace)
3085
+ left = hspace - node.offsetWidth;
3086
+ }
3087
+ node.style.top = top + "px";
3088
+ node.style.left = node.style.right = "";
3089
+ if (horiz == "right") {
3090
+ left = display.sizer.clientWidth - node.offsetWidth;
3091
+ node.style.right = "0px";
3092
+ } else {
3093
+ if (horiz == "left") left = 0;
3094
+ else if (horiz == "middle") left = (display.sizer.clientWidth - node.offsetWidth) / 2;
3095
+ node.style.left = left + "px";
3096
+ }
3097
+ if (scroll)
3098
+ scrollIntoView(this, left, top, left + node.offsetWidth, top + node.offsetHeight);
3099
+ },
3100
+
3101
+ triggerOnKeyDown: operation(null, onKeyDown),
3102
+
3103
+ execCommand: function(cmd) {return commands[cmd](this);},
3104
+
3105
+ findPosH: function(from, amount, unit, visually) {
3106
+ var dir = 1;
3107
+ if (amount < 0) { dir = -1; amount = -amount; }
3108
+ for (var i = 0, cur = clipPos(this.doc, from); i < amount; ++i) {
3109
+ cur = findPosH(this.doc, cur, dir, unit, visually);
3110
+ if (cur.hitSide) break;
3111
+ }
3112
+ return cur;
3113
+ },
3114
+
3115
+ moveH: operation(null, function(dir, unit) {
3116
+ var sel = this.doc.sel, pos;
3117
+ if (sel.shift || sel.extend || posEq(sel.from, sel.to))
3118
+ pos = findPosH(this.doc, sel.head, dir, unit, this.options.rtlMoveVisually);
3119
+ else
3120
+ pos = dir < 0 ? sel.from : sel.to;
3121
+ extendSelection(this.doc, pos, pos, dir);
3122
+ }),
3123
+
3124
+ deleteH: operation(null, function(dir, unit) {
3125
+ var sel = this.doc.sel;
3126
+ if (!posEq(sel.from, sel.to)) replaceRange(this.doc, "", sel.from, sel.to, "+delete");
3127
+ else replaceRange(this.doc, "", sel.from, findPosH(this.doc, sel.head, dir, unit, false), "+delete");
3128
+ this.curOp.userSelChange = true;
3129
+ }),
3130
+
3131
+ findPosV: function(from, amount, unit, goalColumn) {
3132
+ var dir = 1, x = goalColumn;
3133
+ if (amount < 0) { dir = -1; amount = -amount; }
3134
+ for (var i = 0, cur = clipPos(this.doc, from); i < amount; ++i) {
3135
+ var coords = cursorCoords(this, cur, "div");
3136
+ if (x == null) x = coords.left;
3137
+ else coords.left = x;
3138
+ cur = findPosV(this, coords, dir, unit);
3139
+ if (cur.hitSide) break;
3140
+ }
3141
+ return cur;
3142
+ },
3143
+
3144
+ moveV: operation(null, function(dir, unit) {
3145
+ var sel = this.doc.sel;
3146
+ var pos = cursorCoords(this, sel.head, "div");
3147
+ if (sel.goalColumn != null) pos.left = sel.goalColumn;
3148
+ var target = findPosV(this, pos, dir, unit);
3149
+
3150
+ if (unit == "page") addToScrollPos(this, 0, charCoords(this, target, "div").top - pos.top);
3151
+ extendSelection(this.doc, target, target, dir);
3152
+ sel.goalColumn = pos.left;
3153
+ }),
3154
+
3155
+ toggleOverwrite: function(value) {
3156
+ if (value != null && value == this.state.overwrite) return;
3157
+ if (this.state.overwrite = !this.state.overwrite)
3158
+ this.display.cursor.className += " CodeMirror-overwrite";
3159
+ else
3160
+ this.display.cursor.className = this.display.cursor.className.replace(" CodeMirror-overwrite", "");
3161
+ },
3162
+ hasFocus: function() { return this.state.focused; },
3163
+
3164
+ scrollTo: operation(null, function(x, y) {
3165
+ updateScrollPos(this, x, y);
3166
+ }),
3167
+ getScrollInfo: function() {
3168
+ var scroller = this.display.scroller, co = scrollerCutOff;
3169
+ return {left: scroller.scrollLeft, top: scroller.scrollTop,
3170
+ height: scroller.scrollHeight - co, width: scroller.scrollWidth - co,
3171
+ clientHeight: scroller.clientHeight - co, clientWidth: scroller.clientWidth - co};
3172
+ },
3173
+
3174
+ scrollIntoView: operation(null, function(pos, margin) {
3175
+ if (typeof pos == "number") pos = Pos(pos, 0);
3176
+ if (!margin) margin = 0;
3177
+ var coords = pos;
3178
+
3179
+ if (!pos || pos.line != null) {
3180
+ this.curOp.scrollToPos = pos ? clipPos(this.doc, pos) : this.doc.sel.head;
3181
+ this.curOp.scrollToPosMargin = margin;
3182
+ coords = cursorCoords(this, this.curOp.scrollToPos);
3183
+ }
3184
+ var sPos = calculateScrollPos(this, coords.left, coords.top - margin, coords.right, coords.bottom + margin);
3185
+ updateScrollPos(this, sPos.scrollLeft, sPos.scrollTop);
3186
+ }),
3187
+
3188
+ setSize: operation(null, function(width, height) {
3189
+ function interpret(val) {
3190
+ return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val;
3191
+ }
3192
+ if (width != null) this.display.wrapper.style.width = interpret(width);
3193
+ if (height != null) this.display.wrapper.style.height = interpret(height);
3194
+ if (this.options.lineWrapping)
3195
+ this.display.measureLineCache.length = this.display.measureLineCachePos = 0;
3196
+ this.curOp.forceUpdate = true;
3197
+ }),
3198
+
3199
+ operation: function(f){return runInOp(this, f);},
3200
+
3201
+ refresh: operation(null, function() {
3202
+ var badHeight = this.display.cachedTextHeight == null;
3203
+ clearCaches(this);
3204
+ updateScrollPos(this, this.doc.scrollLeft, this.doc.scrollTop);
3205
+ regChange(this);
3206
+ if (badHeight) estimateLineHeights(this);
3207
+ }),
3208
+
3209
+ swapDoc: operation(null, function(doc) {
3210
+ var old = this.doc;
3211
+ old.cm = null;
3212
+ attachDoc(this, doc);
3213
+ clearCaches(this);
3214
+ resetInput(this, true);
3215
+ updateScrollPos(this, doc.scrollLeft, doc.scrollTop);
3216
+ return old;
3217
+ }),
3218
+
3219
+ getInputField: function(){return this.display.input;},
3220
+ getWrapperElement: function(){return this.display.wrapper;},
3221
+ getScrollerElement: function(){return this.display.scroller;},
3222
+ getGutterElement: function(){return this.display.gutters;}
3223
+ };
3224
+ eventMixin(CodeMirror);
3225
+
3226
+ // OPTION DEFAULTS
3227
+
3228
+ var optionHandlers = CodeMirror.optionHandlers = {};
3229
+
3230
+ // The default configuration options.
3231
+ var defaults = CodeMirror.defaults = {};
3232
+
3233
+ function option(name, deflt, handle, notOnInit) {
3234
+ CodeMirror.defaults[name] = deflt;
3235
+ if (handle) optionHandlers[name] =
3236
+ notOnInit ? function(cm, val, old) {if (old != Init) handle(cm, val, old);} : handle;
3237
+ }
3238
+
3239
+ var Init = CodeMirror.Init = {toString: function(){return "CodeMirror.Init";}};
3240
+
3241
+ // These two are, on init, called from the constructor because they
3242
+ // have to be initialized before the editor can start at all.
3243
+ option("value", "", function(cm, val) {
3244
+ cm.setValue(val);
3245
+ }, true);
3246
+ option("mode", null, function(cm, val) {
3247
+ cm.doc.modeOption = val;
3248
+ loadMode(cm);
3249
+ }, true);
3250
+
3251
+ option("indentUnit", 2, loadMode, true);
3252
+ option("indentWithTabs", false);
3253
+ option("smartIndent", true);
3254
+ option("tabSize", 4, function(cm) {
3255
+ loadMode(cm);
3256
+ clearCaches(cm);
3257
+ regChange(cm);
3258
+ }, true);
3259
+ option("electricChars", true);
3260
+ option("rtlMoveVisually", !windows);
3261
+
3262
+ option("theme", "default", function(cm) {
3263
+ themeChanged(cm);
3264
+ guttersChanged(cm);
3265
+ }, true);
3266
+ option("keyMap", "default", keyMapChanged);
3267
+ option("extraKeys", null);
3268
+
3269
+ option("onKeyEvent", null);
3270
+ option("onDragEvent", null);
3271
+
3272
+ option("lineWrapping", false, wrappingChanged, true);
3273
+ option("gutters", [], function(cm) {
3274
+ setGuttersForLineNumbers(cm.options);
3275
+ guttersChanged(cm);
3276
+ }, true);
3277
+ option("fixedGutter", true, function(cm, val) {
3278
+ cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0";
3279
+ cm.refresh();
3280
+ }, true);
3281
+ option("coverGutterNextToScrollbar", false, updateScrollbars, true);
3282
+ option("lineNumbers", false, function(cm) {
3283
+ setGuttersForLineNumbers(cm.options);
3284
+ guttersChanged(cm);
3285
+ }, true);
3286
+ option("firstLineNumber", 1, guttersChanged, true);
3287
+ option("lineNumberFormatter", function(integer) {return integer;}, guttersChanged, true);
3288
+ option("showCursorWhenSelecting", false, updateSelection, true);
3289
+
3290
+ option("readOnly", false, function(cm, val) {
3291
+ if (val == "nocursor") {onBlur(cm); cm.display.input.blur();}
3292
+ else if (!val) resetInput(cm, true);
3293
+ });
3294
+ option("dragDrop", true);
3295
+
3296
+ option("cursorBlinkRate", 530);
3297
+ option("cursorScrollMargin", 0);
3298
+ option("cursorHeight", 1);
3299
+ option("workTime", 100);
3300
+ option("workDelay", 100);
3301
+ option("flattenSpans", true);
3302
+ option("pollInterval", 100);
3303
+ option("undoDepth", 40, function(cm, val){cm.doc.history.undoDepth = val;});
3304
+ option("historyEventDelay", 500);
3305
+ option("viewportMargin", 10, function(cm){cm.refresh();}, true);
3306
+ option("maxHighlightLength", 10000, function(cm){loadMode(cm); cm.refresh();}, true);
3307
+ option("crudeMeasuringFrom", 10000);
3308
+ option("moveInputWithCursor", true, function(cm, val) {
3309
+ if (!val) cm.display.inputDiv.style.top = cm.display.inputDiv.style.left = 0;
3310
+ });
3311
+
3312
+ option("tabindex", null, function(cm, val) {
3313
+ cm.display.input.tabIndex = val || "";
3314
+ });
3315
+ option("autofocus", null);
3316
+
3317
+ // MODE DEFINITION AND QUERYING
3318
+
3319
+ // Known modes, by name and by MIME
3320
+ var modes = CodeMirror.modes = {}, mimeModes = CodeMirror.mimeModes = {};
3321
+
3322
+ CodeMirror.defineMode = function(name, mode) {
3323
+ if (!CodeMirror.defaults.mode && name != "null") CodeMirror.defaults.mode = name;
3324
+ if (arguments.length > 2) {
3325
+ mode.dependencies = [];
3326
+ for (var i = 2; i < arguments.length; ++i) mode.dependencies.push(arguments[i]);
3327
+ }
3328
+ modes[name] = mode;
3329
+ };
3330
+
3331
+ CodeMirror.defineMIME = function(mime, spec) {
3332
+ mimeModes[mime] = spec;
3333
+ };
3334
+
3335
+ CodeMirror.resolveMode = function(spec) {
3336
+ if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) {
3337
+ spec = mimeModes[spec];
3338
+ } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) {
3339
+ var found = mimeModes[spec.name];
3340
+ spec = createObj(found, spec);
3341
+ spec.name = found.name;
3342
+ } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) {
3343
+ return CodeMirror.resolveMode("application/xml");
3344
+ }
3345
+ if (typeof spec == "string") return {name: spec};
3346
+ else return spec || {name: "null"};
3347
+ };
3348
+
3349
+ CodeMirror.getMode = function(options, spec) {
3350
+ var spec = CodeMirror.resolveMode(spec);
3351
+ var mfactory = modes[spec.name];
3352
+ if (!mfactory) return CodeMirror.getMode(options, "text/plain");
3353
+ var modeObj = mfactory(options, spec);
3354
+ if (modeExtensions.hasOwnProperty(spec.name)) {
3355
+ var exts = modeExtensions[spec.name];
3356
+ for (var prop in exts) {
3357
+ if (!exts.hasOwnProperty(prop)) continue;
3358
+ if (modeObj.hasOwnProperty(prop)) modeObj["_" + prop] = modeObj[prop];
3359
+ modeObj[prop] = exts[prop];
3360
+ }
3361
+ }
3362
+ modeObj.name = spec.name;
3363
+
3364
+ return modeObj;
3365
+ };
3366
+
3367
+ CodeMirror.defineMode("null", function() {
3368
+ return {token: function(stream) {stream.skipToEnd();}};
3369
+ });
3370
+ CodeMirror.defineMIME("text/plain", "null");
3371
+
3372
+ var modeExtensions = CodeMirror.modeExtensions = {};
3373
+ CodeMirror.extendMode = function(mode, properties) {
3374
+ var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});
3375
+ copyObj(properties, exts);
3376
+ };
3377
+
3378
+ // EXTENSIONS
3379
+
3380
+ CodeMirror.defineExtension = function(name, func) {
3381
+ CodeMirror.prototype[name] = func;
3382
+ };
3383
+ CodeMirror.defineDocExtension = function(name, func) {
3384
+ Doc.prototype[name] = func;
3385
+ };
3386
+ CodeMirror.defineOption = option;
3387
+
3388
+ var initHooks = [];
3389
+ CodeMirror.defineInitHook = function(f) {initHooks.push(f);};
3390
+
3391
+ var helpers = CodeMirror.helpers = {};
3392
+ CodeMirror.registerHelper = function(type, name, value) {
3393
+ if (!helpers.hasOwnProperty(type)) helpers[type] = CodeMirror[type] = {};
3394
+ helpers[type][name] = value;
3395
+ };
3396
+
3397
+ // UTILITIES
3398
+
3399
+ CodeMirror.isWordChar = isWordChar;
3400
+
3401
+ // MODE STATE HANDLING
3402
+
3403
+ // Utility functions for working with state. Exported because modes
3404
+ // sometimes need to do this.
3405
+ function copyState(mode, state) {
3406
+ if (state === true) return state;
3407
+ if (mode.copyState) return mode.copyState(state);
3408
+ var nstate = {};
3409
+ for (var n in state) {
3410
+ var val = state[n];
3411
+ if (val instanceof Array) val = val.concat([]);
3412
+ nstate[n] = val;
3413
+ }
3414
+ return nstate;
3415
+ }
3416
+ CodeMirror.copyState = copyState;
3417
+
3418
+ function startState(mode, a1, a2) {
3419
+ return mode.startState ? mode.startState(a1, a2) : true;
3420
+ }
3421
+ CodeMirror.startState = startState;
3422
+
3423
+ CodeMirror.innerMode = function(mode, state) {
3424
+ while (mode.innerMode) {
3425
+ var info = mode.innerMode(state);
3426
+ if (!info || info.mode == mode) break;
3427
+ state = info.state;
3428
+ mode = info.mode;
3429
+ }
3430
+ return info || {mode: mode, state: state};
3431
+ };
3432
+
3433
+ // STANDARD COMMANDS
3434
+
3435
+ var commands = CodeMirror.commands = {
3436
+ selectAll: function(cm) {cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()));},
3437
+ killLine: function(cm) {
3438
+ var from = cm.getCursor(true), to = cm.getCursor(false), sel = !posEq(from, to);
3439
+ if (!sel && cm.getLine(from.line).length == from.ch)
3440
+ cm.replaceRange("", from, Pos(from.line + 1, 0), "+delete");
3441
+ else cm.replaceRange("", from, sel ? to : Pos(from.line), "+delete");
3442
+ },
3443
+ deleteLine: function(cm) {
3444
+ var l = cm.getCursor().line;
3445
+ cm.replaceRange("", Pos(l, 0), Pos(l), "+delete");
3446
+ },
3447
+ delLineLeft: function(cm) {
3448
+ var cur = cm.getCursor();
3449
+ cm.replaceRange("", Pos(cur.line, 0), cur, "+delete");
3450
+ },
3451
+ undo: function(cm) {cm.undo();},
3452
+ redo: function(cm) {cm.redo();},
3453
+ goDocStart: function(cm) {cm.extendSelection(Pos(cm.firstLine(), 0));},
3454
+ goDocEnd: function(cm) {cm.extendSelection(Pos(cm.lastLine()));},
3455
+ goLineStart: function(cm) {
3456
+ cm.extendSelection(lineStart(cm, cm.getCursor().line));
3457
+ },
3458
+ goLineStartSmart: function(cm) {
3459
+ var cur = cm.getCursor(), start = lineStart(cm, cur.line);
3460
+ var line = cm.getLineHandle(start.line);
3461
+ var order = getOrder(line);
3462
+ if (!order || order[0].level == 0) {
3463
+ var firstNonWS = Math.max(0, line.text.search(/\S/));
3464
+ var inWS = cur.line == start.line && cur.ch <= firstNonWS && cur.ch;
3465
+ cm.extendSelection(Pos(start.line, inWS ? 0 : firstNonWS));
3466
+ } else cm.extendSelection(start);
3467
+ },
3468
+ goLineEnd: function(cm) {
3469
+ cm.extendSelection(lineEnd(cm, cm.getCursor().line));
3470
+ },
3471
+ goLineRight: function(cm) {
3472
+ var top = cm.charCoords(cm.getCursor(), "div").top + 5;
3473
+ cm.extendSelection(cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div"));
3474
+ },
3475
+ goLineLeft: function(cm) {
3476
+ var top = cm.charCoords(cm.getCursor(), "div").top + 5;
3477
+ cm.extendSelection(cm.coordsChar({left: 0, top: top}, "div"));
3478
+ },
3479
+ goLineUp: function(cm) {cm.moveV(-1, "line");},
3480
+ goLineDown: function(cm) {cm.moveV(1, "line");},
3481
+ goPageUp: function(cm) {cm.moveV(-1, "page");},
3482
+ goPageDown: function(cm) {cm.moveV(1, "page");},
3483
+ goCharLeft: function(cm) {cm.moveH(-1, "char");},
3484
+ goCharRight: function(cm) {cm.moveH(1, "char");},
3485
+ goColumnLeft: function(cm) {cm.moveH(-1, "column");},
3486
+ goColumnRight: function(cm) {cm.moveH(1, "column");},
3487
+ goWordLeft: function(cm) {cm.moveH(-1, "word");},
3488
+ goGroupRight: function(cm) {cm.moveH(1, "group");},
3489
+ goGroupLeft: function(cm) {cm.moveH(-1, "group");},
3490
+ goWordRight: function(cm) {cm.moveH(1, "word");},
3491
+ delCharBefore: function(cm) {cm.deleteH(-1, "char");},
3492
+ delCharAfter: function(cm) {cm.deleteH(1, "char");},
3493
+ delWordBefore: function(cm) {cm.deleteH(-1, "word");},
3494
+ delWordAfter: function(cm) {cm.deleteH(1, "word");},
3495
+ delGroupBefore: function(cm) {cm.deleteH(-1, "group");},
3496
+ delGroupAfter: function(cm) {cm.deleteH(1, "group");},
3497
+ indentAuto: function(cm) {cm.indentSelection("smart");},
3498
+ indentMore: function(cm) {cm.indentSelection("add");},
3499
+ indentLess: function(cm) {cm.indentSelection("subtract");},
3500
+ insertTab: function(cm) {cm.replaceSelection("\t", "end", "+input");},
3501
+ defaultTab: function(cm) {
3502
+ if (cm.somethingSelected()) cm.indentSelection("add");
3503
+ else cm.replaceSelection("\t", "end", "+input");
3504
+ },
3505
+ transposeChars: function(cm) {
3506
+ var cur = cm.getCursor(), line = cm.getLine(cur.line);
3507
+ if (cur.ch > 0 && cur.ch < line.length - 1)
3508
+ cm.replaceRange(line.charAt(cur.ch) + line.charAt(cur.ch - 1),
3509
+ Pos(cur.line, cur.ch - 1), Pos(cur.line, cur.ch + 1));
3510
+ },
3511
+ newlineAndIndent: function(cm) {
3512
+ operation(cm, function() {
3513
+ cm.replaceSelection("\n", "end", "+input");
3514
+ cm.indentLine(cm.getCursor().line, null, true);
3515
+ })();
3516
+ },
3517
+ toggleOverwrite: function(cm) {cm.toggleOverwrite();}
3518
+ };
3519
+
3520
+ // STANDARD KEYMAPS
3521
+
3522
+ var keyMap = CodeMirror.keyMap = {};
3523
+ keyMap.basic = {
3524
+ "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
3525
+ "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
3526
+ "Delete": "delCharAfter", "Backspace": "delCharBefore", "Tab": "defaultTab", "Shift-Tab": "indentAuto",
3527
+ "Enter": "newlineAndIndent", "Insert": "toggleOverwrite"
3528
+ };
3529
+ // Note that the save and find-related commands aren't defined by
3530
+ // default. Unknown commands are simply ignored.
3531
+ keyMap.pcDefault = {
3532
+ "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
3533
+ "Ctrl-Home": "goDocStart", "Alt-Up": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Down": "goDocEnd",
3534
+ "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
3535
+ "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find",
3536
+ "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
3537
+ "Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
3538
+ fallthrough: "basic"
3539
+ };
3540
+ keyMap.macDefault = {
3541
+ "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
3542
+ "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft",
3543
+ "Alt-Right": "goGroupRight", "Cmd-Left": "goLineStart", "Cmd-Right": "goLineEnd", "Alt-Backspace": "delGroupBefore",
3544
+ "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find",
3545
+ "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
3546
+ "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delLineLeft",
3547
+ fallthrough: ["basic", "emacsy"]
3548
+ };
3549
+ keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault;
3550
+ keyMap.emacsy = {
3551
+ "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
3552
+ "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
3553
+ "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore",
3554
+ "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars"
3555
+ };
3556
+
3557
+ // KEYMAP DISPATCH
3558
+
3559
+ function getKeyMap(val) {
3560
+ if (typeof val == "string") return keyMap[val];
3561
+ else return val;
3562
+ }
3563
+
3564
+ function lookupKey(name, maps, handle) {
3565
+ function lookup(map) {
3566
+ map = getKeyMap(map);
3567
+ var found = map[name];
3568
+ if (found === false) return "stop";
3569
+ if (found != null && handle(found)) return true;
3570
+ if (map.nofallthrough) return "stop";
3571
+
3572
+ var fallthrough = map.fallthrough;
3573
+ if (fallthrough == null) return false;
3574
+ if (Object.prototype.toString.call(fallthrough) != "[object Array]")
3575
+ return lookup(fallthrough);
3576
+ for (var i = 0, e = fallthrough.length; i < e; ++i) {
3577
+ var done = lookup(fallthrough[i]);
3578
+ if (done) return done;
3579
+ }
3580
+ return false;
3581
+ }
3582
+
3583
+ for (var i = 0; i < maps.length; ++i) {
3584
+ var done = lookup(maps[i]);
3585
+ if (done) return done != "stop";
3586
+ }
3587
+ }
3588
+ function isModifierKey(event) {
3589
+ var name = keyNames[event.keyCode];
3590
+ return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod";
3591
+ }
3592
+ function keyName(event, noShift) {
3593
+ if (opera && event.keyCode == 34 && event["char"]) return false;
3594
+ var name = keyNames[event.keyCode];
3595
+ if (name == null || event.altGraphKey) return false;
3596
+ if (event.altKey) name = "Alt-" + name;
3597
+ if (flipCtrlCmd ? event.metaKey : event.ctrlKey) name = "Ctrl-" + name;
3598
+ if (flipCtrlCmd ? event.ctrlKey : event.metaKey) name = "Cmd-" + name;
3599
+ if (!noShift && event.shiftKey) name = "Shift-" + name;
3600
+ return name;
3601
+ }
3602
+ CodeMirror.lookupKey = lookupKey;
3603
+ CodeMirror.isModifierKey = isModifierKey;
3604
+ CodeMirror.keyName = keyName;
3605
+
3606
+ // FROMTEXTAREA
3607
+
3608
+ CodeMirror.fromTextArea = function(textarea, options) {
3609
+ if (!options) options = {};
3610
+ options.value = textarea.value;
3611
+ if (!options.tabindex && textarea.tabindex)
3612
+ options.tabindex = textarea.tabindex;
3613
+ if (!options.placeholder && textarea.placeholder)
3614
+ options.placeholder = textarea.placeholder;
3615
+ // Set autofocus to true if this textarea is focused, or if it has
3616
+ // autofocus and no other element is focused.
3617
+ if (options.autofocus == null) {
3618
+ var hasFocus = document.body;
3619
+ // doc.activeElement occasionally throws on IE
3620
+ try { hasFocus = document.activeElement; } catch(e) {}
3621
+ options.autofocus = hasFocus == textarea ||
3622
+ textarea.getAttribute("autofocus") != null && hasFocus == document.body;
3623
+ }
3624
+
3625
+ function save() {textarea.value = cm.getValue();}
3626
+ if (textarea.form) {
3627
+ on(textarea.form, "submit", save);
3628
+ // Deplorable hack to make the submit method do the right thing.
3629
+ if (!options.leaveSubmitMethodAlone) {
3630
+ var form = textarea.form, realSubmit = form.submit;
3631
+ try {
3632
+ var wrappedSubmit = form.submit = function() {
3633
+ save();
3634
+ form.submit = realSubmit;
3635
+ form.submit();
3636
+ form.submit = wrappedSubmit;
3637
+ };
3638
+ } catch(e) {}
3639
+ }
3640
+ }
3641
+
3642
+ textarea.style.display = "none";
3643
+ var cm = CodeMirror(function(node) {
3644
+ textarea.parentNode.insertBefore(node, textarea.nextSibling);
3645
+ }, options);
3646
+ cm.save = save;
3647
+ cm.getTextArea = function() { return textarea; };
3648
+ cm.toTextArea = function() {
3649
+ save();
3650
+ textarea.parentNode.removeChild(cm.getWrapperElement());
3651
+ textarea.style.display = "";
3652
+ if (textarea.form) {
3653
+ off(textarea.form, "submit", save);
3654
+ if (typeof textarea.form.submit == "function")
3655
+ textarea.form.submit = realSubmit;
3656
+ }
3657
+ };
3658
+ return cm;
3659
+ };
3660
+
3661
+ // STRING STREAM
3662
+
3663
+ // Fed to the mode parsers, provides helper functions to make
3664
+ // parsers more succinct.
3665
+
3666
+ // The character stream used by a mode's parser.
3667
+ function StringStream(string, tabSize) {
3668
+ this.pos = this.start = 0;
3669
+ this.string = string;
3670
+ this.tabSize = tabSize || 8;
3671
+ this.lastColumnPos = this.lastColumnValue = 0;
3672
+ }
3673
+
3674
+ StringStream.prototype = {
3675
+ eol: function() {return this.pos >= this.string.length;},
3676
+ sol: function() {return this.pos == 0;},
3677
+ peek: function() {return this.string.charAt(this.pos) || undefined;},
3678
+ next: function() {
3679
+ if (this.pos < this.string.length)
3680
+ return this.string.charAt(this.pos++);
3681
+ },
3682
+ eat: function(match) {
3683
+ var ch = this.string.charAt(this.pos);
3684
+ if (typeof match == "string") var ok = ch == match;
3685
+ else var ok = ch && (match.test ? match.test(ch) : match(ch));
3686
+ if (ok) {++this.pos; return ch;}
3687
+ },
3688
+ eatWhile: function(match) {
3689
+ var start = this.pos;
3690
+ while (this.eat(match)){}
3691
+ return this.pos > start;
3692
+ },
3693
+ eatSpace: function() {
3694
+ var start = this.pos;
3695
+ while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) ++this.pos;
3696
+ return this.pos > start;
3697
+ },
3698
+ skipToEnd: function() {this.pos = this.string.length;},
3699
+ skipTo: function(ch) {
3700
+ var found = this.string.indexOf(ch, this.pos);
3701
+ if (found > -1) {this.pos = found; return true;}
3702
+ },
3703
+ backUp: function(n) {this.pos -= n;},
3704
+ column: function() {
3705
+ if (this.lastColumnPos < this.start) {
3706
+ this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue);
3707
+ this.lastColumnPos = this.start;
3708
+ }
3709
+ return this.lastColumnValue;
3710
+ },
3711
+ indentation: function() {return countColumn(this.string, null, this.tabSize);},
3712
+ match: function(pattern, consume, caseInsensitive) {
3713
+ if (typeof pattern == "string") {
3714
+ var cased = function(str) {return caseInsensitive ? str.toLowerCase() : str;};
3715
+ var substr = this.string.substr(this.pos, pattern.length);
3716
+ if (cased(substr) == cased(pattern)) {
3717
+ if (consume !== false) this.pos += pattern.length;
3718
+ return true;
3719
+ }
3720
+ } else {
3721
+ var match = this.string.slice(this.pos).match(pattern);
3722
+ if (match && match.index > 0) return null;
3723
+ if (match && consume !== false) this.pos += match[0].length;
3724
+ return match;
3725
+ }
3726
+ },
3727
+ current: function(){return this.string.slice(this.start, this.pos);}
3728
+ };
3729
+ CodeMirror.StringStream = StringStream;
3730
+
3731
+ // TEXTMARKERS
3732
+
3733
+ function TextMarker(doc, type) {
3734
+ this.lines = [];
3735
+ this.type = type;
3736
+ this.doc = doc;
3737
+ }
3738
+ CodeMirror.TextMarker = TextMarker;
3739
+ eventMixin(TextMarker);
3740
+
3741
+ TextMarker.prototype.clear = function() {
3742
+ if (this.explicitlyCleared) return;
3743
+ var cm = this.doc.cm, withOp = cm && !cm.curOp;
3744
+ if (withOp) startOperation(cm);
3745
+ if (hasHandler(this, "clear")) {
3746
+ var found = this.find();
3747
+ if (found) signalLater(this, "clear", found.from, found.to);
3748
+ }
3749
+ var min = null, max = null;
3750
+ for (var i = 0; i < this.lines.length; ++i) {
3751
+ var line = this.lines[i];
3752
+ var span = getMarkedSpanFor(line.markedSpans, this);
3753
+ if (span.to != null) max = lineNo(line);
3754
+ line.markedSpans = removeMarkedSpan(line.markedSpans, span);
3755
+ if (span.from != null)
3756
+ min = lineNo(line);
3757
+ else if (this.collapsed && !lineIsHidden(this.doc, line) && cm)
3758
+ updateLineHeight(line, textHeight(cm.display));
3759
+ }
3760
+ if (cm && this.collapsed && !cm.options.lineWrapping) for (var i = 0; i < this.lines.length; ++i) {
3761
+ var visual = visualLine(cm.doc, this.lines[i]), len = lineLength(cm.doc, visual);
3762
+ if (len > cm.display.maxLineLength) {
3763
+ cm.display.maxLine = visual;
3764
+ cm.display.maxLineLength = len;
3765
+ cm.display.maxLineChanged = true;
3766
+ }
3767
+ }
3768
+
3769
+ if (min != null && cm) regChange(cm, min, max + 1);
3770
+ this.lines.length = 0;
3771
+ this.explicitlyCleared = true;
3772
+ if (this.atomic && this.doc.cantEdit) {
3773
+ this.doc.cantEdit = false;
3774
+ if (cm) reCheckSelection(cm);
3775
+ }
3776
+ if (withOp) endOperation(cm);
3777
+ };
3778
+
3779
+ TextMarker.prototype.find = function() {
3780
+ var from, to;
3781
+ for (var i = 0; i < this.lines.length; ++i) {
3782
+ var line = this.lines[i];
3783
+ var span = getMarkedSpanFor(line.markedSpans, this);
3784
+ if (span.from != null || span.to != null) {
3785
+ var found = lineNo(line);
3786
+ if (span.from != null) from = Pos(found, span.from);
3787
+ if (span.to != null) to = Pos(found, span.to);
3788
+ }
3789
+ }
3790
+ if (this.type == "bookmark") return from;
3791
+ return from && {from: from, to: to};
3792
+ };
3793
+
3794
+ TextMarker.prototype.changed = function() {
3795
+ var pos = this.find(), cm = this.doc.cm;
3796
+ if (!pos || !cm) return;
3797
+ if (this.type != "bookmark") pos = pos.from;
3798
+ var line = getLine(this.doc, pos.line);
3799
+ clearCachedMeasurement(cm, line);
3800
+ if (pos.line >= cm.display.showingFrom && pos.line < cm.display.showingTo) {
3801
+ for (var node = cm.display.lineDiv.firstChild; node; node = node.nextSibling) if (node.lineObj == line) {
3802
+ if (node.offsetHeight != line.height) updateLineHeight(line, node.offsetHeight);
3803
+ break;
3804
+ }
3805
+ runInOp(cm, function() {
3806
+ cm.curOp.selectionChanged = cm.curOp.forceUpdate = cm.curOp.updateMaxLine = true;
3807
+ });
3808
+ }
3809
+ };
3810
+
3811
+ TextMarker.prototype.attachLine = function(line) {
3812
+ if (!this.lines.length && this.doc.cm) {
3813
+ var op = this.doc.cm.curOp;
3814
+ if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1)
3815
+ (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this);
3816
+ }
3817
+ this.lines.push(line);
3818
+ };
3819
+ TextMarker.prototype.detachLine = function(line) {
3820
+ this.lines.splice(indexOf(this.lines, line), 1);
3821
+ if (!this.lines.length && this.doc.cm) {
3822
+ var op = this.doc.cm.curOp;
3823
+ (op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this);
3824
+ }
3825
+ };
3826
+
3827
+ function markText(doc, from, to, options, type) {
3828
+ if (options && options.shared) return markTextShared(doc, from, to, options, type);
3829
+ if (doc.cm && !doc.cm.curOp) return operation(doc.cm, markText)(doc, from, to, options, type);
3830
+
3831
+ var marker = new TextMarker(doc, type);
3832
+ if (type == "range" && !posLess(from, to)) return marker;
3833
+ if (options) copyObj(options, marker);
3834
+ if (marker.replacedWith) {
3835
+ marker.collapsed = true;
3836
+ marker.replacedWith = elt("span", [marker.replacedWith], "CodeMirror-widget");
3837
+ if (!options.handleMouseEvents) marker.replacedWith.ignoreEvents = true;
3838
+ }
3839
+ if (marker.collapsed) sawCollapsedSpans = true;
3840
+
3841
+ if (marker.addToHistory)
3842
+ addToHistory(doc, {from: from, to: to, origin: "markText"},
3843
+ {head: doc.sel.head, anchor: doc.sel.anchor}, NaN);
3844
+
3845
+ var curLine = from.line, size = 0, collapsedAtStart, collapsedAtEnd, cm = doc.cm, updateMaxLine;
3846
+ doc.iter(curLine, to.line + 1, function(line) {
3847
+ if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(doc, line) == cm.display.maxLine)
3848
+ updateMaxLine = true;
3849
+ var span = {from: null, to: null, marker: marker};
3850
+ size += line.text.length;
3851
+ if (curLine == from.line) {span.from = from.ch; size -= from.ch;}
3852
+ if (curLine == to.line) {span.to = to.ch; size -= line.text.length - to.ch;}
3853
+ if (marker.collapsed) {
3854
+ if (curLine == to.line) collapsedAtEnd = collapsedSpanAt(line, to.ch);
3855
+ if (curLine == from.line) collapsedAtStart = collapsedSpanAt(line, from.ch);
3856
+ else updateLineHeight(line, 0);
3857
+ }
3858
+ addMarkedSpan(line, span);
3859
+ ++curLine;
3860
+ });
3861
+ if (marker.collapsed) doc.iter(from.line, to.line + 1, function(line) {
3862
+ if (lineIsHidden(doc, line)) updateLineHeight(line, 0);
3863
+ });
3864
+
3865
+ if (marker.clearOnEnter) on(marker, "beforeCursorEnter", function() { marker.clear(); });
3866
+
3867
+ if (marker.readOnly) {
3868
+ sawReadOnlySpans = true;
3869
+ if (doc.history.done.length || doc.history.undone.length)
3870
+ doc.clearHistory();
3871
+ }
3872
+ if (marker.collapsed) {
3873
+ if (collapsedAtStart != collapsedAtEnd)
3874
+ throw new Error("Inserting collapsed marker overlapping an existing one");
3875
+ marker.size = size;
3876
+ marker.atomic = true;
3877
+ }
3878
+ if (cm) {
3879
+ if (updateMaxLine) cm.curOp.updateMaxLine = true;
3880
+ if (marker.className || marker.title || marker.startStyle || marker.endStyle || marker.collapsed)
3881
+ regChange(cm, from.line, to.line + 1);
3882
+ if (marker.atomic) reCheckSelection(cm);
3883
+ }
3884
+ return marker;
3885
+ }
3886
+
3887
+ // SHARED TEXTMARKERS
3888
+
3889
+ function SharedTextMarker(markers, primary) {
3890
+ this.markers = markers;
3891
+ this.primary = primary;
3892
+ for (var i = 0, me = this; i < markers.length; ++i) {
3893
+ markers[i].parent = this;
3894
+ on(markers[i], "clear", function(){me.clear();});
3895
+ }
3896
+ }
3897
+ CodeMirror.SharedTextMarker = SharedTextMarker;
3898
+ eventMixin(SharedTextMarker);
3899
+
3900
+ SharedTextMarker.prototype.clear = function() {
3901
+ if (this.explicitlyCleared) return;
3902
+ this.explicitlyCleared = true;
3903
+ for (var i = 0; i < this.markers.length; ++i)
3904
+ this.markers[i].clear();
3905
+ signalLater(this, "clear");
3906
+ };
3907
+ SharedTextMarker.prototype.find = function() {
3908
+ return this.primary.find();
3909
+ };
3910
+
3911
+ function markTextShared(doc, from, to, options, type) {
3912
+ options = copyObj(options);
3913
+ options.shared = false;
3914
+ var markers = [markText(doc, from, to, options, type)], primary = markers[0];
3915
+ var widget = options.replacedWith;
3916
+ linkedDocs(doc, function(doc) {
3917
+ if (widget) options.replacedWith = widget.cloneNode(true);
3918
+ markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type));
3919
+ for (var i = 0; i < doc.linked.length; ++i)
3920
+ if (doc.linked[i].isParent) return;
3921
+ primary = lst(markers);
3922
+ });
3923
+ return new SharedTextMarker(markers, primary);
3924
+ }
3925
+
3926
+ // TEXTMARKER SPANS
3927
+
3928
+ function getMarkedSpanFor(spans, marker) {
3929
+ if (spans) for (var i = 0; i < spans.length; ++i) {
3930
+ var span = spans[i];
3931
+ if (span.marker == marker) return span;
3932
+ }
3933
+ }
3934
+ function removeMarkedSpan(spans, span) {
3935
+ for (var r, i = 0; i < spans.length; ++i)
3936
+ if (spans[i] != span) (r || (r = [])).push(spans[i]);
3937
+ return r;
3938
+ }
3939
+ function addMarkedSpan(line, span) {
3940
+ line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span];
3941
+ span.marker.attachLine(line);
3942
+ }
3943
+
3944
+ function markedSpansBefore(old, startCh, isInsert) {
3945
+ if (old) for (var i = 0, nw; i < old.length; ++i) {
3946
+ var span = old[i], marker = span.marker;
3947
+ var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh);
3948
+ if (startsBefore || marker.type == "bookmark" && span.from == startCh && (!isInsert || !span.marker.insertLeft)) {
3949
+ var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh);
3950
+ (nw || (nw = [])).push({from: span.from,
3951
+ to: endsAfter ? null : span.to,
3952
+ marker: marker});
3953
+ }
3954
+ }
3955
+ return nw;
3956
+ }
3957
+
3958
+ function markedSpansAfter(old, endCh, isInsert) {
3959
+ if (old) for (var i = 0, nw; i < old.length; ++i) {
3960
+ var span = old[i], marker = span.marker;
3961
+ var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh);
3962
+ if (endsAfter || marker.type == "bookmark" && span.from == endCh && (!isInsert || span.marker.insertLeft)) {
3963
+ var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh);
3964
+ (nw || (nw = [])).push({from: startsBefore ? null : span.from - endCh,
3965
+ to: span.to == null ? null : span.to - endCh,
3966
+ marker: marker});
3967
+ }
3968
+ }
3969
+ return nw;
3970
+ }
3971
+
3972
+ function stretchSpansOverChange(doc, change) {
3973
+ var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans;
3974
+ var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans;
3975
+ if (!oldFirst && !oldLast) return null;
3976
+
3977
+ var startCh = change.from.ch, endCh = change.to.ch, isInsert = posEq(change.from, change.to);
3978
+ // Get the spans that 'stick out' on both sides
3979
+ var first = markedSpansBefore(oldFirst, startCh, isInsert);
3980
+ var last = markedSpansAfter(oldLast, endCh, isInsert);
3981
+
3982
+ // Next, merge those two ends
3983
+ var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0);
3984
+ if (first) {
3985
+ // Fix up .to properties of first
3986
+ for (var i = 0; i < first.length; ++i) {
3987
+ var span = first[i];
3988
+ if (span.to == null) {
3989
+ var found = getMarkedSpanFor(last, span.marker);
3990
+ if (!found) span.to = startCh;
3991
+ else if (sameLine) span.to = found.to == null ? null : found.to + offset;
3992
+ }
3993
+ }
3994
+ }
3995
+ if (last) {
3996
+ // Fix up .from in last (or move them into first in case of sameLine)
3997
+ for (var i = 0; i < last.length; ++i) {
3998
+ var span = last[i];
3999
+ if (span.to != null) span.to += offset;
4000
+ if (span.from == null) {
4001
+ var found = getMarkedSpanFor(first, span.marker);
4002
+ if (!found) {
4003
+ span.from = offset;
4004
+ if (sameLine) (first || (first = [])).push(span);
4005
+ }
4006
+ } else {
4007
+ span.from += offset;
4008
+ if (sameLine) (first || (first = [])).push(span);
4009
+ }
4010
+ }
4011
+ }
4012
+ if (sameLine && first) {
4013
+ // Make sure we didn't create any zero-length spans
4014
+ for (var i = 0; i < first.length; ++i)
4015
+ if (first[i].from != null && first[i].from == first[i].to && first[i].marker.type != "bookmark")
4016
+ first.splice(i--, 1);
4017
+ if (!first.length) first = null;
4018
+ }
4019
+
4020
+ var newMarkers = [first];
4021
+ if (!sameLine) {
4022
+ // Fill gap with whole-line-spans
4023
+ var gap = change.text.length - 2, gapMarkers;
4024
+ if (gap > 0 && first)
4025
+ for (var i = 0; i < first.length; ++i)
4026
+ if (first[i].to == null)
4027
+ (gapMarkers || (gapMarkers = [])).push({from: null, to: null, marker: first[i].marker});
4028
+ for (var i = 0; i < gap; ++i)
4029
+ newMarkers.push(gapMarkers);
4030
+ newMarkers.push(last);
4031
+ }
4032
+ return newMarkers;
4033
+ }
4034
+
4035
+ function mergeOldSpans(doc, change) {
4036
+ var old = getOldSpans(doc, change);
4037
+ var stretched = stretchSpansOverChange(doc, change);
4038
+ if (!old) return stretched;
4039
+ if (!stretched) return old;
4040
+
4041
+ for (var i = 0; i < old.length; ++i) {
4042
+ var oldCur = old[i], stretchCur = stretched[i];
4043
+ if (oldCur && stretchCur) {
4044
+ spans: for (var j = 0; j < stretchCur.length; ++j) {
4045
+ var span = stretchCur[j];
4046
+ for (var k = 0; k < oldCur.length; ++k)
4047
+ if (oldCur[k].marker == span.marker) continue spans;
4048
+ oldCur.push(span);
4049
+ }
4050
+ } else if (stretchCur) {
4051
+ old[i] = stretchCur;
4052
+ }
4053
+ }
4054
+ return old;
4055
+ }
4056
+
4057
+ function removeReadOnlyRanges(doc, from, to) {
4058
+ var markers = null;
4059
+ doc.iter(from.line, to.line + 1, function(line) {
4060
+ if (line.markedSpans) for (var i = 0; i < line.markedSpans.length; ++i) {
4061
+ var mark = line.markedSpans[i].marker;
4062
+ if (mark.readOnly && (!markers || indexOf(markers, mark) == -1))
4063
+ (markers || (markers = [])).push(mark);
4064
+ }
4065
+ });
4066
+ if (!markers) return null;
4067
+ var parts = [{from: from, to: to}];
4068
+ for (var i = 0; i < markers.length; ++i) {
4069
+ var mk = markers[i], m = mk.find();
4070
+ for (var j = 0; j < parts.length; ++j) {
4071
+ var p = parts[j];
4072
+ if (posLess(p.to, m.from) || posLess(m.to, p.from)) continue;
4073
+ var newParts = [j, 1];
4074
+ if (posLess(p.from, m.from) || !mk.inclusiveLeft && posEq(p.from, m.from))
4075
+ newParts.push({from: p.from, to: m.from});
4076
+ if (posLess(m.to, p.to) || !mk.inclusiveRight && posEq(p.to, m.to))
4077
+ newParts.push({from: m.to, to: p.to});
4078
+ parts.splice.apply(parts, newParts);
4079
+ j += newParts.length - 1;
4080
+ }
4081
+ }
4082
+ return parts;
4083
+ }
4084
+
4085
+ function collapsedSpanAt(line, ch) {
4086
+ var sps = sawCollapsedSpans && line.markedSpans, found;
4087
+ if (sps) for (var sp, i = 0; i < sps.length; ++i) {
4088
+ sp = sps[i];
4089
+ if (!sp.marker.collapsed) continue;
4090
+ if ((sp.from == null || sp.from < ch) &&
4091
+ (sp.to == null || sp.to > ch) &&
4092
+ (!found || found.width < sp.marker.width))
4093
+ found = sp.marker;
4094
+ }
4095
+ return found;
4096
+ }
4097
+ function collapsedSpanAtStart(line) { return collapsedSpanAt(line, -1); }
4098
+ function collapsedSpanAtEnd(line) { return collapsedSpanAt(line, line.text.length + 1); }
4099
+
4100
+ function visualLine(doc, line) {
4101
+ var merged;
4102
+ while (merged = collapsedSpanAtStart(line))
4103
+ line = getLine(doc, merged.find().from.line);
4104
+ return line;
4105
+ }
4106
+
4107
+ function lineIsHidden(doc, line) {
4108
+ var sps = sawCollapsedSpans && line.markedSpans;
4109
+ if (sps) for (var sp, i = 0; i < sps.length; ++i) {
4110
+ sp = sps[i];
4111
+ if (!sp.marker.collapsed) continue;
4112
+ if (sp.from == null) return true;
4113
+ if (sp.marker.replacedWith) continue;
4114
+ if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp))
4115
+ return true;
4116
+ }
4117
+ }
4118
+ function lineIsHiddenInner(doc, line, span) {
4119
+ if (span.to == null) {
4120
+ var end = span.marker.find().to, endLine = getLine(doc, end.line);
4121
+ return lineIsHiddenInner(doc, endLine, getMarkedSpanFor(endLine.markedSpans, span.marker));
4122
+ }
4123
+ if (span.marker.inclusiveRight && span.to == line.text.length)
4124
+ return true;
4125
+ for (var sp, i = 0; i < line.markedSpans.length; ++i) {
4126
+ sp = line.markedSpans[i];
4127
+ if (sp.marker.collapsed && !sp.marker.replacedWith && sp.from == span.to &&
4128
+ (sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
4129
+ lineIsHiddenInner(doc, line, sp)) return true;
4130
+ }
4131
+ }
4132
+
4133
+ function detachMarkedSpans(line) {
4134
+ var spans = line.markedSpans;
4135
+ if (!spans) return;
4136
+ for (var i = 0; i < spans.length; ++i)
4137
+ spans[i].marker.detachLine(line);
4138
+ line.markedSpans = null;
4139
+ }
4140
+
4141
+ function attachMarkedSpans(line, spans) {
4142
+ if (!spans) return;
4143
+ for (var i = 0; i < spans.length; ++i)
4144
+ spans[i].marker.attachLine(line);
4145
+ line.markedSpans = spans;
4146
+ }
4147
+
4148
+ // LINE WIDGETS
4149
+
4150
+ var LineWidget = CodeMirror.LineWidget = function(cm, node, options) {
4151
+ if (options) for (var opt in options) if (options.hasOwnProperty(opt))
4152
+ this[opt] = options[opt];
4153
+ this.cm = cm;
4154
+ this.node = node;
4155
+ };
4156
+ eventMixin(LineWidget);
4157
+ function widgetOperation(f) {
4158
+ return function() {
4159
+ var withOp = !this.cm.curOp;
4160
+ if (withOp) startOperation(this.cm);
4161
+ try {var result = f.apply(this, arguments);}
4162
+ finally {if (withOp) endOperation(this.cm);}
4163
+ return result;
4164
+ };
4165
+ }
4166
+ LineWidget.prototype.clear = widgetOperation(function() {
4167
+ var ws = this.line.widgets, no = lineNo(this.line);
4168
+ if (no == null || !ws) return;
4169
+ for (var i = 0; i < ws.length; ++i) if (ws[i] == this) ws.splice(i--, 1);
4170
+ if (!ws.length) this.line.widgets = null;
4171
+ var aboveVisible = heightAtLine(this.cm, this.line) < this.cm.doc.scrollTop;
4172
+ updateLineHeight(this.line, Math.max(0, this.line.height - widgetHeight(this)));
4173
+ if (aboveVisible) addToScrollPos(this.cm, 0, -this.height);
4174
+ regChange(this.cm, no, no + 1);
4175
+ });
4176
+ LineWidget.prototype.changed = widgetOperation(function() {
4177
+ var oldH = this.height;
4178
+ this.height = null;
4179
+ var diff = widgetHeight(this) - oldH;
4180
+ if (!diff) return;
4181
+ updateLineHeight(this.line, this.line.height + diff);
4182
+ var no = lineNo(this.line);
4183
+ regChange(this.cm, no, no + 1);
4184
+ });
4185
+
4186
+ function widgetHeight(widget) {
4187
+ if (widget.height != null) return widget.height;
4188
+ if (!widget.node.parentNode || widget.node.parentNode.nodeType != 1)
4189
+ removeChildrenAndAdd(widget.cm.display.measure, elt("div", [widget.node], null, "position: relative"));
4190
+ return widget.height = widget.node.offsetHeight;
4191
+ }
4192
+
4193
+ function addLineWidget(cm, handle, node, options) {
4194
+ var widget = new LineWidget(cm, node, options);
4195
+ if (widget.noHScroll) cm.display.alignWidgets = true;
4196
+ changeLine(cm, handle, function(line) {
4197
+ var widgets = line.widgets || (line.widgets = []);
4198
+ if (widget.insertAt == null) widgets.push(widget);
4199
+ else widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget);
4200
+ widget.line = line;
4201
+ if (!lineIsHidden(cm.doc, line) || widget.showIfHidden) {
4202
+ var aboveVisible = heightAtLine(cm, line) < cm.doc.scrollTop;
4203
+ updateLineHeight(line, line.height + widgetHeight(widget));
4204
+ if (aboveVisible) addToScrollPos(cm, 0, widget.height);
4205
+ }
4206
+ return true;
4207
+ });
4208
+ return widget;
4209
+ }
4210
+
4211
+ // LINE DATA STRUCTURE
4212
+
4213
+ // Line objects. These hold state related to a line, including
4214
+ // highlighting info (the styles array).
4215
+ var Line = CodeMirror.Line = function(text, markedSpans, estimateHeight) {
4216
+ this.text = text;
4217
+ attachMarkedSpans(this, markedSpans);
4218
+ this.height = estimateHeight ? estimateHeight(this) : 1;
4219
+ };
4220
+ eventMixin(Line);
4221
+
4222
+ function updateLine(line, text, markedSpans, estimateHeight) {
4223
+ line.text = text;
4224
+ if (line.stateAfter) line.stateAfter = null;
4225
+ if (line.styles) line.styles = null;
4226
+ if (line.order != null) line.order = null;
4227
+ detachMarkedSpans(line);
4228
+ attachMarkedSpans(line, markedSpans);
4229
+ var estHeight = estimateHeight ? estimateHeight(line) : 1;
4230
+ if (estHeight != line.height) updateLineHeight(line, estHeight);
4231
+ }
4232
+
4233
+ function cleanUpLine(line) {
4234
+ line.parent = null;
4235
+ detachMarkedSpans(line);
4236
+ }
4237
+
4238
+ // Run the given mode's parser over a line, update the styles
4239
+ // array, which contains alternating fragments of text and CSS
4240
+ // classes.
4241
+ function runMode(cm, text, mode, state, f) {
4242
+ var flattenSpans = mode.flattenSpans;
4243
+ if (flattenSpans == null) flattenSpans = cm.options.flattenSpans;
4244
+ var curStart = 0, curStyle = null;
4245
+ var stream = new StringStream(text, cm.options.tabSize), style;
4246
+ if (text == "" && mode.blankLine) mode.blankLine(state);
4247
+ while (!stream.eol()) {
4248
+ if (stream.pos > cm.options.maxHighlightLength) {
4249
+ flattenSpans = false;
4250
+ stream.pos = text.length;
4251
+ style = null;
4252
+ } else {
4253
+ style = mode.token(stream, state);
4254
+ }
4255
+ if (!flattenSpans || curStyle != style) {
4256
+ if (curStart < stream.start) f(stream.start, curStyle);
4257
+ curStart = stream.start; curStyle = style;
4258
+ }
4259
+ stream.start = stream.pos;
4260
+ }
4261
+ while (curStart < stream.pos) {
4262
+ // Webkit seems to refuse to render text nodes longer than 57444 characters
4263
+ var pos = Math.min(stream.pos, curStart + 50000);
4264
+ f(pos, curStyle);
4265
+ curStart = pos;
4266
+ }
4267
+ }
4268
+
4269
+ function highlightLine(cm, line, state) {
4270
+ // A styles array always starts with a number identifying the
4271
+ // mode/overlays that it is based on (for easy invalidation).
4272
+ var st = [cm.state.modeGen];
4273
+ // Compute the base array of styles
4274
+ runMode(cm, line.text, cm.doc.mode, state, function(end, style) {st.push(end, style);});
4275
+
4276
+ // Run overlays, adjust style array.
4277
+ for (var o = 0; o < cm.state.overlays.length; ++o) {
4278
+ var overlay = cm.state.overlays[o], i = 1, at = 0;
4279
+ runMode(cm, line.text, overlay.mode, true, function(end, style) {
4280
+ var start = i;
4281
+ // Ensure there's a token end at the current position, and that i points at it
4282
+ while (at < end) {
4283
+ var i_end = st[i];
4284
+ if (i_end > end)
4285
+ st.splice(i, 1, end, st[i+1], i_end);
4286
+ i += 2;
4287
+ at = Math.min(end, i_end);
4288
+ }
4289
+ if (!style) return;
4290
+ if (overlay.opaque) {
4291
+ st.splice(start, i - start, end, style);
4292
+ i = start + 2;
4293
+ } else {
4294
+ for (; start < i; start += 2) {
4295
+ var cur = st[start+1];
4296
+ st[start+1] = cur ? cur + " " + style : style;
4297
+ }
4298
+ }
4299
+ });
4300
+ }
4301
+
4302
+ return st;
4303
+ }
4304
+
4305
+ function getLineStyles(cm, line) {
4306
+ if (!line.styles || line.styles[0] != cm.state.modeGen)
4307
+ line.styles = highlightLine(cm, line, line.stateAfter = getStateBefore(cm, lineNo(line)));
4308
+ return line.styles;
4309
+ }
4310
+
4311
+ // Lightweight form of highlight -- proceed over this line and
4312
+ // update state, but don't save a style array.
4313
+ function processLine(cm, line, state) {
4314
+ var mode = cm.doc.mode;
4315
+ var stream = new StringStream(line.text, cm.options.tabSize);
4316
+ if (line.text == "" && mode.blankLine) mode.blankLine(state);
4317
+ while (!stream.eol() && stream.pos <= cm.options.maxHighlightLength) {
4318
+ mode.token(stream, state);
4319
+ stream.start = stream.pos;
4320
+ }
4321
+ }
4322
+
4323
+ var styleToClassCache = {};
4324
+ function interpretTokenStyle(style, builder) {
4325
+ if (!style) return null;
4326
+ for (;;) {
4327
+ var lineClass = style.match(/(?:^|\s)line-(background-)?(\S+)/);
4328
+ if (!lineClass) break;
4329
+ style = style.slice(0, lineClass.index) + style.slice(lineClass.index + lineClass[0].length);
4330
+ var prop = lineClass[1] ? "bgClass" : "textClass";
4331
+ if (builder[prop] == null)
4332
+ builder[prop] = lineClass[2];
4333
+ else if (!(new RegExp("(?:^|\s)" + lineClass[2] + "(?:$|\s)")).test(builder[prop]))
4334
+ builder[prop] += " " + lineClass[2];
4335
+ }
4336
+ return styleToClassCache[style] ||
4337
+ (styleToClassCache[style] = "cm-" + style.replace(/ +/g, " cm-"));
4338
+ }
4339
+
4340
+ function buildLineContent(cm, realLine, measure, copyWidgets) {
4341
+ var merged, line = realLine, empty = true;
4342
+ while (merged = collapsedSpanAtStart(line))
4343
+ line = getLine(cm.doc, merged.find().from.line);
4344
+
4345
+ var builder = {pre: elt("pre"), col: 0, pos: 0,
4346
+ measure: null, measuredSomething: false, cm: cm,
4347
+ copyWidgets: copyWidgets};
4348
+
4349
+ do {
4350
+ if (line.text) empty = false;
4351
+ builder.measure = line == realLine && measure;
4352
+ builder.pos = 0;
4353
+ builder.addToken = builder.measure ? buildTokenMeasure : buildToken;
4354
+ if ((ie || webkit) && cm.getOption("lineWrapping"))
4355
+ builder.addToken = buildTokenSplitSpaces(builder.addToken);
4356
+ var next = insertLineContent(line, builder, getLineStyles(cm, line));
4357
+ if (measure && line == realLine && !builder.measuredSomething) {
4358
+ measure[0] = builder.pre.appendChild(zeroWidthElement(cm.display.measure));
4359
+ builder.measuredSomething = true;
4360
+ }
4361
+ if (next) line = getLine(cm.doc, next.to.line);
4362
+ } while (next);
4363
+
4364
+ if (measure && !builder.measuredSomething && !measure[0])
4365
+ measure[0] = builder.pre.appendChild(empty ? elt("span", "\u00a0") : zeroWidthElement(cm.display.measure));
4366
+ if (!builder.pre.firstChild && !lineIsHidden(cm.doc, realLine))
4367
+ builder.pre.appendChild(document.createTextNode("\u00a0"));
4368
+
4369
+ var order;
4370
+ // Work around problem with the reported dimensions of single-char
4371
+ // direction spans on IE (issue #1129). See also the comment in
4372
+ // cursorCoords.
4373
+ if (measure && ie && (order = getOrder(line))) {
4374
+ var l = order.length - 1;
4375
+ if (order[l].from == order[l].to) --l;
4376
+ var last = order[l], prev = order[l - 1];
4377
+ if (last.from + 1 == last.to && prev && last.level < prev.level) {
4378
+ var span = measure[builder.pos - 1];
4379
+ if (span) span.parentNode.insertBefore(span.measureRight = zeroWidthElement(cm.display.measure),
4380
+ span.nextSibling);
4381
+ }
4382
+ }
4383
+
4384
+ var textClass = builder.textClass ? builder.textClass + " " + (realLine.textClass || "") : realLine.textClass;
4385
+ if (textClass) builder.pre.className = textClass;
4386
+
4387
+ signal(cm, "renderLine", cm, realLine, builder.pre);
4388
+ return builder;
4389
+ }
4390
+
4391
+ var tokenSpecialChars = /[\t\u0000-\u0019\u00ad\u200b\u2028\u2029\uFEFF]/g;
4392
+ function buildToken(builder, text, style, startStyle, endStyle, title) {
4393
+ if (!text) return;
4394
+ if (!tokenSpecialChars.test(text)) {
4395
+ builder.col += text.length;
4396
+ var content = document.createTextNode(text);
4397
+ } else {
4398
+ var content = document.createDocumentFragment(), pos = 0;
4399
+ while (true) {
4400
+ tokenSpecialChars.lastIndex = pos;
4401
+ var m = tokenSpecialChars.exec(text);
4402
+ var skipped = m ? m.index - pos : text.length - pos;
4403
+ if (skipped) {
4404
+ content.appendChild(document.createTextNode(text.slice(pos, pos + skipped)));
4405
+ builder.col += skipped;
4406
+ }
4407
+ if (!m) break;
4408
+ pos += skipped + 1;
4409
+ if (m[0] == "\t") {
4410
+ var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize;
4411
+ content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab"));
4412
+ builder.col += tabWidth;
4413
+ } else {
4414
+ var token = elt("span", "\u2022", "cm-invalidchar");
4415
+ token.title = "\\u" + m[0].charCodeAt(0).toString(16);
4416
+ content.appendChild(token);
4417
+ builder.col += 1;
4418
+ }
4419
+ }
4420
+ }
4421
+ if (style || startStyle || endStyle || builder.measure) {
4422
+ var fullStyle = style || "";
4423
+ if (startStyle) fullStyle += startStyle;
4424
+ if (endStyle) fullStyle += endStyle;
4425
+ var token = elt("span", [content], fullStyle);
4426
+ if (title) token.title = title;
4427
+ return builder.pre.appendChild(token);
4428
+ }
4429
+ builder.pre.appendChild(content);
4430
+ }
4431
+
4432
+ function buildTokenMeasure(builder, text, style, startStyle, endStyle) {
4433
+ var wrapping = builder.cm.options.lineWrapping;
4434
+ for (var i = 0; i < text.length; ++i) {
4435
+ var ch = text.charAt(i), start = i == 0;
4436
+ if (ch >= "\ud800" && ch < "\udbff" && i < text.length - 1) {
4437
+ ch = text.slice(i, i + 2);
4438
+ ++i;
4439
+ } else if (i && wrapping && spanAffectsWrapping(text, i)) {
4440
+ builder.pre.appendChild(elt("wbr"));
4441
+ }
4442
+ var old = builder.measure[builder.pos];
4443
+ var span = builder.measure[builder.pos] =
4444
+ buildToken(builder, ch, style,
4445
+ start && startStyle, i == text.length - 1 && endStyle);
4446
+ if (old) span.leftSide = old.leftSide || old;
4447
+ // In IE single-space nodes wrap differently than spaces
4448
+ // embedded in larger text nodes, except when set to
4449
+ // white-space: normal (issue #1268).
4450
+ if (ie && wrapping && ch == " " && i && !/\s/.test(text.charAt(i - 1)) &&
4451
+ i < text.length - 1 && !/\s/.test(text.charAt(i + 1)))
4452
+ span.style.whiteSpace = "normal";
4453
+ builder.pos += ch.length;
4454
+ }
4455
+ if (text.length) builder.measuredSomething = true;
4456
+ }
4457
+
4458
+ function buildTokenSplitSpaces(inner) {
4459
+ function split(old) {
4460
+ var out = " ";
4461
+ for (var i = 0; i < old.length - 2; ++i) out += i % 2 ? " " : "\u00a0";
4462
+ out += " ";
4463
+ return out;
4464
+ }
4465
+ return function(builder, text, style, startStyle, endStyle, title) {
4466
+ return inner(builder, text.replace(/ {3,}/, split), style, startStyle, endStyle, title);
4467
+ };
4468
+ }
4469
+
4470
+ function buildCollapsedSpan(builder, size, marker, ignoreWidget) {
4471
+ var widget = !ignoreWidget && marker.replacedWith;
4472
+ if (widget) {
4473
+ if (builder.copyWidgets) widget = widget.cloneNode(true);
4474
+ builder.pre.appendChild(widget);
4475
+ if (builder.measure) {
4476
+ if (size) {
4477
+ builder.measure[builder.pos] = widget;
4478
+ } else {
4479
+ var elt = zeroWidthElement(builder.cm.display.measure);
4480
+ if (marker.type == "bookmark" && !marker.insertLeft)
4481
+ builder.measure[builder.pos] = builder.pre.appendChild(elt);
4482
+ else if (builder.measure[builder.pos])
4483
+ return;
4484
+ else
4485
+ builder.measure[builder.pos] = builder.pre.insertBefore(elt, widget);
4486
+ }
4487
+ builder.measuredSomething = true;
4488
+ }
4489
+ }
4490
+ builder.pos += size;
4491
+ }
4492
+
4493
+ // Outputs a number of spans to make up a line, taking highlighting
4494
+ // and marked text into account.
4495
+ function insertLineContent(line, builder, styles) {
4496
+ var spans = line.markedSpans, allText = line.text, at = 0;
4497
+ if (!spans) {
4498
+ for (var i = 1; i < styles.length; i+=2)
4499
+ builder.addToken(builder, allText.slice(at, at = styles[i]), interpretTokenStyle(styles[i+1], builder));
4500
+ return;
4501
+ }
4502
+
4503
+ var len = allText.length, pos = 0, i = 1, text = "", style;
4504
+ var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, title, collapsed;
4505
+ for (;;) {
4506
+ if (nextChange == pos) { // Update current marker set
4507
+ spanStyle = spanEndStyle = spanStartStyle = title = "";
4508
+ collapsed = null; nextChange = Infinity;
4509
+ var foundBookmarks = [];
4510
+ for (var j = 0; j < spans.length; ++j) {
4511
+ var sp = spans[j], m = sp.marker;
4512
+ if (sp.from <= pos && (sp.to == null || sp.to > pos)) {
4513
+ if (sp.to != null && nextChange > sp.to) { nextChange = sp.to; spanEndStyle = ""; }
4514
+ if (m.className) spanStyle += " " + m.className;
4515
+ if (m.startStyle && sp.from == pos) spanStartStyle += " " + m.startStyle;
4516
+ if (m.endStyle && sp.to == nextChange) spanEndStyle += " " + m.endStyle;
4517
+ if (m.title && !title) title = m.title;
4518
+ if (m.collapsed && (!collapsed || collapsed.marker.size < m.size))
4519
+ collapsed = sp;
4520
+ } else if (sp.from > pos && nextChange > sp.from) {
4521
+ nextChange = sp.from;
4522
+ }
4523
+ if (m.type == "bookmark" && sp.from == pos && m.replacedWith) foundBookmarks.push(m);
4524
+ }
4525
+ if (collapsed && (collapsed.from || 0) == pos) {
4526
+ buildCollapsedSpan(builder, (collapsed.to == null ? len : collapsed.to) - pos,
4527
+ collapsed.marker, collapsed.from == null);
4528
+ if (collapsed.to == null) return collapsed.marker.find();
4529
+ }
4530
+ if (!collapsed && foundBookmarks.length) for (var j = 0; j < foundBookmarks.length; ++j)
4531
+ buildCollapsedSpan(builder, 0, foundBookmarks[j]);
4532
+ }
4533
+ if (pos >= len) break;
4534
+
4535
+ var upto = Math.min(len, nextChange);
4536
+ while (true) {
4537
+ if (text) {
4538
+ var end = pos + text.length;
4539
+ if (!collapsed) {
4540
+ var tokenText = end > upto ? text.slice(0, upto - pos) : text;
4541
+ builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle,
4542
+ spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", title);
4543
+ }
4544
+ if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;}
4545
+ pos = end;
4546
+ spanStartStyle = "";
4547
+ }
4548
+ text = allText.slice(at, at = styles[i++]);
4549
+ style = interpretTokenStyle(styles[i++], builder);
4550
+ }
4551
+ }
4552
+ }
4553
+
4554
+ // DOCUMENT DATA STRUCTURE
4555
+
4556
+ function updateDoc(doc, change, markedSpans, selAfter, estimateHeight) {
4557
+ function spansFor(n) {return markedSpans ? markedSpans[n] : null;}
4558
+ function update(line, text, spans) {
4559
+ updateLine(line, text, spans, estimateHeight);
4560
+ signalLater(line, "change", line, change);
4561
+ }
4562
+
4563
+ var from = change.from, to = change.to, text = change.text;
4564
+ var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line);
4565
+ var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line;
4566
+
4567
+ // First adjust the line structure
4568
+ if (from.ch == 0 && to.ch == 0 && lastText == "") {
4569
+ // This is a whole-line replace. Treated specially to make
4570
+ // sure line objects move the way they are supposed to.
4571
+ for (var i = 0, e = text.length - 1, added = []; i < e; ++i)
4572
+ added.push(new Line(text[i], spansFor(i), estimateHeight));
4573
+ update(lastLine, lastLine.text, lastSpans);
4574
+ if (nlines) doc.remove(from.line, nlines);
4575
+ if (added.length) doc.insert(from.line, added);
4576
+ } else if (firstLine == lastLine) {
4577
+ if (text.length == 1) {
4578
+ update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans);
4579
+ } else {
4580
+ for (var added = [], i = 1, e = text.length - 1; i < e; ++i)
4581
+ added.push(new Line(text[i], spansFor(i), estimateHeight));
4582
+ added.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight));
4583
+ update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
4584
+ doc.insert(from.line + 1, added);
4585
+ }
4586
+ } else if (text.length == 1) {
4587
+ update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0));
4588
+ doc.remove(from.line + 1, nlines);
4589
+ } else {
4590
+ update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
4591
+ update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans);
4592
+ for (var i = 1, e = text.length - 1, added = []; i < e; ++i)
4593
+ added.push(new Line(text[i], spansFor(i), estimateHeight));
4594
+ if (nlines > 1) doc.remove(from.line + 1, nlines - 1);
4595
+ doc.insert(from.line + 1, added);
4596
+ }
4597
+
4598
+ signalLater(doc, "change", doc, change);
4599
+ setSelection(doc, selAfter.anchor, selAfter.head, null, true);
4600
+ }
4601
+
4602
+ function LeafChunk(lines) {
4603
+ this.lines = lines;
4604
+ this.parent = null;
4605
+ for (var i = 0, e = lines.length, height = 0; i < e; ++i) {
4606
+ lines[i].parent = this;
4607
+ height += lines[i].height;
4608
+ }
4609
+ this.height = height;
4610
+ }
4611
+
4612
+ LeafChunk.prototype = {
4613
+ chunkSize: function() { return this.lines.length; },
4614
+ removeInner: function(at, n) {
4615
+ for (var i = at, e = at + n; i < e; ++i) {
4616
+ var line = this.lines[i];
4617
+ this.height -= line.height;
4618
+ cleanUpLine(line);
4619
+ signalLater(line, "delete");
4620
+ }
4621
+ this.lines.splice(at, n);
4622
+ },
4623
+ collapse: function(lines) {
4624
+ lines.splice.apply(lines, [lines.length, 0].concat(this.lines));
4625
+ },
4626
+ insertInner: function(at, lines, height) {
4627
+ this.height += height;
4628
+ this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at));
4629
+ for (var i = 0, e = lines.length; i < e; ++i) lines[i].parent = this;
4630
+ },
4631
+ iterN: function(at, n, op) {
4632
+ for (var e = at + n; at < e; ++at)
4633
+ if (op(this.lines[at])) return true;
4634
+ }
4635
+ };
4636
+
4637
+ function BranchChunk(children) {
4638
+ this.children = children;
4639
+ var size = 0, height = 0;
4640
+ for (var i = 0, e = children.length; i < e; ++i) {
4641
+ var ch = children[i];
4642
+ size += ch.chunkSize(); height += ch.height;
4643
+ ch.parent = this;
4644
+ }
4645
+ this.size = size;
4646
+ this.height = height;
4647
+ this.parent = null;
4648
+ }
4649
+
4650
+ BranchChunk.prototype = {
4651
+ chunkSize: function() { return this.size; },
4652
+ removeInner: function(at, n) {
4653
+ this.size -= n;
4654
+ for (var i = 0; i < this.children.length; ++i) {
4655
+ var child = this.children[i], sz = child.chunkSize();
4656
+ if (at < sz) {
4657
+ var rm = Math.min(n, sz - at), oldHeight = child.height;
4658
+ child.removeInner(at, rm);
4659
+ this.height -= oldHeight - child.height;
4660
+ if (sz == rm) { this.children.splice(i--, 1); child.parent = null; }
4661
+ if ((n -= rm) == 0) break;
4662
+ at = 0;
4663
+ } else at -= sz;
4664
+ }
4665
+ if (this.size - n < 25) {
4666
+ var lines = [];
4667
+ this.collapse(lines);
4668
+ this.children = [new LeafChunk(lines)];
4669
+ this.children[0].parent = this;
4670
+ }
4671
+ },
4672
+ collapse: function(lines) {
4673
+ for (var i = 0, e = this.children.length; i < e; ++i) this.children[i].collapse(lines);
4674
+ },
4675
+ insertInner: function(at, lines, height) {
4676
+ this.size += lines.length;
4677
+ this.height += height;
4678
+ for (var i = 0, e = this.children.length; i < e; ++i) {
4679
+ var child = this.children[i], sz = child.chunkSize();
4680
+ if (at <= sz) {
4681
+ child.insertInner(at, lines, height);
4682
+ if (child.lines && child.lines.length > 50) {
4683
+ while (child.lines.length > 50) {
4684
+ var spilled = child.lines.splice(child.lines.length - 25, 25);
4685
+ var newleaf = new LeafChunk(spilled);
4686
+ child.height -= newleaf.height;
4687
+ this.children.splice(i + 1, 0, newleaf);
4688
+ newleaf.parent = this;
4689
+ }
4690
+ this.maybeSpill();
4691
+ }
4692
+ break;
4693
+ }
4694
+ at -= sz;
4695
+ }
4696
+ },
4697
+ maybeSpill: function() {
4698
+ if (this.children.length <= 10) return;
4699
+ var me = this;
4700
+ do {
4701
+ var spilled = me.children.splice(me.children.length - 5, 5);
4702
+ var sibling = new BranchChunk(spilled);
4703
+ if (!me.parent) { // Become the parent node
4704
+ var copy = new BranchChunk(me.children);
4705
+ copy.parent = me;
4706
+ me.children = [copy, sibling];
4707
+ me = copy;
4708
+ } else {
4709
+ me.size -= sibling.size;
4710
+ me.height -= sibling.height;
4711
+ var myIndex = indexOf(me.parent.children, me);
4712
+ me.parent.children.splice(myIndex + 1, 0, sibling);
4713
+ }
4714
+ sibling.parent = me.parent;
4715
+ } while (me.children.length > 10);
4716
+ me.parent.maybeSpill();
4717
+ },
4718
+ iterN: function(at, n, op) {
4719
+ for (var i = 0, e = this.children.length; i < e; ++i) {
4720
+ var child = this.children[i], sz = child.chunkSize();
4721
+ if (at < sz) {
4722
+ var used = Math.min(n, sz - at);
4723
+ if (child.iterN(at, used, op)) return true;
4724
+ if ((n -= used) == 0) break;
4725
+ at = 0;
4726
+ } else at -= sz;
4727
+ }
4728
+ }
4729
+ };
4730
+
4731
+ var nextDocId = 0;
4732
+ var Doc = CodeMirror.Doc = function(text, mode, firstLine) {
4733
+ if (!(this instanceof Doc)) return new Doc(text, mode, firstLine);
4734
+ if (firstLine == null) firstLine = 0;
4735
+
4736
+ BranchChunk.call(this, [new LeafChunk([new Line("", null)])]);
4737
+ this.first = firstLine;
4738
+ this.scrollTop = this.scrollLeft = 0;
4739
+ this.cantEdit = false;
4740
+ this.history = makeHistory();
4741
+ this.cleanGeneration = 1;
4742
+ this.frontier = firstLine;
4743
+ var start = Pos(firstLine, 0);
4744
+ this.sel = {from: start, to: start, head: start, anchor: start, shift: false, extend: false, goalColumn: null};
4745
+ this.id = ++nextDocId;
4746
+ this.modeOption = mode;
4747
+
4748
+ if (typeof text == "string") text = splitLines(text);
4749
+ updateDoc(this, {from: start, to: start, text: text}, null, {head: start, anchor: start});
4750
+ };
4751
+
4752
+ Doc.prototype = createObj(BranchChunk.prototype, {
4753
+ constructor: Doc,
4754
+ iter: function(from, to, op) {
4755
+ if (op) this.iterN(from - this.first, to - from, op);
4756
+ else this.iterN(this.first, this.first + this.size, from);
4757
+ },
4758
+
4759
+ insert: function(at, lines) {
4760
+ var height = 0;
4761
+ for (var i = 0, e = lines.length; i < e; ++i) height += lines[i].height;
4762
+ this.insertInner(at - this.first, lines, height);
4763
+ },
4764
+ remove: function(at, n) { this.removeInner(at - this.first, n); },
4765
+
4766
+ getValue: function(lineSep) {
4767
+ var lines = getLines(this, this.first, this.first + this.size);
4768
+ if (lineSep === false) return lines;
4769
+ return lines.join(lineSep || "\n");
4770
+ },
4771
+ setValue: function(code) {
4772
+ var top = Pos(this.first, 0), last = this.first + this.size - 1;
4773
+ makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length),
4774
+ text: splitLines(code), origin: "setValue"},
4775
+ {head: top, anchor: top}, true);
4776
+ },
4777
+ replaceRange: function(code, from, to, origin) {
4778
+ from = clipPos(this, from);
4779
+ to = to ? clipPos(this, to) : from;
4780
+ replaceRange(this, code, from, to, origin);
4781
+ },
4782
+ getRange: function(from, to, lineSep) {
4783
+ var lines = getBetween(this, clipPos(this, from), clipPos(this, to));
4784
+ if (lineSep === false) return lines;
4785
+ return lines.join(lineSep || "\n");
4786
+ },
4787
+
4788
+ getLine: function(line) {var l = this.getLineHandle(line); return l && l.text;},
4789
+ setLine: function(line, text) {
4790
+ if (isLine(this, line))
4791
+ replaceRange(this, text, Pos(line, 0), clipPos(this, Pos(line)));
4792
+ },
4793
+ removeLine: function(line) {
4794
+ if (line) replaceRange(this, "", clipPos(this, Pos(line - 1)), clipPos(this, Pos(line)));
4795
+ else replaceRange(this, "", Pos(0, 0), clipPos(this, Pos(1, 0)));
4796
+ },
4797
+
4798
+ getLineHandle: function(line) {if (isLine(this, line)) return getLine(this, line);},
4799
+ getLineNumber: function(line) {return lineNo(line);},
4800
+
4801
+ getLineHandleVisualStart: function(line) {
4802
+ if (typeof line == "number") line = getLine(this, line);
4803
+ return visualLine(this, line);
4804
+ },
4805
+
4806
+ lineCount: function() {return this.size;},
4807
+ firstLine: function() {return this.first;},
4808
+ lastLine: function() {return this.first + this.size - 1;},
4809
+
4810
+ clipPos: function(pos) {return clipPos(this, pos);},
4811
+
4812
+ getCursor: function(start) {
4813
+ var sel = this.sel, pos;
4814
+ if (start == null || start == "head") pos = sel.head;
4815
+ else if (start == "anchor") pos = sel.anchor;
4816
+ else if (start == "end" || start === false) pos = sel.to;
4817
+ else pos = sel.from;
4818
+ return copyPos(pos);
4819
+ },
4820
+ somethingSelected: function() {return !posEq(this.sel.head, this.sel.anchor);},
4821
+
4822
+ setCursor: docOperation(function(line, ch, extend) {
4823
+ var pos = clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line);
4824
+ if (extend) extendSelection(this, pos);
4825
+ else setSelection(this, pos, pos);
4826
+ }),
4827
+ setSelection: docOperation(function(anchor, head, bias) {
4828
+ setSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), bias);
4829
+ }),
4830
+ extendSelection: docOperation(function(from, to, bias) {
4831
+ extendSelection(this, clipPos(this, from), to && clipPos(this, to), bias);
4832
+ }),
4833
+
4834
+ getSelection: function(lineSep) {return this.getRange(this.sel.from, this.sel.to, lineSep);},
4835
+ replaceSelection: function(code, collapse, origin) {
4836
+ makeChange(this, {from: this.sel.from, to: this.sel.to, text: splitLines(code), origin: origin}, collapse || "around");
4837
+ },
4838
+ undo: docOperation(function() {makeChangeFromHistory(this, "undo");}),
4839
+ redo: docOperation(function() {makeChangeFromHistory(this, "redo");}),
4840
+
4841
+ setExtending: function(val) {this.sel.extend = val;},
4842
+
4843
+ historySize: function() {
4844
+ var hist = this.history;
4845
+ return {undo: hist.done.length, redo: hist.undone.length};
4846
+ },
4847
+ clearHistory: function() {this.history = makeHistory(this.history.maxGeneration);},
4848
+
4849
+ markClean: function() {
4850
+ this.cleanGeneration = this.changeGeneration();
4851
+ },
4852
+ changeGeneration: function() {
4853
+ this.history.lastOp = this.history.lastOrigin = null;
4854
+ return this.history.generation;
4855
+ },
4856
+ isClean: function (gen) {
4857
+ return this.history.generation == (gen || this.cleanGeneration);
4858
+ },
4859
+
4860
+ getHistory: function() {
4861
+ return {done: copyHistoryArray(this.history.done),
4862
+ undone: copyHistoryArray(this.history.undone)};
4863
+ },
4864
+ setHistory: function(histData) {
4865
+ var hist = this.history = makeHistory(this.history.maxGeneration);
4866
+ hist.done = histData.done.slice(0);
4867
+ hist.undone = histData.undone.slice(0);
4868
+ },
4869
+
4870
+ markText: function(from, to, options) {
4871
+ return markText(this, clipPos(this, from), clipPos(this, to), options, "range");
4872
+ },
4873
+ setBookmark: function(pos, options) {
4874
+ var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options),
4875
+ insertLeft: options && options.insertLeft};
4876
+ pos = clipPos(this, pos);
4877
+ return markText(this, pos, pos, realOpts, "bookmark");
4878
+ },
4879
+ findMarksAt: function(pos) {
4880
+ pos = clipPos(this, pos);
4881
+ var markers = [], spans = getLine(this, pos.line).markedSpans;
4882
+ if (spans) for (var i = 0; i < spans.length; ++i) {
4883
+ var span = spans[i];
4884
+ if ((span.from == null || span.from <= pos.ch) &&
4885
+ (span.to == null || span.to >= pos.ch))
4886
+ markers.push(span.marker.parent || span.marker);
4887
+ }
4888
+ return markers;
4889
+ },
4890
+ getAllMarks: function() {
4891
+ var markers = [];
4892
+ this.iter(function(line) {
4893
+ var sps = line.markedSpans;
4894
+ if (sps) for (var i = 0; i < sps.length; ++i)
4895
+ if (sps[i].from != null) markers.push(sps[i].marker);
4896
+ });
4897
+ return markers;
4898
+ },
4899
+
4900
+ posFromIndex: function(off) {
4901
+ var ch, lineNo = this.first;
4902
+ this.iter(function(line) {
4903
+ var sz = line.text.length + 1;
4904
+ if (sz > off) { ch = off; return true; }
4905
+ off -= sz;
4906
+ ++lineNo;
4907
+ });
4908
+ return clipPos(this, Pos(lineNo, ch));
4909
+ },
4910
+ indexFromPos: function (coords) {
4911
+ coords = clipPos(this, coords);
4912
+ var index = coords.ch;
4913
+ if (coords.line < this.first || coords.ch < 0) return 0;
4914
+ this.iter(this.first, coords.line, function (line) {
4915
+ index += line.text.length + 1;
4916
+ });
4917
+ return index;
4918
+ },
4919
+
4920
+ copy: function(copyHistory) {
4921
+ var doc = new Doc(getLines(this, this.first, this.first + this.size), this.modeOption, this.first);
4922
+ doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft;
4923
+ doc.sel = {from: this.sel.from, to: this.sel.to, head: this.sel.head, anchor: this.sel.anchor,
4924
+ shift: this.sel.shift, extend: false, goalColumn: this.sel.goalColumn};
4925
+ if (copyHistory) {
4926
+ doc.history.undoDepth = this.history.undoDepth;
4927
+ doc.setHistory(this.getHistory());
4928
+ }
4929
+ return doc;
4930
+ },
4931
+
4932
+ linkedDoc: function(options) {
4933
+ if (!options) options = {};
4934
+ var from = this.first, to = this.first + this.size;
4935
+ if (options.from != null && options.from > from) from = options.from;
4936
+ if (options.to != null && options.to < to) to = options.to;
4937
+ var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from);
4938
+ if (options.sharedHist) copy.history = this.history;
4939
+ (this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist});
4940
+ copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}];
4941
+ return copy;
4942
+ },
4943
+ unlinkDoc: function(other) {
4944
+ if (other instanceof CodeMirror) other = other.doc;
4945
+ if (this.linked) for (var i = 0; i < this.linked.length; ++i) {
4946
+ var link = this.linked[i];
4947
+ if (link.doc != other) continue;
4948
+ this.linked.splice(i, 1);
4949
+ other.unlinkDoc(this);
4950
+ break;
4951
+ }
4952
+ // If the histories were shared, split them again
4953
+ if (other.history == this.history) {
4954
+ var splitIds = [other.id];
4955
+ linkedDocs(other, function(doc) {splitIds.push(doc.id);}, true);
4956
+ other.history = makeHistory();
4957
+ other.history.done = copyHistoryArray(this.history.done, splitIds);
4958
+ other.history.undone = copyHistoryArray(this.history.undone, splitIds);
4959
+ }
4960
+ },
4961
+ iterLinkedDocs: function(f) {linkedDocs(this, f);},
4962
+
4963
+ getMode: function() {return this.mode;},
4964
+ getEditor: function() {return this.cm;}
4965
+ });
4966
+
4967
+ Doc.prototype.eachLine = Doc.prototype.iter;
4968
+
4969
+ // The Doc methods that should be available on CodeMirror instances
4970
+ var dontDelegate = "iter insert remove copy getEditor".split(" ");
4971
+ for (var prop in Doc.prototype) if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0)
4972
+ CodeMirror.prototype[prop] = (function(method) {
4973
+ return function() {return method.apply(this.doc, arguments);};
4974
+ })(Doc.prototype[prop]);
4975
+
4976
+ eventMixin(Doc);
4977
+
4978
+ function linkedDocs(doc, f, sharedHistOnly) {
4979
+ function propagate(doc, skip, sharedHist) {
4980
+ if (doc.linked) for (var i = 0; i < doc.linked.length; ++i) {
4981
+ var rel = doc.linked[i];
4982
+ if (rel.doc == skip) continue;
4983
+ var shared = sharedHist && rel.sharedHist;
4984
+ if (sharedHistOnly && !shared) continue;
4985
+ f(rel.doc, shared);
4986
+ propagate(rel.doc, doc, shared);
4987
+ }
4988
+ }
4989
+ propagate(doc, null, true);
4990
+ }
4991
+
4992
+ function attachDoc(cm, doc) {
4993
+ if (doc.cm) throw new Error("This document is already in use.");
4994
+ cm.doc = doc;
4995
+ doc.cm = cm;
4996
+ estimateLineHeights(cm);
4997
+ loadMode(cm);
4998
+ if (!cm.options.lineWrapping) computeMaxLength(cm);
4999
+ cm.options.mode = doc.modeOption;
5000
+ regChange(cm);
5001
+ }
5002
+
5003
+ // LINE UTILITIES
5004
+
5005
+ function getLine(chunk, n) {
5006
+ n -= chunk.first;
5007
+ while (!chunk.lines) {
5008
+ for (var i = 0;; ++i) {
5009
+ var child = chunk.children[i], sz = child.chunkSize();
5010
+ if (n < sz) { chunk = child; break; }
5011
+ n -= sz;
5012
+ }
5013
+ }
5014
+ return chunk.lines[n];
5015
+ }
5016
+
5017
+ function getBetween(doc, start, end) {
5018
+ var out = [], n = start.line;
5019
+ doc.iter(start.line, end.line + 1, function(line) {
5020
+ var text = line.text;
5021
+ if (n == end.line) text = text.slice(0, end.ch);
5022
+ if (n == start.line) text = text.slice(start.ch);
5023
+ out.push(text);
5024
+ ++n;
5025
+ });
5026
+ return out;
5027
+ }
5028
+ function getLines(doc, from, to) {
5029
+ var out = [];
5030
+ doc.iter(from, to, function(line) { out.push(line.text); });
5031
+ return out;
5032
+ }
5033
+
5034
+ function updateLineHeight(line, height) {
5035
+ var diff = height - line.height;
5036
+ for (var n = line; n; n = n.parent) n.height += diff;
5037
+ }
5038
+
5039
+ function lineNo(line) {
5040
+ if (line.parent == null) return null;
5041
+ var cur = line.parent, no = indexOf(cur.lines, line);
5042
+ for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {
5043
+ for (var i = 0;; ++i) {
5044
+ if (chunk.children[i] == cur) break;
5045
+ no += chunk.children[i].chunkSize();
5046
+ }
5047
+ }
5048
+ return no + cur.first;
5049
+ }
5050
+
5051
+ function lineAtHeight(chunk, h) {
5052
+ var n = chunk.first;
5053
+ outer: do {
5054
+ for (var i = 0, e = chunk.children.length; i < e; ++i) {
5055
+ var child = chunk.children[i], ch = child.height;
5056
+ if (h < ch) { chunk = child; continue outer; }
5057
+ h -= ch;
5058
+ n += child.chunkSize();
5059
+ }
5060
+ return n;
5061
+ } while (!chunk.lines);
5062
+ for (var i = 0, e = chunk.lines.length; i < e; ++i) {
5063
+ var line = chunk.lines[i], lh = line.height;
5064
+ if (h < lh) break;
5065
+ h -= lh;
5066
+ }
5067
+ return n + i;
5068
+ }
5069
+
5070
+ function heightAtLine(cm, lineObj) {
5071
+ lineObj = visualLine(cm.doc, lineObj);
5072
+
5073
+ var h = 0, chunk = lineObj.parent;
5074
+ for (var i = 0; i < chunk.lines.length; ++i) {
5075
+ var line = chunk.lines[i];
5076
+ if (line == lineObj) break;
5077
+ else h += line.height;
5078
+ }
5079
+ for (var p = chunk.parent; p; chunk = p, p = chunk.parent) {
5080
+ for (var i = 0; i < p.children.length; ++i) {
5081
+ var cur = p.children[i];
5082
+ if (cur == chunk) break;
5083
+ else h += cur.height;
5084
+ }
5085
+ }
5086
+ return h;
5087
+ }
5088
+
5089
+ function getOrder(line) {
5090
+ var order = line.order;
5091
+ if (order == null) order = line.order = bidiOrdering(line.text);
5092
+ return order;
5093
+ }
5094
+
5095
+ // HISTORY
5096
+
5097
+ function makeHistory(startGen) {
5098
+ return {
5099
+ // Arrays of history events. Doing something adds an event to
5100
+ // done and clears undo. Undoing moves events from done to
5101
+ // undone, redoing moves them in the other direction.
5102
+ done: [], undone: [], undoDepth: Infinity,
5103
+ // Used to track when changes can be merged into a single undo
5104
+ // event
5105
+ lastTime: 0, lastOp: null, lastOrigin: null,
5106
+ // Used by the isClean() method
5107
+ generation: startGen || 1, maxGeneration: startGen || 1
5108
+ };
5109
+ }
5110
+
5111
+ function attachLocalSpans(doc, change, from, to) {
5112
+ var existing = change["spans_" + doc.id], n = 0;
5113
+ doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function(line) {
5114
+ if (line.markedSpans)
5115
+ (existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans;
5116
+ ++n;
5117
+ });
5118
+ }
5119
+
5120
+ function historyChangeFromChange(doc, change) {
5121
+ var from = { line: change.from.line, ch: change.from.ch };
5122
+ var histChange = {from: from, to: changeEnd(change), text: getBetween(doc, change.from, change.to)};
5123
+ attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);
5124
+ linkedDocs(doc, function(doc) {attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);}, true);
5125
+ return histChange;
5126
+ }
5127
+
5128
+ function addToHistory(doc, change, selAfter, opId) {
5129
+ var hist = doc.history;
5130
+ hist.undone.length = 0;
5131
+ var time = +new Date, cur = lst(hist.done);
5132
+
5133
+ if (cur &&
5134
+ (hist.lastOp == opId ||
5135
+ hist.lastOrigin == change.origin && change.origin &&
5136
+ ((change.origin.charAt(0) == "+" && doc.cm && hist.lastTime > time - doc.cm.options.historyEventDelay) ||
5137
+ change.origin.charAt(0) == "*"))) {
5138
+ // Merge this change into the last event
5139
+ var last = lst(cur.changes);
5140
+ if (posEq(change.from, change.to) && posEq(change.from, last.to)) {
5141
+ // Optimized case for simple insertion -- don't want to add
5142
+ // new changesets for every character typed
5143
+ last.to = changeEnd(change);
5144
+ } else {
5145
+ // Add new sub-event
5146
+ cur.changes.push(historyChangeFromChange(doc, change));
5147
+ }
5148
+ cur.anchorAfter = selAfter.anchor; cur.headAfter = selAfter.head;
5149
+ } else {
5150
+ // Can not be merged, start a new event.
5151
+ cur = {changes: [historyChangeFromChange(doc, change)],
5152
+ generation: hist.generation,
5153
+ anchorBefore: doc.sel.anchor, headBefore: doc.sel.head,
5154
+ anchorAfter: selAfter.anchor, headAfter: selAfter.head};
5155
+ hist.done.push(cur);
5156
+ hist.generation = ++hist.maxGeneration;
5157
+ while (hist.done.length > hist.undoDepth)
5158
+ hist.done.shift();
5159
+ }
5160
+ hist.lastTime = time;
5161
+ hist.lastOp = opId;
5162
+ hist.lastOrigin = change.origin;
5163
+ }
5164
+
5165
+ function removeClearedSpans(spans) {
5166
+ if (!spans) return null;
5167
+ for (var i = 0, out; i < spans.length; ++i) {
5168
+ if (spans[i].marker.explicitlyCleared) { if (!out) out = spans.slice(0, i); }
5169
+ else if (out) out.push(spans[i]);
5170
+ }
5171
+ return !out ? spans : out.length ? out : null;
5172
+ }
5173
+
5174
+ function getOldSpans(doc, change) {
5175
+ var found = change["spans_" + doc.id];
5176
+ if (!found) return null;
5177
+ for (var i = 0, nw = []; i < change.text.length; ++i)
5178
+ nw.push(removeClearedSpans(found[i]));
5179
+ return nw;
5180
+ }
5181
+
5182
+ // Used both to provide a JSON-safe object in .getHistory, and, when
5183
+ // detaching a document, to split the history in two
5184
+ function copyHistoryArray(events, newGroup) {
5185
+ for (var i = 0, copy = []; i < events.length; ++i) {
5186
+ var event = events[i], changes = event.changes, newChanges = [];
5187
+ copy.push({changes: newChanges, anchorBefore: event.anchorBefore, headBefore: event.headBefore,
5188
+ anchorAfter: event.anchorAfter, headAfter: event.headAfter});
5189
+ for (var j = 0; j < changes.length; ++j) {
5190
+ var change = changes[j], m;
5191
+ newChanges.push({from: change.from, to: change.to, text: change.text});
5192
+ if (newGroup) for (var prop in change) if (m = prop.match(/^spans_(\d+)$/)) {
5193
+ if (indexOf(newGroup, Number(m[1])) > -1) {
5194
+ lst(newChanges)[prop] = change[prop];
5195
+ delete change[prop];
5196
+ }
5197
+ }
5198
+ }
5199
+ }
5200
+ return copy;
5201
+ }
5202
+
5203
+ // Rebasing/resetting history to deal with externally-sourced changes
5204
+
5205
+ function rebaseHistSel(pos, from, to, diff) {
5206
+ if (to < pos.line) {
5207
+ pos.line += diff;
5208
+ } else if (from < pos.line) {
5209
+ pos.line = from;
5210
+ pos.ch = 0;
5211
+ }
5212
+ }
5213
+
5214
+ // Tries to rebase an array of history events given a change in the
5215
+ // document. If the change touches the same lines as the event, the
5216
+ // event, and everything 'behind' it, is discarded. If the change is
5217
+ // before the event, the event's positions are updated. Uses a
5218
+ // copy-on-write scheme for the positions, to avoid having to
5219
+ // reallocate them all on every rebase, but also avoid problems with
5220
+ // shared position objects being unsafely updated.
5221
+ function rebaseHistArray(array, from, to, diff) {
5222
+ for (var i = 0; i < array.length; ++i) {
5223
+ var sub = array[i], ok = true;
5224
+ for (var j = 0; j < sub.changes.length; ++j) {
5225
+ var cur = sub.changes[j];
5226
+ if (!sub.copied) { cur.from = copyPos(cur.from); cur.to = copyPos(cur.to); }
5227
+ if (to < cur.from.line) {
5228
+ cur.from.line += diff;
5229
+ cur.to.line += diff;
5230
+ } else if (from <= cur.to.line) {
5231
+ ok = false;
5232
+ break;
5233
+ }
5234
+ }
5235
+ if (!sub.copied) {
5236
+ sub.anchorBefore = copyPos(sub.anchorBefore); sub.headBefore = copyPos(sub.headBefore);
5237
+ sub.anchorAfter = copyPos(sub.anchorAfter); sub.readAfter = copyPos(sub.headAfter);
5238
+ sub.copied = true;
5239
+ }
5240
+ if (!ok) {
5241
+ array.splice(0, i + 1);
5242
+ i = 0;
5243
+ } else {
5244
+ rebaseHistSel(sub.anchorBefore); rebaseHistSel(sub.headBefore);
5245
+ rebaseHistSel(sub.anchorAfter); rebaseHistSel(sub.headAfter);
5246
+ }
5247
+ }
5248
+ }
5249
+
5250
+ function rebaseHist(hist, change) {
5251
+ var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1;
5252
+ rebaseHistArray(hist.done, from, to, diff);
5253
+ rebaseHistArray(hist.undone, from, to, diff);
5254
+ }
5255
+
5256
+ // EVENT OPERATORS
5257
+
5258
+ function stopMethod() {e_stop(this);}
5259
+ // Ensure an event has a stop method.
5260
+ function addStop(event) {
5261
+ if (!event.stop) event.stop = stopMethod;
5262
+ return event;
5263
+ }
5264
+
5265
+ function e_preventDefault(e) {
5266
+ if (e.preventDefault) e.preventDefault();
5267
+ else e.returnValue = false;
5268
+ }
5269
+ function e_stopPropagation(e) {
5270
+ if (e.stopPropagation) e.stopPropagation();
5271
+ else e.cancelBubble = true;
5272
+ }
5273
+ function e_defaultPrevented(e) {
5274
+ return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false;
5275
+ }
5276
+ function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);}
5277
+ CodeMirror.e_stop = e_stop;
5278
+ CodeMirror.e_preventDefault = e_preventDefault;
5279
+ CodeMirror.e_stopPropagation = e_stopPropagation;
5280
+
5281
+ function e_target(e) {return e.target || e.srcElement;}
5282
+ function e_button(e) {
5283
+ var b = e.which;
5284
+ if (b == null) {
5285
+ if (e.button & 1) b = 1;
5286
+ else if (e.button & 2) b = 3;
5287
+ else if (e.button & 4) b = 2;
5288
+ }
5289
+ if (mac && e.ctrlKey && b == 1) b = 3;
5290
+ return b;
5291
+ }
5292
+
5293
+ // EVENT HANDLING
5294
+
5295
+ function on(emitter, type, f) {
5296
+ if (emitter.addEventListener)
5297
+ emitter.addEventListener(type, f, false);
5298
+ else if (emitter.attachEvent)
5299
+ emitter.attachEvent("on" + type, f);
5300
+ else {
5301
+ var map = emitter._handlers || (emitter._handlers = {});
5302
+ var arr = map[type] || (map[type] = []);
5303
+ arr.push(f);
5304
+ }
5305
+ }
5306
+
5307
+ function off(emitter, type, f) {
5308
+ if (emitter.removeEventListener)
5309
+ emitter.removeEventListener(type, f, false);
5310
+ else if (emitter.detachEvent)
5311
+ emitter.detachEvent("on" + type, f);
5312
+ else {
5313
+ var arr = emitter._handlers && emitter._handlers[type];
5314
+ if (!arr) return;
5315
+ for (var i = 0; i < arr.length; ++i)
5316
+ if (arr[i] == f) { arr.splice(i, 1); break; }
5317
+ }
5318
+ }
5319
+
5320
+ function signal(emitter, type /*, values...*/) {
5321
+ var arr = emitter._handlers && emitter._handlers[type];
5322
+ if (!arr) return;
5323
+ var args = Array.prototype.slice.call(arguments, 2);
5324
+ for (var i = 0; i < arr.length; ++i) arr[i].apply(null, args);
5325
+ }
5326
+
5327
+ var delayedCallbacks, delayedCallbackDepth = 0;
5328
+ function signalLater(emitter, type /*, values...*/) {
5329
+ var arr = emitter._handlers && emitter._handlers[type];
5330
+ if (!arr) return;
5331
+ var args = Array.prototype.slice.call(arguments, 2);
5332
+ if (!delayedCallbacks) {
5333
+ ++delayedCallbackDepth;
5334
+ delayedCallbacks = [];
5335
+ setTimeout(fireDelayed, 0);
5336
+ }
5337
+ function bnd(f) {return function(){f.apply(null, args);};};
5338
+ for (var i = 0; i < arr.length; ++i)
5339
+ delayedCallbacks.push(bnd(arr[i]));
5340
+ }
5341
+
5342
+ function signalDOMEvent(cm, e, override) {
5343
+ signal(cm, override || e.type, cm, e);
5344
+ return e_defaultPrevented(e) || e.codemirrorIgnore;
5345
+ }
5346
+
5347
+ function fireDelayed() {
5348
+ --delayedCallbackDepth;
5349
+ var delayed = delayedCallbacks;
5350
+ delayedCallbacks = null;
5351
+ for (var i = 0; i < delayed.length; ++i) delayed[i]();
5352
+ }
5353
+
5354
+ function hasHandler(emitter, type) {
5355
+ var arr = emitter._handlers && emitter._handlers[type];
5356
+ return arr && arr.length > 0;
5357
+ }
5358
+
5359
+ CodeMirror.on = on; CodeMirror.off = off; CodeMirror.signal = signal;
5360
+
5361
+ function eventMixin(ctor) {
5362
+ ctor.prototype.on = function(type, f) {on(this, type, f);};
5363
+ ctor.prototype.off = function(type, f) {off(this, type, f);};
5364
+ }
5365
+
5366
+ // MISC UTILITIES
5367
+
5368
+ // Number of pixels added to scroller and sizer to hide scrollbar
5369
+ var scrollerCutOff = 30;
5370
+
5371
+ // Returned or thrown by various protocols to signal 'I'm not
5372
+ // handling this'.
5373
+ var Pass = CodeMirror.Pass = {toString: function(){return "CodeMirror.Pass";}};
5374
+
5375
+ function Delayed() {this.id = null;}
5376
+ Delayed.prototype = {set: function(ms, f) {clearTimeout(this.id); this.id = setTimeout(f, ms);}};
5377
+
5378
+ // Counts the column offset in a string, taking tabs into account.
5379
+ // Used mostly to find indentation.
5380
+ function countColumn(string, end, tabSize, startIndex, startValue) {
5381
+ if (end == null) {
5382
+ end = string.search(/[^\s\u00a0]/);
5383
+ if (end == -1) end = string.length;
5384
+ }
5385
+ for (var i = startIndex || 0, n = startValue || 0; i < end; ++i) {
5386
+ if (string.charAt(i) == "\t") n += tabSize - (n % tabSize);
5387
+ else ++n;
5388
+ }
5389
+ return n;
5390
+ }
5391
+ CodeMirror.countColumn = countColumn;
5392
+
5393
+ var spaceStrs = [""];
5394
+ function spaceStr(n) {
5395
+ while (spaceStrs.length <= n)
5396
+ spaceStrs.push(lst(spaceStrs) + " ");
5397
+ return spaceStrs[n];
5398
+ }
5399
+
5400
+ function lst(arr) { return arr[arr.length-1]; }
5401
+
5402
+ function selectInput(node) {
5403
+ if (ios) { // Mobile Safari apparently has a bug where select() is broken.
5404
+ node.selectionStart = 0;
5405
+ node.selectionEnd = node.value.length;
5406
+ } else {
5407
+ // Suppress mysterious IE10 errors
5408
+ try { node.select(); }
5409
+ catch(_e) {}
5410
+ }
5411
+ }
5412
+
5413
+ function indexOf(collection, elt) {
5414
+ if (collection.indexOf) return collection.indexOf(elt);
5415
+ for (var i = 0, e = collection.length; i < e; ++i)
5416
+ if (collection[i] == elt) return i;
5417
+ return -1;
5418
+ }
5419
+
5420
+ function createObj(base, props) {
5421
+ function Obj() {}
5422
+ Obj.prototype = base;
5423
+ var inst = new Obj();
5424
+ if (props) copyObj(props, inst);
5425
+ return inst;
5426
+ }
5427
+
5428
+ function copyObj(obj, target) {
5429
+ if (!target) target = {};
5430
+ for (var prop in obj) if (obj.hasOwnProperty(prop)) target[prop] = obj[prop];
5431
+ return target;
5432
+ }
5433
+
5434
+ function emptyArray(size) {
5435
+ for (var a = [], i = 0; i < size; ++i) a.push(undefined);
5436
+ return a;
5437
+ }
5438
+
5439
+ function bind(f) {
5440
+ var args = Array.prototype.slice.call(arguments, 1);
5441
+ return function(){return f.apply(null, args);};
5442
+ }
5443
+
5444
+ var nonASCIISingleCaseWordChar = /[\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/;
5445
+ function isWordChar(ch) {
5446
+ return /\w/.test(ch) || ch > "\x80" &&
5447
+ (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch));
5448
+ }
5449
+
5450
+ function isEmpty(obj) {
5451
+ for (var n in obj) if (obj.hasOwnProperty(n) && obj[n]) return false;
5452
+ return true;
5453
+ }
5454
+
5455
+ var isExtendingChar = /[\u0300-\u036F\u0483-\u0487\u0488-\u0489\u0591-\u05BD\u05BF\u05C1-\u05C2\u05C4-\u05C5\u05C7\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7-\u06E8\u06EA-\u06ED\uA66F\uA670-\uA672\uA674-\uA67D\uA69F\udc00-\udfff]/;
5456
+
5457
+ // DOM UTILITIES
5458
+
5459
+ function elt(tag, content, className, style) {
5460
+ var e = document.createElement(tag);
5461
+ if (className) e.className = className;
5462
+ if (style) e.style.cssText = style;
5463
+ if (typeof content == "string") setTextContent(e, content);
5464
+ else if (content) for (var i = 0; i < content.length; ++i) e.appendChild(content[i]);
5465
+ return e;
5466
+ }
5467
+
5468
+ function removeChildren(e) {
5469
+ for (var count = e.childNodes.length; count > 0; --count)
5470
+ e.removeChild(e.firstChild);
5471
+ return e;
5472
+ }
5473
+
5474
+ function removeChildrenAndAdd(parent, e) {
5475
+ return removeChildren(parent).appendChild(e);
5476
+ }
5477
+
5478
+ function setTextContent(e, str) {
5479
+ if (ie_lt9) {
5480
+ e.innerHTML = "";
5481
+ e.appendChild(document.createTextNode(str));
5482
+ } else e.textContent = str;
5483
+ }
5484
+
5485
+ function getRect(node) {
5486
+ return node.getBoundingClientRect();
5487
+ }
5488
+ CodeMirror.replaceGetRect = function(f) { getRect = f; };
5489
+
5490
+ // FEATURE DETECTION
5491
+
5492
+ // Detect drag-and-drop
5493
+ var dragAndDrop = function() {
5494
+ // There is *some* kind of drag-and-drop support in IE6-8, but I
5495
+ // couldn't get it to work yet.
5496
+ if (ie_lt9) return false;
5497
+ var div = elt('div');
5498
+ return "draggable" in div || "dragDrop" in div;
5499
+ }();
5500
+
5501
+ // For a reason I have yet to figure out, some browsers disallow
5502
+ // word wrapping between certain characters *only* if a new inline
5503
+ // element is started between them. This makes it hard to reliably
5504
+ // measure the position of things, since that requires inserting an
5505
+ // extra span. This terribly fragile set of tests matches the
5506
+ // character combinations that suffer from this phenomenon on the
5507
+ // various browsers.
5508
+ function spanAffectsWrapping() { return false; }
5509
+ if (gecko) // Only for "$'"
5510
+ spanAffectsWrapping = function(str, i) {
5511
+ return str.charCodeAt(i - 1) == 36 && str.charCodeAt(i) == 39;
5512
+ };
5513
+ else if (safari && !/Version\/([6-9]|\d\d)\b/.test(navigator.userAgent))
5514
+ spanAffectsWrapping = function(str, i) {
5515
+ return /\-[^ \-?]|\?[^ !\'\"\),.\-\/:;\?\]\}]/.test(str.slice(i - 1, i + 1));
5516
+ };
5517
+ else if (webkit && /Chrome\/(?:29|[3-9]\d|\d\d\d)\./.test(navigator.userAgent))
5518
+ spanAffectsWrapping = function(str, i) {
5519
+ var code = str.charCodeAt(i - 1);
5520
+ return code >= 8208 && code <= 8212;
5521
+ };
5522
+ else if (webkit)
5523
+ spanAffectsWrapping = function(str, i) {
5524
+ if (i > 1 && str.charCodeAt(i - 1) == 45) {
5525
+ if (/\w/.test(str.charAt(i - 2)) && /[^\-?\.]/.test(str.charAt(i))) return true;
5526
+ if (i > 2 && /[\d\.,]/.test(str.charAt(i - 2)) && /[\d\.,]/.test(str.charAt(i))) return false;
5527
+ }
5528
+ return /[~!#%&*)=+}\]\\|\"\.>,:;][({[<]|-[^\-?\.\u2010-\u201f\u2026]|\?[\w~`@#$%\^&*(_=+{[|><]|…[\w~`@#$%\^&*(_=+{[><]/.test(str.slice(i - 1, i + 1));
5529
+ };
5530
+
5531
+ var knownScrollbarWidth;
5532
+ function scrollbarWidth(measure) {
5533
+ if (knownScrollbarWidth != null) return knownScrollbarWidth;
5534
+ var test = elt("div", null, null, "width: 50px; height: 50px; overflow-x: scroll");
5535
+ removeChildrenAndAdd(measure, test);
5536
+ if (test.offsetWidth)
5537
+ knownScrollbarWidth = test.offsetHeight - test.clientHeight;
5538
+ return knownScrollbarWidth || 0;
5539
+ }
5540
+
5541
+ var zwspSupported;
5542
+ function zeroWidthElement(measure) {
5543
+ if (zwspSupported == null) {
5544
+ var test = elt("span", "\u200b");
5545
+ removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")]));
5546
+ if (measure.firstChild.offsetHeight != 0)
5547
+ zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !ie_lt8;
5548
+ }
5549
+ if (zwspSupported) return elt("span", "\u200b");
5550
+ else return elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px");
5551
+ }
5552
+
5553
+ // See if "".split is the broken IE version, if so, provide an
5554
+ // alternative way to split lines.
5555
+ var splitLines = "\n\nb".split(/\n/).length != 3 ? function(string) {
5556
+ var pos = 0, result = [], l = string.length;
5557
+ while (pos <= l) {
5558
+ var nl = string.indexOf("\n", pos);
5559
+ if (nl == -1) nl = string.length;
5560
+ var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl);
5561
+ var rt = line.indexOf("\r");
5562
+ if (rt != -1) {
5563
+ result.push(line.slice(0, rt));
5564
+ pos += rt + 1;
5565
+ } else {
5566
+ result.push(line);
5567
+ pos = nl + 1;
5568
+ }
5569
+ }
5570
+ return result;
5571
+ } : function(string){return string.split(/\r\n?|\n/);};
5572
+ CodeMirror.splitLines = splitLines;
5573
+
5574
+ var hasSelection = window.getSelection ? function(te) {
5575
+ try { return te.selectionStart != te.selectionEnd; }
5576
+ catch(e) { return false; }
5577
+ } : function(te) {
5578
+ try {var range = te.ownerDocument.selection.createRange();}
5579
+ catch(e) {}
5580
+ if (!range || range.parentElement() != te) return false;
5581
+ return range.compareEndPoints("StartToEnd", range) != 0;
5582
+ };
5583
+
5584
+ var hasCopyEvent = (function() {
5585
+ var e = elt("div");
5586
+ if ("oncopy" in e) return true;
5587
+ e.setAttribute("oncopy", "return;");
5588
+ return typeof e.oncopy == 'function';
5589
+ })();
5590
+
5591
+ // KEY NAMING
5592
+
5593
+ var keyNames = {3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
5594
+ 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
5595
+ 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
5596
+ 46: "Delete", 59: ";", 91: "Mod", 92: "Mod", 93: "Mod", 109: "-", 107: "=", 127: "Delete",
5597
+ 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\",
5598
+ 221: "]", 222: "'", 63276: "PageUp", 63277: "PageDown", 63275: "End", 63273: "Home",
5599
+ 63234: "Left", 63232: "Up", 63235: "Right", 63233: "Down", 63302: "Insert", 63272: "Delete"};
5600
+ CodeMirror.keyNames = keyNames;
5601
+ (function() {
5602
+ // Number keys
5603
+ for (var i = 0; i < 10; i++) keyNames[i + 48] = String(i);
5604
+ // Alphabetic keys
5605
+ for (var i = 65; i <= 90; i++) keyNames[i] = String.fromCharCode(i);
5606
+ // Function keys
5607
+ for (var i = 1; i <= 12; i++) keyNames[i + 111] = keyNames[i + 63235] = "F" + i;
5608
+ })();
5609
+
5610
+ // BIDI HELPERS
5611
+
5612
+ function iterateBidiSections(order, from, to, f) {
5613
+ if (!order) return f(from, to, "ltr");
5614
+ var found = false;
5615
+ for (var i = 0; i < order.length; ++i) {
5616
+ var part = order[i];
5617
+ if (part.from < to && part.to > from || from == to && part.to == from) {
5618
+ f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr");
5619
+ found = true;
5620
+ }
5621
+ }
5622
+ if (!found) f(from, to, "ltr");
5623
+ }
5624
+
5625
+ function bidiLeft(part) { return part.level % 2 ? part.to : part.from; }
5626
+ function bidiRight(part) { return part.level % 2 ? part.from : part.to; }
5627
+
5628
+ function lineLeft(line) { var order = getOrder(line); return order ? bidiLeft(order[0]) : 0; }
5629
+ function lineRight(line) {
5630
+ var order = getOrder(line);
5631
+ if (!order) return line.text.length;
5632
+ return bidiRight(lst(order));
5633
+ }
5634
+
5635
+ function lineStart(cm, lineN) {
5636
+ var line = getLine(cm.doc, lineN);
5637
+ var visual = visualLine(cm.doc, line);
5638
+ if (visual != line) lineN = lineNo(visual);
5639
+ var order = getOrder(visual);
5640
+ var ch = !order ? 0 : order[0].level % 2 ? lineRight(visual) : lineLeft(visual);
5641
+ return Pos(lineN, ch);
5642
+ }
5643
+ function lineEnd(cm, lineN) {
5644
+ var merged, line;
5645
+ while (merged = collapsedSpanAtEnd(line = getLine(cm.doc, lineN)))
5646
+ lineN = merged.find().to.line;
5647
+ var order = getOrder(line);
5648
+ var ch = !order ? line.text.length : order[0].level % 2 ? lineLeft(line) : lineRight(line);
5649
+ return Pos(lineN, ch);
5650
+ }
5651
+
5652
+ function compareBidiLevel(order, a, b) {
5653
+ var linedir = order[0].level;
5654
+ if (a == linedir) return true;
5655
+ if (b == linedir) return false;
5656
+ return a < b;
5657
+ }
5658
+ var bidiOther;
5659
+ function getBidiPartAt(order, pos) {
5660
+ for (var i = 0, found; i < order.length; ++i) {
5661
+ var cur = order[i];
5662
+ if (cur.from < pos && cur.to > pos) { bidiOther = null; return i; }
5663
+ if (cur.from == pos || cur.to == pos) {
5664
+ if (found == null) {
5665
+ found = i;
5666
+ } else if (compareBidiLevel(order, cur.level, order[found].level)) {
5667
+ bidiOther = found;
5668
+ return i;
5669
+ } else {
5670
+ bidiOther = i;
5671
+ return found;
5672
+ }
5673
+ }
5674
+ }
5675
+ bidiOther = null;
5676
+ return found;
5677
+ }
5678
+
5679
+ function moveInLine(line, pos, dir, byUnit) {
5680
+ if (!byUnit) return pos + dir;
5681
+ do pos += dir;
5682
+ while (pos > 0 && isExtendingChar.test(line.text.charAt(pos)));
5683
+ return pos;
5684
+ }
5685
+
5686
+ // This is somewhat involved. It is needed in order to move
5687
+ // 'visually' through bi-directional text -- i.e., pressing left
5688
+ // should make the cursor go left, even when in RTL text. The
5689
+ // tricky part is the 'jumps', where RTL and LTR text touch each
5690
+ // other. This often requires the cursor offset to move more than
5691
+ // one unit, in order to visually move one unit.
5692
+ function moveVisually(line, start, dir, byUnit) {
5693
+ var bidi = getOrder(line);
5694
+ if (!bidi) return moveLogically(line, start, dir, byUnit);
5695
+ var pos = getBidiPartAt(bidi, start), part = bidi[pos];
5696
+ var target = moveInLine(line, start, part.level % 2 ? -dir : dir, byUnit);
5697
+
5698
+ for (;;) {
5699
+ if (target > part.from && target < part.to) return target;
5700
+ if (target == part.from || target == part.to) {
5701
+ if (getBidiPartAt(bidi, target) == pos) return target;
5702
+ part = bidi[pos += dir];
5703
+ return (dir > 0) == part.level % 2 ? part.to : part.from;
5704
+ } else {
5705
+ part = bidi[pos += dir];
5706
+ if (!part) return null;
5707
+ if ((dir > 0) == part.level % 2)
5708
+ target = moveInLine(line, part.to, -1, byUnit);
5709
+ else
5710
+ target = moveInLine(line, part.from, 1, byUnit);
5711
+ }
5712
+ }
5713
+ }
5714
+
5715
+ function moveLogically(line, start, dir, byUnit) {
5716
+ var target = start + dir;
5717
+ if (byUnit) while (target > 0 && isExtendingChar.test(line.text.charAt(target))) target += dir;
5718
+ return target < 0 || target > line.text.length ? null : target;
5719
+ }
5720
+
5721
+ // Bidirectional ordering algorithm
5722
+ // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm
5723
+ // that this (partially) implements.
5724
+
5725
+ // One-char codes used for character types:
5726
+ // L (L): Left-to-Right
5727
+ // R (R): Right-to-Left
5728
+ // r (AL): Right-to-Left Arabic
5729
+ // 1 (EN): European Number
5730
+ // + (ES): European Number Separator
5731
+ // % (ET): European Number Terminator
5732
+ // n (AN): Arabic Number
5733
+ // , (CS): Common Number Separator
5734
+ // m (NSM): Non-Spacing Mark
5735
+ // b (BN): Boundary Neutral
5736
+ // s (B): Paragraph Separator
5737
+ // t (S): Segment Separator
5738
+ // w (WS): Whitespace
5739
+ // N (ON): Other Neutrals
5740
+
5741
+ // Returns null if characters are ordered as they appear
5742
+ // (left-to-right), or an array of sections ({from, to, level}
5743
+ // objects) in the order in which they occur visually.
5744
+ var bidiOrdering = (function() {
5745
+ // Character types for codepoints 0 to 0xff
5746
+ var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLL";
5747
+ // Character types for codepoints 0x600 to 0x6ff
5748
+ var arabicTypes = "rrrrrrrrrrrr,rNNmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmrrrrrrrnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmNmmmmrrrrrrrrrrrrrrrrrr";
5749
+ function charType(code) {
5750
+ if (code <= 0xff) return lowTypes.charAt(code);
5751
+ else if (0x590 <= code && code <= 0x5f4) return "R";
5752
+ else if (0x600 <= code && code <= 0x6ff) return arabicTypes.charAt(code - 0x600);
5753
+ else if (0x700 <= code && code <= 0x8ac) return "r";
5754
+ else return "L";
5755
+ }
5756
+
5757
+ var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;
5758
+ var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/;
5759
+ // Browsers seem to always treat the boundaries of block elements as being L.
5760
+ var outerType = "L";
5761
+
5762
+ return function(str) {
5763
+ if (!bidiRE.test(str)) return false;
5764
+ var len = str.length, types = [];
5765
+ for (var i = 0, type; i < len; ++i)
5766
+ types.push(type = charType(str.charCodeAt(i)));
5767
+
5768
+ // W1. Examine each non-spacing mark (NSM) in the level run, and
5769
+ // change the type of the NSM to the type of the previous
5770
+ // character. If the NSM is at the start of the level run, it will
5771
+ // get the type of sor.
5772
+ for (var i = 0, prev = outerType; i < len; ++i) {
5773
+ var type = types[i];
5774
+ if (type == "m") types[i] = prev;
5775
+ else prev = type;
5776
+ }
5777
+
5778
+ // W2. Search backwards from each instance of a European number
5779
+ // until the first strong type (R, L, AL, or sor) is found. If an
5780
+ // AL is found, change the type of the European number to Arabic
5781
+ // number.
5782
+ // W3. Change all ALs to R.
5783
+ for (var i = 0, cur = outerType; i < len; ++i) {
5784
+ var type = types[i];
5785
+ if (type == "1" && cur == "r") types[i] = "n";
5786
+ else if (isStrong.test(type)) { cur = type; if (type == "r") types[i] = "R"; }
5787
+ }
5788
+
5789
+ // W4. A single European separator between two European numbers
5790
+ // changes to a European number. A single common separator between
5791
+ // two numbers of the same type changes to that type.
5792
+ for (var i = 1, prev = types[0]; i < len - 1; ++i) {
5793
+ var type = types[i];
5794
+ if (type == "+" && prev == "1" && types[i+1] == "1") types[i] = "1";
5795
+ else if (type == "," && prev == types[i+1] &&
5796
+ (prev == "1" || prev == "n")) types[i] = prev;
5797
+ prev = type;
5798
+ }
5799
+
5800
+ // W5. A sequence of European terminators adjacent to European
5801
+ // numbers changes to all European numbers.
5802
+ // W6. Otherwise, separators and terminators change to Other
5803
+ // Neutral.
5804
+ for (var i = 0; i < len; ++i) {
5805
+ var type = types[i];
5806
+ if (type == ",") types[i] = "N";
5807
+ else if (type == "%") {
5808
+ for (var end = i + 1; end < len && types[end] == "%"; ++end) {}
5809
+ var replace = (i && types[i-1] == "!") || (end < len - 1 && types[end] == "1") ? "1" : "N";
5810
+ for (var j = i; j < end; ++j) types[j] = replace;
5811
+ i = end - 1;
5812
+ }
5813
+ }
5814
+
5815
+ // W7. Search backwards from each instance of a European number
5816
+ // until the first strong type (R, L, or sor) is found. If an L is
5817
+ // found, then change the type of the European number to L.
5818
+ for (var i = 0, cur = outerType; i < len; ++i) {
5819
+ var type = types[i];
5820
+ if (cur == "L" && type == "1") types[i] = "L";
5821
+ else if (isStrong.test(type)) cur = type;
5822
+ }
5823
+
5824
+ // N1. A sequence of neutrals takes the direction of the
5825
+ // surrounding strong text if the text on both sides has the same
5826
+ // direction. European and Arabic numbers act as if they were R in
5827
+ // terms of their influence on neutrals. Start-of-level-run (sor)
5828
+ // and end-of-level-run (eor) are used at level run boundaries.
5829
+ // N2. Any remaining neutrals take the embedding direction.
5830
+ for (var i = 0; i < len; ++i) {
5831
+ if (isNeutral.test(types[i])) {
5832
+ for (var end = i + 1; end < len && isNeutral.test(types[end]); ++end) {}
5833
+ var before = (i ? types[i-1] : outerType) == "L";
5834
+ var after = (end < len - 1 ? types[end] : outerType) == "L";
5835
+ var replace = before || after ? "L" : "R";
5836
+ for (var j = i; j < end; ++j) types[j] = replace;
5837
+ i = end - 1;
5838
+ }
5839
+ }
5840
+
5841
+ // Here we depart from the documented algorithm, in order to avoid
5842
+ // building up an actual levels array. Since there are only three
5843
+ // levels (0, 1, 2) in an implementation that doesn't take
5844
+ // explicit embedding into account, we can build up the order on
5845
+ // the fly, without following the level-based algorithm.
5846
+ var order = [], m;
5847
+ for (var i = 0; i < len;) {
5848
+ if (countsAsLeft.test(types[i])) {
5849
+ var start = i;
5850
+ for (++i; i < len && countsAsLeft.test(types[i]); ++i) {}
5851
+ order.push({from: start, to: i, level: 0});
5852
+ } else {
5853
+ var pos = i, at = order.length;
5854
+ for (++i; i < len && types[i] != "L"; ++i) {}
5855
+ for (var j = pos; j < i;) {
5856
+ if (countsAsNum.test(types[j])) {
5857
+ if (pos < j) order.splice(at, 0, {from: pos, to: j, level: 1});
5858
+ var nstart = j;
5859
+ for (++j; j < i && countsAsNum.test(types[j]); ++j) {}
5860
+ order.splice(at, 0, {from: nstart, to: j, level: 2});
5861
+ pos = j;
5862
+ } else ++j;
5863
+ }
5864
+ if (pos < i) order.splice(at, 0, {from: pos, to: i, level: 1});
5865
+ }
5866
+ }
5867
+ if (order[0].level == 1 && (m = str.match(/^\s+/))) {
5868
+ order[0].from = m[0].length;
5869
+ order.unshift({from: 0, to: m[0].length, level: 0});
5870
+ }
5871
+ if (lst(order).level == 1 && (m = str.match(/\s+$/))) {
5872
+ lst(order).to -= m[0].length;
5873
+ order.push({from: len - m[0].length, to: len, level: 0});
5874
+ }
5875
+ if (order[0].level != lst(order).level)
5876
+ order.push({from: len, to: len, level: order[0].level});
5877
+
5878
+ return order;
5879
+ };
5880
+ })();
5881
+
5882
+ // THE END
5883
+
5884
+ CodeMirror.version = "3.18.0";
5885
+
5886
+ return CodeMirror;
5887
+ })();
assets/js/css.js ADDED
@@ -0,0 +1,627 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ CodeMirror.defineMode("css", function(config, parserConfig) {
2
+ "use strict";
3
+
4
+ if (!parserConfig.propertyKeywords) parserConfig = CodeMirror.resolveMode("text/css");
5
+
6
+ var indentUnit = config.indentUnit,
7
+ hooks = parserConfig.hooks || {},
8
+ atMediaTypes = parserConfig.atMediaTypes || {},
9
+ atMediaFeatures = parserConfig.atMediaFeatures || {},
10
+ propertyKeywords = parserConfig.propertyKeywords || {},
11
+ colorKeywords = parserConfig.colorKeywords || {},
12
+ valueKeywords = parserConfig.valueKeywords || {},
13
+ allowNested = !!parserConfig.allowNested,
14
+ type = null;
15
+
16
+ function ret(style, tp) { type = tp; return style; }
17
+
18
+ function tokenBase(stream, state) {
19
+ var ch = stream.next();
20
+ if (hooks[ch]) {
21
+ // result[0] is style and result[1] is type
22
+ var result = hooks[ch](stream, state);
23
+ if (result !== false) return result;
24
+ }
25
+ if (ch == "@") {stream.eatWhile(/[\w\\\-]/); return ret("def", stream.current());}
26
+ else if (ch == "=") ret(null, "compare");
27
+ else if ((ch == "~" || ch == "|") && stream.eat("=")) return ret(null, "compare");
28
+ else if (ch == "\"" || ch == "'") {
29
+ state.tokenize = tokenString(ch);
30
+ return state.tokenize(stream, state);
31
+ }
32
+ else if (ch == "#") {
33
+ stream.eatWhile(/[\w\\\-]/);
34
+ return ret("atom", "hash");
35
+ }
36
+ else if (ch == "!") {
37
+ stream.match(/^\s*\w*/);
38
+ return ret("keyword", "important");
39
+ }
40
+ else if (/\d/.test(ch) || ch == "." && stream.eat(/\d/)) {
41
+ stream.eatWhile(/[\w.%]/);
42
+ return ret("number", "unit");
43
+ }
44
+ else if (ch === "-") {
45
+ if (/\d/.test(stream.peek())) {
46
+ stream.eatWhile(/[\w.%]/);
47
+ return ret("number", "unit");
48
+ } else if (stream.match(/^[^-]+-/)) {
49
+ return ret("meta", "meta");
50
+ }
51
+ }
52
+ else if (/[,+>*\/]/.test(ch)) {
53
+ return ret(null, "select-op");
54
+ }
55
+ else if (ch == "." && stream.match(/^-?[_a-z][_a-z0-9-]*/i)) {
56
+ return ret("qualifier", "qualifier");
57
+ }
58
+ else if (ch == ":") {
59
+ return ret("operator", ch);
60
+ }
61
+ else if (/[;{}\[\]\(\)]/.test(ch)) {
62
+ return ret(null, ch);
63
+ }
64
+ else if (ch == "u" && stream.match("rl(")) {
65
+ stream.backUp(1);
66
+ state.tokenize = tokenParenthesized;
67
+ return ret("property", "variable");
68
+ }
69
+ else {
70
+ stream.eatWhile(/[\w\\\-]/);
71
+ return ret("property", "variable");
72
+ }
73
+ }
74
+
75
+ function tokenString(quote, nonInclusive) {
76
+ return function(stream, state) {
77
+ var escaped = false, ch;
78
+ while ((ch = stream.next()) != null) {
79
+ if (ch == quote && !escaped)
80
+ break;
81
+ escaped = !escaped && ch == "\\";
82
+ }
83
+ if (!escaped) {
84
+ if (nonInclusive) stream.backUp(1);
85
+ state.tokenize = tokenBase;
86
+ }
87
+ return ret("string", "string");
88
+ };
89
+ }
90
+
91
+ function tokenParenthesized(stream, state) {
92
+ stream.next(); // Must be '('
93
+ if (!stream.match(/\s*[\"\']/, false))
94
+ state.tokenize = tokenString(")", true);
95
+ else
96
+ state.tokenize = tokenBase;
97
+ return ret(null, "(");
98
+ }
99
+
100
+ return {
101
+ startState: function(base) {
102
+ return {tokenize: tokenBase,
103
+ baseIndent: base || 0,
104
+ stack: [],
105
+ lastToken: null};
106
+ },
107
+
108
+ token: function(stream, state) {
109
+
110
+ // Use these terms when applicable (see http://www.xanthir.com/blog/b4E50)
111
+ //
112
+ // rule** or **ruleset:
113
+ // A selector + braces combo, or an at-rule.
114
+ //
115
+ // declaration block:
116
+ // A sequence of declarations.
117
+ //
118
+ // declaration:
119
+ // A property + colon + value combo.
120
+ //
121
+ // property value:
122
+ // The entire value of a property.
123
+ //
124
+ // component value:
125
+ // A single piece of a property value. Like the 5px in
126
+ // text-shadow: 0 0 5px blue;. Can also refer to things that are
127
+ // multiple terms, like the 1-4 terms that make up the background-size
128
+ // portion of the background shorthand.
129
+ //
130
+ // term:
131
+ // The basic unit of author-facing CSS, like a single number (5),
132
+ // dimension (5px), string ("foo"), or function. Officially defined
133
+ // by the CSS 2.1 grammar (look for the 'term' production)
134
+ //
135
+ //
136
+ // simple selector:
137
+ // A single atomic selector, like a type selector, an attr selector, a
138
+ // class selector, etc.
139
+ //
140
+ // compound selector:
141
+ // One or more simple selectors without a combinator. div.example is
142
+ // compound, div > .example is not.
143
+ //
144
+ // complex selector:
145
+ // One or more compound selectors chained with combinators.
146
+ //
147
+ // combinator:
148
+ // The parts of selectors that express relationships. There are four
149
+ // currently - the space (descendant combinator), the greater-than
150
+ // bracket (child combinator), the plus sign (next sibling combinator),
151
+ // and the tilda (following sibling combinator).
152
+ //
153
+ // sequence of selectors:
154
+ // One or more of the named type of selector chained with commas.
155
+
156
+ state.tokenize = state.tokenize || tokenBase;
157
+ if (state.tokenize == tokenBase && stream.eatSpace()) return null;
158
+ var style = state.tokenize(stream, state);
159
+ if (style && typeof style != "string") style = ret(style[0], style[1]);
160
+
161
+ // Changing style returned based on context
162
+ var context = state.stack[state.stack.length-1];
163
+ if (style == "variable") {
164
+ if (type == "variable-definition") state.stack.push("propertyValue");
165
+ return state.lastToken = "variable-2";
166
+ } else if (style == "property") {
167
+ var word = stream.current().toLowerCase();
168
+ if (context == "propertyValue") {
169
+ if (valueKeywords.hasOwnProperty(word)) {
170
+ style = "string-2";
171
+ } else if (colorKeywords.hasOwnProperty(word)) {
172
+ style = "keyword";
173
+ } else {
174
+ style = "variable-2";
175
+ }
176
+ } else if (context == "rule") {
177
+ if (!propertyKeywords.hasOwnProperty(word)) {
178
+ style += " error";
179
+ }
180
+ } else if (context == "block") {
181
+ // if a value is present in both property, value, or color, the order
182
+ // of preference is property -> color -> value
183
+ if (propertyKeywords.hasOwnProperty(word)) {
184
+ style = "property";
185
+ } else if (colorKeywords.hasOwnProperty(word)) {
186
+ style = "keyword";
187
+ } else if (valueKeywords.hasOwnProperty(word)) {
188
+ style = "string-2";
189
+ } else {
190
+ style = "tag";
191
+ }
192
+ } else if (!context || context == "@media{") {
193
+ style = "tag";
194
+ } else if (context == "@media") {
195
+ if (atMediaTypes[stream.current()]) {
196
+ style = "attribute"; // Known attribute
197
+ } else if (/^(only|not)$/.test(word)) {
198
+ style = "keyword";
199
+ } else if (word == "and") {
200
+ style = "error"; // "and" is only allowed in @mediaType
201
+ } else if (atMediaFeatures.hasOwnProperty(word)) {
202
+ style = "error"; // Known property, should be in @mediaType(
203
+ } else {
204
+ // Unknown, expecting keyword or attribute, assuming attribute
205
+ style = "attribute error";
206
+ }
207
+ } else if (context == "@mediaType") {
208
+ if (atMediaTypes.hasOwnProperty(word)) {
209
+ style = "attribute";
210
+ } else if (word == "and") {
211
+ style = "operator";
212
+ } else if (/^(only|not)$/.test(word)) {
213
+ style = "error"; // Only allowed in @media
214
+ } else {
215
+ // Unknown attribute or property, but expecting property (preceded
216
+ // by "and"). Should be in parentheses
217
+ style = "error";
218
+ }
219
+ } else if (context == "@mediaType(") {
220
+ if (propertyKeywords.hasOwnProperty(word)) {
221
+ // do nothing, remains "property"
222
+ } else if (atMediaTypes.hasOwnProperty(word)) {
223
+ style = "error"; // Known property, should be in parentheses
224
+ } else if (word == "and") {
225
+ style = "operator";
226
+ } else if (/^(only|not)$/.test(word)) {
227
+ style = "error"; // Only allowed in @media
228
+ } else {
229
+ style += " error";
230
+ }
231
+ } else if (context == "@import") {
232
+ style = "tag";
233
+ } else {
234
+ style = "error";
235
+ }
236
+ } else if (style == "atom") {
237
+ if(!context || context == "@media{" || context == "block") {
238
+ style = "builtin";
239
+ } else if (context == "propertyValue") {
240
+ if (!/^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/.test(stream.current())) {
241
+ style += " error";
242
+ }
243
+ } else {
244
+ style = "error";
245
+ }
246
+ } else if (context == "@media" && type == "{") {
247
+ style = "error";
248
+ }
249
+
250
+ // Push/pop context stack
251
+ if (type == "{") {
252
+ if (context == "@media" || context == "@mediaType") {
253
+ state.stack[state.stack.length-1] = "@media{";
254
+ }
255
+ else {
256
+ var newContext = allowNested ? "block" : "rule";
257
+ state.stack.push(newContext);
258
+ }
259
+ }
260
+ else if (type == "}") {
261
+ if (context == "interpolation") style = "operator";
262
+ state.stack.pop();
263
+ if (context == "propertyValue") state.stack.pop();
264
+ }
265
+ else if (type == "interpolation") state.stack.push("interpolation");
266
+ else if (type == "@media") state.stack.push("@media");
267
+ else if (type == "@import") state.stack.push("@import");
268
+ else if (context == "@media" && /\b(keyword|attribute)\b/.test(style))
269
+ state.stack[state.stack.length-1] = "@mediaType";
270
+ else if (context == "@mediaType" && stream.current() == ",")
271
+ state.stack[state.stack.length-1] = "@media";
272
+ else if (type == "(") {
273
+ if (context == "@media" || context == "@mediaType") {
274
+ // Make sure @mediaType is used to avoid error on {
275
+ state.stack[state.stack.length-1] = "@mediaType";
276
+ state.stack.push("@mediaType(");
277
+ }
278
+ else state.stack.push("(");
279
+ }
280
+ else if (type == ")") {
281
+ if (context == "propertyValue") {
282
+ // In @mediaType( without closing ; after propertyValue
283
+ state.stack.pop();
284
+ }
285
+ state.stack.pop();
286
+ }
287
+ else if (type == ":" && state.lastToken == "property") state.stack.push("propertyValue");
288
+ else if (context == "propertyValue" && type == ";") state.stack.pop();
289
+ else if (context == "@import" && type == ";") state.stack.pop();
290
+
291
+ return state.lastToken = style;
292
+ },
293
+
294
+ indent: function(state, textAfter) {
295
+ var n = state.stack.length;
296
+ if (/^\}/.test(textAfter))
297
+ n -= state.stack[n-1] == "propertyValue" ? 2 : 1;
298
+ return state.baseIndent + n * indentUnit;
299
+ },
300
+
301
+ electricChars: "}",
302
+ blockCommentStart: "/*",
303
+ blockCommentEnd: "*/",
304
+ fold: "brace"
305
+ };
306
+ });
307
+
308
+ (function() {
309
+ function keySet(array) {
310
+ var keys = {};
311
+ for (var i = 0; i < array.length; ++i) {
312
+ keys[array[i]] = true;
313
+ }
314
+ return keys;
315
+ }
316
+
317
+ var atMediaTypes = keySet([
318
+ "all", "aural", "braille", "handheld", "print", "projection", "screen",
319
+ "tty", "tv", "embossed"
320
+ ]);
321
+
322
+ var atMediaFeatures = keySet([
323
+ "width", "min-width", "max-width", "height", "min-height", "max-height",
324
+ "device-width", "min-device-width", "max-device-width", "device-height",
325
+ "min-device-height", "max-device-height", "aspect-ratio",
326
+ "min-aspect-ratio", "max-aspect-ratio", "device-aspect-ratio",
327
+ "min-device-aspect-ratio", "max-device-aspect-ratio", "color", "min-color",
328
+ "max-color", "color-index", "min-color-index", "max-color-index",
329
+ "monochrome", "min-monochrome", "max-monochrome", "resolution",
330
+ "min-resolution", "max-resolution", "scan", "grid"
331
+ ]);
332
+
333
+ var propertyKeywords = keySet([
334
+ "align-content", "align-items", "align-self", "alignment-adjust",
335
+ "alignment-baseline", "anchor-point", "animation", "animation-delay",
336
+ "animation-direction", "animation-duration", "animation-iteration-count",
337
+ "animation-name", "animation-play-state", "animation-timing-function",
338
+ "appearance", "azimuth", "backface-visibility", "background",
339
+ "background-attachment", "background-clip", "background-color",
340
+ "background-image", "background-origin", "background-position",
341
+ "background-repeat", "background-size", "baseline-shift", "binding",
342
+ "bleed", "bookmark-label", "bookmark-level", "bookmark-state",
343
+ "bookmark-target", "border", "border-bottom", "border-bottom-color",
344
+ "border-bottom-left-radius", "border-bottom-right-radius",
345
+ "border-bottom-style", "border-bottom-width", "border-collapse",
346
+ "border-color", "border-image", "border-image-outset",
347
+ "border-image-repeat", "border-image-slice", "border-image-source",
348
+ "border-image-width", "border-left", "border-left-color",
349
+ "border-left-style", "border-left-width", "border-radius", "border-right",
350
+ "border-right-color", "border-right-style", "border-right-width",
351
+ "border-spacing", "border-style", "border-top", "border-top-color",
352
+ "border-top-left-radius", "border-top-right-radius", "border-top-style",
353
+ "border-top-width", "border-width", "bottom", "box-decoration-break",
354
+ "box-shadow", "box-sizing", "break-after", "break-before", "break-inside",
355
+ "caption-side", "clear", "clip", "color", "color-profile", "column-count",
356
+ "column-fill", "column-gap", "column-rule", "column-rule-color",
357
+ "column-rule-style", "column-rule-width", "column-span", "column-width",
358
+ "columns", "content", "counter-increment", "counter-reset", "crop", "cue",
359
+ "cue-after", "cue-before", "cursor", "direction", "display",
360
+ "dominant-baseline", "drop-initial-after-adjust",
361
+ "drop-initial-after-align", "drop-initial-before-adjust",
362
+ "drop-initial-before-align", "drop-initial-size", "drop-initial-value",
363
+ "elevation", "empty-cells", "fit", "fit-position", "flex", "flex-basis",
364
+ "flex-direction", "flex-flow", "flex-grow", "flex-shrink", "flex-wrap",
365
+ "float", "float-offset", "flow-from", "flow-into", "font", "font-feature-settings",
366
+ "font-family", "font-kerning", "font-language-override", "font-size", "font-size-adjust",
367
+ "font-stretch", "font-style", "font-synthesis", "font-variant",
368
+ "font-variant-alternates", "font-variant-caps", "font-variant-east-asian",
369
+ "font-variant-ligatures", "font-variant-numeric", "font-variant-position",
370
+ "font-weight", "grid-cell", "grid-column", "grid-column-align",
371
+ "grid-column-sizing", "grid-column-span", "grid-columns", "grid-flow",
372
+ "grid-row", "grid-row-align", "grid-row-sizing", "grid-row-span",
373
+ "grid-rows", "grid-template", "hanging-punctuation", "height", "hyphens",
374
+ "icon", "image-orientation", "image-rendering", "image-resolution",
375
+ "inline-box-align", "justify-content", "left", "letter-spacing",
376
+ "line-break", "line-height", "line-stacking", "line-stacking-ruby",
377
+ "line-stacking-shift", "line-stacking-strategy", "list-style",
378
+ "list-style-image", "list-style-position", "list-style-type", "margin",
379
+ "margin-bottom", "margin-left", "margin-right", "margin-top",
380
+ "marker-offset", "marks", "marquee-direction", "marquee-loop",
381
+ "marquee-play-count", "marquee-speed", "marquee-style", "max-height",
382
+ "max-width", "min-height", "min-width", "move-to", "nav-down", "nav-index",
383
+ "nav-left", "nav-right", "nav-up", "opacity", "order", "orphans", "outline",
384
+ "outline-color", "outline-offset", "outline-style", "outline-width",
385
+ "overflow", "overflow-style", "overflow-wrap", "overflow-x", "overflow-y",
386
+ "padding", "padding-bottom", "padding-left", "padding-right", "padding-top",
387
+ "page", "page-break-after", "page-break-before", "page-break-inside",
388
+ "page-policy", "pause", "pause-after", "pause-before", "perspective",
389
+ "perspective-origin", "pitch", "pitch-range", "play-during", "position",
390
+ "presentation-level", "punctuation-trim", "quotes", "region-break-after",
391
+ "region-break-before", "region-break-inside", "region-fragment",
392
+ "rendering-intent", "resize", "rest", "rest-after", "rest-before", "richness",
393
+ "right", "rotation", "rotation-point", "ruby-align", "ruby-overhang",
394
+ "ruby-position", "ruby-span", "shape-inside", "shape-outside", "size",
395
+ "speak", "speak-as", "speak-header",
396
+ "speak-numeral", "speak-punctuation", "speech-rate", "stress", "string-set",
397
+ "tab-size", "table-layout", "target", "target-name", "target-new",
398
+ "target-position", "text-align", "text-align-last", "text-decoration",
399
+ "text-decoration-color", "text-decoration-line", "text-decoration-skip",
400
+ "text-decoration-style", "text-emphasis", "text-emphasis-color",
401
+ "text-emphasis-position", "text-emphasis-style", "text-height",
402
+ "text-indent", "text-justify", "text-outline", "text-overflow", "text-shadow",
403
+ "text-size-adjust", "text-space-collapse", "text-transform", "text-underline-position",
404
+ "text-wrap", "top", "transform", "transform-origin", "transform-style",
405
+ "transition", "transition-delay", "transition-duration",
406
+ "transition-property", "transition-timing-function", "unicode-bidi",
407
+ "vertical-align", "visibility", "voice-balance", "voice-duration",
408
+ "voice-family", "voice-pitch", "voice-range", "voice-rate", "voice-stress",
409
+ "voice-volume", "volume", "white-space", "widows", "width", "word-break",
410
+ "word-spacing", "word-wrap", "z-index", "zoom",
411
+ // SVG-specific
412
+ "clip-path", "clip-rule", "mask", "enable-background", "filter", "flood-color",
413
+ "flood-opacity", "lighting-color", "stop-color", "stop-opacity", "pointer-events",
414
+ "color-interpolation", "color-interpolation-filters", "color-profile",
415
+ "color-rendering", "fill", "fill-opacity", "fill-rule", "image-rendering",
416
+ "marker", "marker-end", "marker-mid", "marker-start", "shape-rendering", "stroke",
417
+ "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin",
418
+ "stroke-miterlimit", "stroke-opacity", "stroke-width", "text-rendering",
419
+ "baseline-shift", "dominant-baseline", "glyph-orientation-horizontal",
420
+ "glyph-orientation-vertical", "kerning", "text-anchor", "writing-mode"
421
+ ]);
422
+
423
+ var colorKeywords = keySet([
424
+ "aliceblue", "antiquewhite", "aqua", "aquamarine", "azure", "beige",
425
+ "bisque", "black", "blanchedalmond", "blue", "blueviolet", "brown",
426
+ "burlywood", "cadetblue", "chartreuse", "chocolate", "coral", "cornflowerblue",
427
+ "cornsilk", "crimson", "cyan", "darkblue", "darkcyan", "darkgoldenrod",
428
+ "darkgray", "darkgreen", "darkkhaki", "darkmagenta", "darkolivegreen",
429
+ "darkorange", "darkorchid", "darkred", "darksalmon", "darkseagreen",
430
+ "darkslateblue", "darkslategray", "darkturquoise", "darkviolet",
431
+ "deeppink", "deepskyblue", "dimgray", "dodgerblue", "firebrick",
432
+ "floralwhite", "forestgreen", "fuchsia", "gainsboro", "ghostwhite",
433
+ "gold", "goldenrod", "gray", "grey", "green", "greenyellow", "honeydew",
434
+ "hotpink", "indianred", "indigo", "ivory", "khaki", "lavender",
435
+ "lavenderblush", "lawngreen", "lemonchiffon", "lightblue", "lightcoral",
436
+ "lightcyan", "lightgoldenrodyellow", "lightgray", "lightgreen", "lightpink",
437
+ "lightsalmon", "lightseagreen", "lightskyblue", "lightslategray",
438
+ "lightsteelblue", "lightyellow", "lime", "limegreen", "linen", "magenta",
439
+ "maroon", "mediumaquamarine", "mediumblue", "mediumorchid", "mediumpurple",
440
+ "mediumseagreen", "mediumslateblue", "mediumspringgreen", "mediumturquoise",
441
+ "mediumvioletred", "midnightblue", "mintcream", "mistyrose", "moccasin",
442
+ "navajowhite", "navy", "oldlace", "olive", "olivedrab", "orange", "orangered",
443
+ "orchid", "palegoldenrod", "palegreen", "paleturquoise", "palevioletred",
444
+ "papayawhip", "peachpuff", "peru", "pink", "plum", "powderblue",
445
+ "purple", "red", "rosybrown", "royalblue", "saddlebrown", "salmon",
446
+ "sandybrown", "seagreen", "seashell", "sienna", "silver", "skyblue",
447
+ "slateblue", "slategray", "snow", "springgreen", "steelblue", "tan",
448
+ "teal", "thistle", "tomato", "turquoise", "violet", "wheat", "white",
449
+ "whitesmoke", "yellow", "yellowgreen"
450
+ ]);
451
+
452
+ var valueKeywords = keySet([
453
+ "above", "absolute", "activeborder", "activecaption", "afar",
454
+ "after-white-space", "ahead", "alias", "all", "all-scroll", "alternate",
455
+ "always", "amharic", "amharic-abegede", "antialiased", "appworkspace",
456
+ "arabic-indic", "armenian", "asterisks", "auto", "avoid", "avoid-column", "avoid-page",
457
+ "avoid-region", "background", "backwards", "baseline", "below", "bidi-override", "binary",
458
+ "bengali", "blink", "block", "block-axis", "bold", "bolder", "border", "border-box",
459
+ "both", "bottom", "break", "break-all", "break-word", "button", "button-bevel",
460
+ "buttonface", "buttonhighlight", "buttonshadow", "buttontext", "cambodian",
461
+ "capitalize", "caps-lock-indicator", "caption", "captiontext", "caret",
462
+ "cell", "center", "checkbox", "circle", "cjk-earthly-branch",
463
+ "cjk-heavenly-stem", "cjk-ideographic", "clear", "clip", "close-quote",
464
+ "col-resize", "collapse", "column", "compact", "condensed", "contain", "content",
465
+ "content-box", "context-menu", "continuous", "copy", "cover", "crop",
466
+ "cross", "crosshair", "currentcolor", "cursive", "dashed", "decimal",
467
+ "decimal-leading-zero", "default", "default-button", "destination-atop",
468
+ "destination-in", "destination-out", "destination-over", "devanagari",
469
+ "disc", "discard", "document", "dot-dash", "dot-dot-dash", "dotted",
470
+ "double", "down", "e-resize", "ease", "ease-in", "ease-in-out", "ease-out",
471
+ "element", "ellipse", "ellipsis", "embed", "end", "ethiopic", "ethiopic-abegede",
472
+ "ethiopic-abegede-am-et", "ethiopic-abegede-gez", "ethiopic-abegede-ti-er",
473
+ "ethiopic-abegede-ti-et", "ethiopic-halehame-aa-er",
474
+ "ethiopic-halehame-aa-et", "ethiopic-halehame-am-et",
475
+ "ethiopic-halehame-gez", "ethiopic-halehame-om-et",
476
+ "ethiopic-halehame-sid-et", "ethiopic-halehame-so-et",
477
+ "ethiopic-halehame-ti-er", "ethiopic-halehame-ti-et",
478
+ "ethiopic-halehame-tig", "ew-resize", "expanded", "extra-condensed",
479
+ "extra-expanded", "fantasy", "fast", "fill", "fixed", "flat", "footnotes",
480
+ "forwards", "from", "geometricPrecision", "georgian", "graytext", "groove",
481
+ "gujarati", "gurmukhi", "hand", "hangul", "hangul-consonant", "hebrew",
482
+ "help", "hidden", "hide", "higher", "highlight", "highlighttext",
483
+ "hiragana", "hiragana-iroha", "horizontal", "hsl", "hsla", "icon", "ignore",
484
+ "inactiveborder", "inactivecaption", "inactivecaptiontext", "infinite",
485
+ "infobackground", "infotext", "inherit", "initial", "inline", "inline-axis",
486
+ "inline-block", "inline-table", "inset", "inside", "intrinsic", "invert",
487
+ "italic", "justify", "kannada", "katakana", "katakana-iroha", "keep-all", "khmer",
488
+ "landscape", "lao", "large", "larger", "left", "level", "lighter",
489
+ "line-through", "linear", "lines", "list-item", "listbox", "listitem",
490
+ "local", "logical", "loud", "lower", "lower-alpha", "lower-armenian",
491
+ "lower-greek", "lower-hexadecimal", "lower-latin", "lower-norwegian",
492
+ "lower-roman", "lowercase", "ltr", "malayalam", "match",
493
+ "media-controls-background", "media-current-time-display",
494
+ "media-fullscreen-button", "media-mute-button", "media-play-button",
495
+ "media-return-to-realtime-button", "media-rewind-button",
496
+ "media-seek-back-button", "media-seek-forward-button", "media-slider",
497
+ "media-sliderthumb", "media-time-remaining-display", "media-volume-slider",
498
+ "media-volume-slider-container", "media-volume-sliderthumb", "medium",
499
+ "menu", "menulist", "menulist-button", "menulist-text",
500
+ "menulist-textfield", "menutext", "message-box", "middle", "min-intrinsic",
501
+ "mix", "mongolian", "monospace", "move", "multiple", "myanmar", "n-resize",
502
+ "narrower", "ne-resize", "nesw-resize", "no-close-quote", "no-drop",
503
+ "no-open-quote", "no-repeat", "none", "normal", "not-allowed", "nowrap",
504
+ "ns-resize", "nw-resize", "nwse-resize", "oblique", "octal", "open-quote",
505
+ "optimizeLegibility", "optimizeSpeed", "oriya", "oromo", "outset",
506
+ "outside", "outside-shape", "overlay", "overline", "padding", "padding-box",
507
+ "painted", "page", "paused", "persian", "plus-darker", "plus-lighter", "pointer",
508
+ "polygon", "portrait", "pre", "pre-line", "pre-wrap", "preserve-3d", "progress", "push-button",
509
+ "radio", "read-only", "read-write", "read-write-plaintext-only", "rectangle", "region",
510
+ "relative", "repeat", "repeat-x", "repeat-y", "reset", "reverse", "rgb", "rgba",
511
+ "ridge", "right", "round", "row-resize", "rtl", "run-in", "running",
512
+ "s-resize", "sans-serif", "scroll", "scrollbar", "se-resize", "searchfield",
513
+ "searchfield-cancel-button", "searchfield-decoration",
514
+ "searchfield-results-button", "searchfield-results-decoration",
515
+ "semi-condensed", "semi-expanded", "separate", "serif", "show", "sidama",
516
+ "single", "skip-white-space", "slide", "slider-horizontal",
517
+ "slider-vertical", "sliderthumb-horizontal", "sliderthumb-vertical", "slow",
518
+ "small", "small-caps", "small-caption", "smaller", "solid", "somali",
519
+ "source-atop", "source-in", "source-out", "source-over", "space", "square",
520
+ "square-button", "start", "static", "status-bar", "stretch", "stroke",
521
+ "sub", "subpixel-antialiased", "super", "sw-resize", "table",
522
+ "table-caption", "table-cell", "table-column", "table-column-group",
523
+ "table-footer-group", "table-header-group", "table-row", "table-row-group",
524
+ "telugu", "text", "text-bottom", "text-top", "textarea", "textfield", "thai",
525
+ "thick", "thin", "threeddarkshadow", "threedface", "threedhighlight",
526
+ "threedlightshadow", "threedshadow", "tibetan", "tigre", "tigrinya-er",
527
+ "tigrinya-er-abegede", "tigrinya-et", "tigrinya-et-abegede", "to", "top",
528
+ "transparent", "ultra-condensed", "ultra-expanded", "underline", "up",
529
+ "upper-alpha", "upper-armenian", "upper-greek", "upper-hexadecimal",
530
+ "upper-latin", "upper-norwegian", "upper-roman", "uppercase", "urdu", "url",
531
+ "vertical", "vertical-text", "visible", "visibleFill", "visiblePainted",
532
+ "visibleStroke", "visual", "w-resize", "wait", "wave", "wider",
533
+ "window", "windowframe", "windowtext", "x-large", "x-small", "xor",
534
+ "xx-large", "xx-small"
535
+ ]);
536
+
537
+ function tokenCComment(stream, state) {
538
+ var maybeEnd = false, ch;
539
+ while ((ch = stream.next()) != null) {
540
+ if (maybeEnd && ch == "/") {
541
+ state.tokenize = null;
542
+ break;
543
+ }
544
+ maybeEnd = (ch == "*");
545
+ }
546
+ return ["comment", "comment"];
547
+ }
548
+
549
+ CodeMirror.defineMIME("text/css", {
550
+ atMediaTypes: atMediaTypes,
551
+ atMediaFeatures: atMediaFeatures,
552
+ propertyKeywords: propertyKeywords,
553
+ colorKeywords: colorKeywords,
554
+ valueKeywords: valueKeywords,
555
+ hooks: {
556
+ "<": function(stream, state) {
557
+ function tokenSGMLComment(stream, state) {
558
+ var dashes = 0, ch;
559
+ while ((ch = stream.next()) != null) {
560
+ if (dashes >= 2 && ch == ">") {
561
+ state.tokenize = null;
562
+ break;
563
+ }
564
+ dashes = (ch == "-") ? dashes + 1 : 0;
565
+ }
566
+ return ["comment", "comment"];
567
+ }
568
+ if (stream.eat("!")) {
569
+ state.tokenize = tokenSGMLComment;
570
+ return tokenSGMLComment(stream, state);
571
+ }
572
+ },
573
+ "/": function(stream, state) {
574
+ if (stream.eat("*")) {
575
+ state.tokenize = tokenCComment;
576
+ return tokenCComment(stream, state);
577
+ }
578
+ return false;
579
+ }
580
+ },
581
+ name: "css"
582
+ });
583
+
584
+ CodeMirror.defineMIME("text/x-scss", {
585
+ atMediaTypes: atMediaTypes,
586
+ atMediaFeatures: atMediaFeatures,
587
+ propertyKeywords: propertyKeywords,
588
+ colorKeywords: colorKeywords,
589
+ valueKeywords: valueKeywords,
590
+ allowNested: true,
591
+ hooks: {
592
+ ":": function(stream) {
593
+ if (stream.match(/\s*{/)) {
594
+ return [null, "{"];
595
+ }
596
+ return false;
597
+ },
598
+ "$": function(stream) {
599
+ stream.match(/^[\w-]+/);
600
+ if (stream.peek() == ":") {
601
+ return ["variable", "variable-definition"];
602
+ }
603
+ return ["variable", "variable"];
604
+ },
605
+ "/": function(stream, state) {
606
+ if (stream.eat("/")) {
607
+ stream.skipToEnd();
608
+ return ["comment", "comment"];
609
+ } else if (stream.eat("*")) {
610
+ state.tokenize = tokenCComment;
611
+ return tokenCComment(stream, state);
612
+ } else {
613
+ return ["operator", "operator"];
614
+ }
615
+ },
616
+ "#": function(stream) {
617
+ if (stream.eat("{")) {
618
+ return ["operator", "interpolation"];
619
+ } else {
620
+ stream.eatWhile(/[\w\\\-]/);
621
+ return ["atom", "hash"];
622
+ }
623
+ }
624
+ },
625
+ name: "css"
626
+ });
627
+ })();
ditty-news-ticker.php CHANGED
@@ -3,7 +3,7 @@
3
  Plugin Name: Ditty News Ticker
4
  Plugin URI: http://dittynewsticker.com/
5
  Description: Ditty News Ticker is a multi-functional data display plugin
6
- Version: 1.2.0
7
  Author: Metaphor Creations
8
  Author URI: http://www.metaphorcreations.com
9
  License: GPL2
@@ -42,12 +42,12 @@ The icons are licensed under a Creative Commons Attribution
42
  /**
43
  * Define constants
44
  *
45
- * @since 1.1.9
46
  */
47
  if ( WP_DEBUG ) {
48
- define ( 'MTPHR_DNT_VERSION', '1.2.0-'.time() );
49
  } else {
50
- define ( 'MTPHR_DNT_VERSION', '1.2.0' );
51
  }
52
  define ( 'MTPHR_DNT_DIR', plugin_dir_path(__FILE__) );
53
  define ( 'MTPHR_DNT_URL', plugins_url().'/ditty-news-ticker' );
3
  Plugin Name: Ditty News Ticker
4
  Plugin URI: http://dittynewsticker.com/
5
  Description: Ditty News Ticker is a multi-functional data display plugin
6
+ Version: 1.2.1
7
  Author: Metaphor Creations
8
  Author URI: http://www.metaphorcreations.com
9
  License: GPL2
42
  /**
43
  * Define constants
44
  *
45
+ * @since 1.2.1
46
  */
47
  if ( WP_DEBUG ) {
48
+ define ( 'MTPHR_DNT_VERSION', '1.2.1-'.time() );
49
  } else {
50
+ define ( 'MTPHR_DNT_VERSION', '1.2.1' );
51
  }
52
  define ( 'MTPHR_DNT_DIR', plugin_dir_path(__FILE__) );
53
  define ( 'MTPHR_DNT_URL', plugins_url().'/ditty-news-ticker' );
includes/functions.php CHANGED
@@ -22,7 +22,7 @@ function ditty_news_ticker( $id='', $class='', $atts=false ) {
22
  /**
23
  * Return the ticker
24
  *
25
- * @since 1.1.8
26
  */
27
  function get_mtphr_dnt_ticker( $id='', $class='', $atts=false ) {
28
 
@@ -60,7 +60,7 @@ function get_mtphr_dnt_ticker( $id='', $class='', $atts=false ) {
60
  if( is_array($_mtphr_dnt_ticks) ) {
61
  foreach( $_mtphr_dnt_ticks as $i => $tick ) {
62
 
63
- if( $text = wp_kses_post($tick['tick']) ) {
64
 
65
  // Get the contents
66
  if( $link = esc_url($tick['link']) ) {
22
  /**
23
  * Return the ticker
24
  *
25
+ * @since 1.2.1
26
  */
27
  function get_mtphr_dnt_ticker( $id='', $class='', $atts=false ) {
28
 
60
  if( is_array($_mtphr_dnt_ticks) ) {
61
  foreach( $_mtphr_dnt_ticks as $i => $tick ) {
62
 
63
+ if( $text = wp_kses_post(nl2br($tick['tick'])) ) {
64
 
65
  // Get the contents
66
  if( $link = esc_url($tick['link']) ) {
includes/metaboxer/metaboxer.css CHANGED
@@ -19,7 +19,7 @@
19
 
20
  /* Fields */
21
  .mtphr-dnt-metaboxer-field {
22
- margin-bottom: 20px;
23
  }
24
  #side-info-column .mtphr-dnt-metaboxer-field {
25
  margin-bottom: 10px;
19
 
20
  /* Fields */
21
  .mtphr-dnt-metaboxer-field {
22
+ /* margin-bottom: 20px; */
23
  }
24
  #side-info-column .mtphr-dnt-metaboxer-field {
25
  margin-bottom: 10px;
includes/metaboxer/metaboxer.js CHANGED
@@ -402,6 +402,22 @@ $('.mtphr-dnt-metaboxer-sort').each( function(index) {
402
  });
403
 
404
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
405
 
406
 
407
 
402
  });
403
 
404
 
405
+ /* --------------------------------------------------------- */
406
+ /* !Add codemirror css editor - 1.2.1 */
407
+ /* --------------------------------------------------------- */
408
+
409
+ $('.mtphr-dnt-custom-css').each( function(i) {
410
+
411
+ var $textarea = $(this).find('textarea');
412
+ var myCodeMirror = CodeMirror.fromTextArea($textarea[0], {
413
+ 'mode' : 'css',
414
+ 'lineNumbers' : true,
415
+ 'lineWrapping' : true
416
+ });
417
+ myCodeMirror.setSize( false, 300 );
418
+ });
419
+
420
+
421
 
422
 
423
 
includes/metaboxer/metaboxer.php CHANGED
@@ -700,7 +700,7 @@ function mtphr_dnt_metaboxer_text( $field, $value='' ) {
700
  /**
701
  * Renders a textarea.
702
  *
703
- * @since 1.0.0
704
  */
705
  function mtphr_dnt_metaboxer_textarea( $field, $value='' ) {
706
  $rows = ( isset($field['rows']) ) ? $field['rows'] : 5;
700
  /**
701
  * Renders a textarea.
702
  *
703
+ * @since 1.2.1
704
  */
705
  function mtphr_dnt_metaboxer_textarea( $field, $value='' ) {
706
  $rows = ( isset($field['rows']) ) ? $field['rows'] : 5;
includes/scripts.php CHANGED
@@ -12,7 +12,7 @@ add_action( 'admin_enqueue_scripts', 'mtphr_dnt_admin_scripts' );
12
  /**
13
  * Load the metaboxer scripts
14
  *
15
- * @since 1.0.0
16
  */
17
  function mtphr_dnt_admin_scripts( $hook ) {
18
 
@@ -20,10 +20,6 @@ function mtphr_dnt_admin_scripts( $hook ) {
20
 
21
  if ( $typenow == 'ditty_news_ticker' ) {
22
 
23
- // Load the metaboxer style sheet
24
- wp_register_style( 'ditty-metaboxer', MTPHR_DNT_URL.'/includes/metaboxer/metaboxer.css', array( 'colors', 'thickbox' ), MTPHR_DNT_VERSION );
25
- wp_enqueue_style( 'ditty-metaboxer' );
26
-
27
  // Load scipts for the media uploader
28
  if(function_exists( 'wp_enqueue_media' )){
29
  wp_enqueue_media();
@@ -33,11 +29,19 @@ function mtphr_dnt_admin_scripts( $hook ) {
33
  wp_enqueue_script('thickbox');
34
  }
35
 
36
- // Load the metaboxer jQuery
 
 
 
 
 
 
 
 
 
 
37
  wp_register_script( 'ditty-metaboxer', MTPHR_DNT_URL.'/includes/metaboxer/metaboxer.js', array( 'jquery','jquery-ui-core','jquery-ui-sortable' ), MTPHR_DNT_VERSION, true );
38
  wp_enqueue_script( 'ditty-metaboxer' );
39
-
40
- // Localize scripts
41
  wp_localize_script( 'ditty-metaboxer', 'ditty_metaboxer_vars', array(
42
  'security' => wp_create_nonce( 'ditty-metaboxer' )
43
  )
12
  /**
13
  * Load the metaboxer scripts
14
  *
15
+ * @since 1.2.1
16
  */
17
  function mtphr_dnt_admin_scripts( $hook ) {
18
 
20
 
21
  if ( $typenow == 'ditty_news_ticker' ) {
22
 
 
 
 
 
23
  // Load scipts for the media uploader
24
  if(function_exists( 'wp_enqueue_media' )){
25
  wp_enqueue_media();
29
  wp_enqueue_script('thickbox');
30
  }
31
 
32
+ // Load the CodeMirror plugin
33
+ wp_register_style( 'codemirror', MTPHR_DNT_URL.'/assets/css/codemirror.css', false, MTPHR_DNT_VERSION );
34
+ wp_enqueue_style( 'codemirror' );
35
+ wp_register_script( 'codemirror', MTPHR_DNT_URL.'/assets/js/codemirror.js', array('jquery'), MTPHR_DNT_VERSION, true );
36
+ wp_enqueue_script( 'codemirror' );
37
+ wp_register_script( 'codemirror-css', MTPHR_DNT_URL.'/assets/js/css.js', array('jquery'), MTPHR_DNT_VERSION, true );
38
+ wp_enqueue_script( 'codemirror-css' );
39
+
40
+ // Load the metaboxer scripts
41
+ wp_register_style( 'ditty-metaboxer', MTPHR_DNT_URL.'/includes/metaboxer/metaboxer.css', array( 'colors', 'thickbox' ), MTPHR_DNT_VERSION );
42
+ wp_enqueue_style( 'ditty-metaboxer' );
43
  wp_register_script( 'ditty-metaboxer', MTPHR_DNT_URL.'/includes/metaboxer/metaboxer.js', array( 'jquery','jquery-ui-core','jquery-ui-sortable' ), MTPHR_DNT_VERSION, true );
44
  wp_enqueue_script( 'ditty-metaboxer' );
 
 
45
  wp_localize_script( 'ditty-metaboxer', 'ditty_metaboxer_vars', array(
46
  'security' => wp_create_nonce( 'ditty-metaboxer' )
47
  )
includes/settings.php CHANGED
@@ -33,7 +33,7 @@ add_action( 'admin_init', 'mtphr_dnt_initialize_settings' );
33
  /**
34
  * Setup the custom options for the settings page
35
  *
36
- * @since 1.1.4
37
  */
38
  function mtphr_dnt_initialize_settings() {
39
 
@@ -53,6 +53,7 @@ function mtphr_dnt_initialize_settings() {
53
  'title' => __( 'Custom CSS', 'ditty-news-ticker' ),
54
  'type' => 'textarea',
55
  'rows' => 20,
 
56
  'description' => __( 'Custom CSS will be added to the head of each page that includes a Ditty News Ticker.', 'ditty-news-ticker' )
57
  );
58
  if( false == get_option('mtphr_dnt_general_settings') ) {
@@ -151,7 +152,7 @@ function mtphr_dnt_general_settings_callback() {
151
  /**
152
  * The custom field callback.
153
  *
154
- * @since 1.0.0
155
  */
156
  function mtphr_dnt_settings_callback( $args ) {
157
 
@@ -166,15 +167,15 @@ function mtphr_dnt_settings_callback( $args ) {
166
  $value = $args['default'];
167
  }
168
  if( isset($args['type']) ) {
169
-
170
- echo '<div class="mtphr-dnt-metaboxer-field mtphr-dnt-metaboxer-'.$args['type'].'">';
171
 
172
  // Call the function to display the field
173
  if ( function_exists('mtphr_dnt_metaboxer_'.$args['type']) ) {
174
  call_user_func( 'mtphr_dnt_metaboxer_'.$args['type'], $args, $value );
175
  }
176
 
177
- echo '<div>';
178
  }
179
 
180
  // Add a descriptions
33
  /**
34
  * Setup the custom options for the settings page
35
  *
36
+ * @since 1.2.1
37
  */
38
  function mtphr_dnt_initialize_settings() {
39
 
53
  'title' => __( 'Custom CSS', 'ditty-news-ticker' ),
54
  'type' => 'textarea',
55
  'rows' => 20,
56
+ 'class' => 'mtphr-dnt-custom-css',
57
  'description' => __( 'Custom CSS will be added to the head of each page that includes a Ditty News Ticker.', 'ditty-news-ticker' )
58
  );
59
  if( false == get_option('mtphr_dnt_general_settings') ) {
152
  /**
153
  * The custom field callback.
154
  *
155
+ * @since 1.2.1
156
  */
157
  function mtphr_dnt_settings_callback( $args ) {
158
 
167
  $value = $args['default'];
168
  }
169
  if( isset($args['type']) ) {
170
+ $class = ( isset($args['class']) ) ? $args['class'] : '';
171
+ echo '<div class="mtphr-dnt-metaboxer-field mtphr-dnt-metaboxer-'.$args['type'].' '.$class.'">';
172
 
173
  // Call the function to display the field
174
  if ( function_exists('mtphr_dnt_metaboxer_'.$args['type']) ) {
175
  call_user_func( 'mtphr_dnt_metaboxer_'.$args['type'], $args, $value );
176
  }
177
 
178
+ echo '</div>';
179
  }
180
 
181
  // Add a descriptions
readme.txt CHANGED
@@ -52,6 +52,10 @@ Each individual Ticker post has multiple settings to customize.
52
 
53
  == Changelog ==
54
 
 
 
 
 
55
  = 1.2.0 =
56
  * Modified ditty-news-ticker js class due to issues with certain versions of jQuery.
57
  * Changed outerWidth() to with().
@@ -150,4 +154,4 @@ Each individual Ticker post has multiple settings to customize.
150
 
151
  == Upgrade Notice ==
152
 
153
- Modified ditty-news-ticker js class due to issues with certain versions of jQuery.
52
 
53
  == Changelog ==
54
 
55
+ = 1.2.1 =
56
+ * Modifed to allow multiline text
57
+ * Updated custom css field to css editor
58
+
59
  = 1.2.0 =
60
  * Modified ditty-news-ticker js class due to issues with certain versions of jQuery.
61
  * Changed outerWidth() to with().
154
 
155
  == Upgrade Notice ==
156
 
157
+ Modifed to allow multiline text and updated custom css field to css editor