Head, Footer and Post Injections - Version 2.0.3

Version Description

Download this release

Release Info

Developer satollo
Plugin Icon wp plugin Head, Footer and Post Injections
Version 2.0.3
Comparing to
See all releases

Version 2.0.3

Files changed (55) hide show
  1. admin.css +107 -0
  2. admin.php +90 -0
  3. codemirror/lib/codemirror.css +335 -0
  4. codemirror/lib/codemirror.js +8892 -0
  5. codemirror/mode/clike/clike.js +781 -0
  6. codemirror/mode/clike/index.html +360 -0
  7. codemirror/mode/clike/scala.html +767 -0
  8. codemirror/mode/clike/test.js +51 -0
  9. codemirror/mode/css/css.js +825 -0
  10. codemirror/mode/css/gss.html +103 -0
  11. codemirror/mode/css/gss_test.js +17 -0
  12. codemirror/mode/css/index.html +75 -0
  13. codemirror/mode/css/less.html +152 -0
  14. codemirror/mode/css/less_test.js +54 -0
  15. codemirror/mode/css/scss.html +157 -0
  16. codemirror/mode/css/scss_test.js +110 -0
  17. codemirror/mode/css/test.js +200 -0
  18. codemirror/mode/htmlembedded/htmlembedded.js +28 -0
  19. codemirror/mode/htmlembedded/index.html +59 -0
  20. codemirror/mode/htmlmixed/htmlmixed.js +152 -0
  21. codemirror/mode/htmlmixed/index.html +89 -0
  22. codemirror/mode/index.html +158 -0
  23. codemirror/mode/javascript/index.html +114 -0
  24. codemirror/mode/javascript/javascript.js +742 -0
  25. codemirror/mode/javascript/json-ld.html +72 -0
  26. codemirror/mode/javascript/test.js +210 -0
  27. codemirror/mode/javascript/typescript.html +61 -0
  28. codemirror/mode/meta.js +202 -0
  29. codemirror/mode/php/index.html +64 -0
  30. codemirror/mode/php/php.js +234 -0
  31. codemirror/mode/php/test.js +154 -0
  32. codemirror/mode/sass/index.html +66 -0
  33. codemirror/mode/sass/sass.js +414 -0
  34. codemirror/mode/xml/index.html +61 -0
  35. codemirror/mode/xml/test.js +51 -0
  36. codemirror/mode/xml/xml.js +394 -0
  37. images/social/round/facebook.png +0 -0
  38. images/social/round/google.png +0 -0
  39. images/social/round/linkedin.png +0 -0
  40. images/social/round/twitter.png +0 -0
  41. images/social/square/facebook.png +0 -0
  42. images/social/square/google.png +0 -0
  43. images/social/square/linkedin.png +0 -0
  44. images/social/square/pinterest.png +0 -0
  45. images/social/square/twitter.png +0 -0
  46. languages/header-footer-es_ES.mo +0 -0
  47. languages/header-footer-es_ES.po +168 -0
  48. languages/header-footer-ru_RU.mo +0 -0
  49. languages/header-footer-ru_RU.po +341 -0
  50. languages/header-footer.pot +147 -0
  51. lib/easytabs/jquery.easytabs.min.js +12 -0
  52. lib/easytabs/tabs.css +71 -0
  53. options.php +771 -0
  54. plugin.php +564 -0
  55. readme.txt +296 -0
admin.css ADDED
@@ -0,0 +1,107 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ .wrap, .form-table, .form-table td, .form-table th, .form-table td p, .form-wrap label {
3
+ font-size: 12px;
4
+ }
5
+
6
+ .satollo-dismiss {
7
+ float: right;
8
+ }
9
+
10
+ .satollo-notice {
11
+ background-color: lightYellow;
12
+ border-color: #E6DB55;
13
+ border-radius: 3px;
14
+ border-style: solid;
15
+ border-width: 1px;
16
+ padding: .6em;
17
+ margin-bottom: .6em;
18
+ margin-top: .6em;
19
+ }
20
+
21
+ .form-table th {
22
+ font-weight: bold;
23
+ font-size: 12px;
24
+ text-align: right;
25
+ border-right: 1px solid #ddd;
26
+ }
27
+ .form-table td {
28
+ font-size: 12px;
29
+ }
30
+
31
+ h3 {
32
+ font-weight: bold;
33
+ margin-bottom: 0;
34
+ padding: 0;
35
+ text-transform: uppercase;
36
+ }
37
+
38
+ .form-table textarea, .ui-widget textarea {
39
+ font-family: Consolas,Monaco,monospace;
40
+ }
41
+
42
+ .CodeMirror {
43
+ border: 1px solid #eee;
44
+ height: 200px;
45
+ }
46
+
47
+ .row {
48
+ margin-left: -10px; margin-right: -10px;
49
+ }
50
+
51
+ .col-2 {
52
+ width: 48%;
53
+ min-width: 350px;
54
+ float: left;
55
+ padding: 10px;
56
+ }
57
+
58
+ .clearfix {
59
+ float: none;
60
+ clear: both;
61
+ display: table;
62
+ content: "";
63
+ }
64
+
65
+ .hefo-cm {
66
+ width: 100%;
67
+ height: 100px;
68
+ }
69
+
70
+ @media all and (max-width: 1200px) {
71
+ .col-2 {
72
+ width: 100%;
73
+ }
74
+ }
75
+
76
+ .tab a {
77
+ text-decoration: none;
78
+ }
79
+
80
+ .tab a.active {
81
+ font-weight: normal;
82
+ }
83
+ .tab a:focus {
84
+ outline: none;
85
+ border: 0;
86
+ box-shadow: none;
87
+ }
88
+
89
+ .rules {
90
+ margin-top: 15px;
91
+ }
92
+ .rules select {
93
+ height: 27px;
94
+ display: block;
95
+ margin-right: 10px;
96
+ }
97
+
98
+ .rules input {
99
+ height: 27px;
100
+ display: block;
101
+ margin-right: 10px;
102
+ }
103
+
104
+ .rules div {
105
+ margin-right: 10px;
106
+ padding-top: 5px;
107
+ }
admin.php ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ delete_option('hefo_version');
4
+
5
+ add_action('admin_init', 'hefo_admin_init');
6
+
7
+ function hefo_admin_init() {
8
+ global $hefo_options;
9
+ if (isset($_GET['page']) && strpos($_GET['page'], 'header-footer/') === 0) {
10
+ header('X-XSS-Protection: 0');
11
+ wp_enqueue_script('jquery-ui-tabs');
12
+ wp_enqueue_script('media-upload');
13
+ wp_enqueue_script('thickbox');
14
+ wp_enqueue_style('thickbox');
15
+ }
16
+
17
+ if (isset($hefo_options['page_add_tags'])) {
18
+ register_taxonomy_for_object_type('post_tag', 'page');
19
+ }
20
+
21
+ if (isset($hefo_options['page_add_categories'])) {
22
+ register_taxonomy_for_object_type('category', 'page');
23
+ }
24
+ }
25
+
26
+ add_action('admin_head', 'hefo_admin_head');
27
+
28
+ function hefo_admin_head() {
29
+ // if (isset($_GET['page']) && strpos($_GET['page'], 'header-footer/') === 0) {
30
+ // echo '<link type="text/css" rel="stylesheet" href="' .
31
+ // get_option('siteurl') . '/wp-content/plugins/header-footer/admin.css"/>';
32
+ // }
33
+ }
34
+
35
+ add_action('admin_menu', 'hefo_admin_menu');
36
+
37
+ function hefo_admin_menu() {
38
+ add_options_page('Header and Footer', 'Header and Footer', 'manage_options', 'header-footer/options.php');
39
+ }
40
+
41
+ add_action('add_meta_boxes', 'hefo_add_meta_boxes');
42
+
43
+ add_action('save_post', 'hefo_save_post');
44
+
45
+ function hefo_add_meta_boxes() {
46
+ foreach (array('post', 'page') as $screen) {
47
+ add_meta_box(
48
+ 'hefo', __('Header and Footer', 'header-footer'), 'hefo_add_meta_boxes_callback', $screen
49
+ );
50
+ }
51
+ }
52
+
53
+ function hefo_add_meta_boxes_callback($post) {
54
+
55
+ // Use nonce for verification
56
+ wp_nonce_field(plugin_basename(__FILE__), 'hefo');
57
+
58
+ // The actual fields for data entry
59
+ // Use get_post_meta to retrieve an existing value from the database and use the value for the form
60
+ $before = get_post_meta($post->ID, 'hefo_before', true);
61
+ $after = get_post_meta($post->ID, 'hefo_after', true);
62
+ echo '<label>';
63
+ echo '<input type="checkbox" id="hefo_before" name="hefo_before" ' . (empty($before) ? "" : "checked") . '> ';
64
+ _e("Disable top injection", 'header-footer');
65
+ echo '</label> ';
66
+ echo '<br>';
67
+ echo '<label>';
68
+ echo '<input type="checkbox" id="hefo_after" name="hefo_after" ' . (empty($after) ? "" : "checked") . '> ';
69
+ _e("Disable bottom injection", 'header-footer');
70
+ echo '</label> ';
71
+ }
72
+
73
+ function hefo_save_post($post_id) {
74
+
75
+ // First we need to check if the current user is authorised to do this action.
76
+ if (isset($_POST['post_type']) && 'page' == $_POST['post_type']) {
77
+ if (!current_user_can('edit_page', $post_id))
78
+ return;
79
+ } else {
80
+ if (!current_user_can('edit_post', $post_id))
81
+ return;
82
+ }
83
+
84
+ // Secondly we need to check if the user intended to change this value.
85
+ if (!isset($_POST['hefo']) || !wp_verify_nonce($_POST['hefo'], plugin_basename(__FILE__)))
86
+ return;
87
+
88
+ update_post_meta($post_id, 'hefo_before', isset($_REQUEST['hefo_before']) ? 1 : 0);
89
+ update_post_meta($post_id, 'hefo_after', isset($_REQUEST['hefo_after']) ? 1 : 0);
90
+ }
codemirror/lib/codemirror.css ADDED
@@ -0,0 +1,335 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* BASICS */
2
+
3
+ .CodeMirror {
4
+ /* Set height, width, borders, and global font properties here */
5
+ font-family: monospace;
6
+ height: 300px;
7
+ color: black;
8
+ }
9
+
10
+ /* PADDING */
11
+
12
+ .CodeMirror-lines {
13
+ padding: 4px 0; /* Vertical padding around content */
14
+ }
15
+ .CodeMirror pre {
16
+ padding: 0 4px; /* Horizontal padding of content */
17
+ }
18
+
19
+ .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
20
+ background-color: white; /* The little square between H and V scrollbars */
21
+ }
22
+
23
+ /* GUTTER */
24
+
25
+ .CodeMirror-gutters {
26
+ border-right: 1px solid #ddd;
27
+ background-color: #f7f7f7;
28
+ white-space: nowrap;
29
+ }
30
+ .CodeMirror-linenumbers {}
31
+ .CodeMirror-linenumber {
32
+ padding: 0 3px 0 5px;
33
+ min-width: 20px;
34
+ text-align: right;
35
+ color: #999;
36
+ white-space: nowrap;
37
+ }
38
+
39
+ .CodeMirror-guttermarker { color: black; }
40
+ .CodeMirror-guttermarker-subtle { color: #999; }
41
+
42
+ /* CURSOR */
43
+
44
+ .CodeMirror-cursor {
45
+ border-left: 1px solid black;
46
+ border-right: none;
47
+ width: 0;
48
+ }
49
+ /* Shown when moving in bi-directional text */
50
+ .CodeMirror div.CodeMirror-secondarycursor {
51
+ border-left: 1px solid silver;
52
+ }
53
+ .cm-fat-cursor .CodeMirror-cursor {
54
+ width: auto;
55
+ border: 0;
56
+ background: #7e7;
57
+ }
58
+ .cm-fat-cursor div.CodeMirror-cursors {
59
+ z-index: 1;
60
+ }
61
+
62
+ .cm-animate-fat-cursor {
63
+ width: auto;
64
+ border: 0;
65
+ -webkit-animation: blink 1.06s steps(1) infinite;
66
+ -moz-animation: blink 1.06s steps(1) infinite;
67
+ animation: blink 1.06s steps(1) infinite;
68
+ background-color: #7e7;
69
+ }
70
+ @-moz-keyframes blink {
71
+ 0% {}
72
+ 50% { background-color: transparent; }
73
+ 100% {}
74
+ }
75
+ @-webkit-keyframes blink {
76
+ 0% {}
77
+ 50% { background-color: transparent; }
78
+ 100% {}
79
+ }
80
+ @keyframes blink {
81
+ 0% {}
82
+ 50% { background-color: transparent; }
83
+ 100% {}
84
+ }
85
+
86
+ /* Can style cursor different in overwrite (non-insert) mode */
87
+ .CodeMirror-overwrite .CodeMirror-cursor {}
88
+
89
+ .cm-tab { display: inline-block; text-decoration: inherit; }
90
+
91
+ .CodeMirror-ruler {
92
+ border-left: 1px solid #ccc;
93
+ position: absolute;
94
+ }
95
+
96
+ /* DEFAULT THEME */
97
+
98
+ .cm-s-default .cm-header {color: blue;}
99
+ .cm-s-default .cm-quote {color: #090;}
100
+ .cm-negative {color: #d44;}
101
+ .cm-positive {color: #292;}
102
+ .cm-header, .cm-strong {font-weight: bold;}
103
+ .cm-em {font-style: italic;}
104
+ .cm-link {text-decoration: underline;}
105
+ .cm-strikethrough {text-decoration: line-through;}
106
+
107
+ .cm-s-default .cm-keyword {color: #708;}
108
+ .cm-s-default .cm-atom {color: #219;}
109
+ .cm-s-default .cm-number {color: #164;}
110
+ .cm-s-default .cm-def {color: #00f;}
111
+ .cm-s-default .cm-variable,
112
+ .cm-s-default .cm-punctuation,
113
+ .cm-s-default .cm-property,
114
+ .cm-s-default .cm-operator {}
115
+ .cm-s-default .cm-variable-2 {color: #05a;}
116
+ .cm-s-default .cm-variable-3 {color: #085;}
117
+ .cm-s-default .cm-comment {color: #a50;}
118
+ .cm-s-default .cm-string {color: #a11;}
119
+ .cm-s-default .cm-string-2 {color: #f50;}
120
+ .cm-s-default .cm-meta {color: #555;}
121
+ .cm-s-default .cm-qualifier {color: #555;}
122
+ .cm-s-default .cm-builtin {color: #30a;}
123
+ .cm-s-default .cm-bracket {color: #997;}
124
+ .cm-s-default .cm-tag {color: #170;}
125
+ .cm-s-default .cm-attribute {color: #00c;}
126
+ .cm-s-default .cm-hr {color: #999;}
127
+ .cm-s-default .cm-link {color: #00c;}
128
+
129
+ .cm-s-default .cm-error {color: #f00;}
130
+ .cm-invalidchar {color: #f00;}
131
+
132
+ .CodeMirror-composing { border-bottom: 2px solid; }
133
+
134
+ /* Default styles for common addons */
135
+
136
+ div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
137
+ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
138
+ .CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); }
139
+ .CodeMirror-activeline-background {background: #e8f2ff;}
140
+
141
+ /* STOP */
142
+
143
+ /* The rest of this file contains styles related to the mechanics of
144
+ the editor. You probably shouldn't touch them. */
145
+
146
+ .CodeMirror {
147
+ position: relative;
148
+ overflow: hidden;
149
+ background: white;
150
+ }
151
+
152
+ .CodeMirror-scroll {
153
+ overflow: scroll !important; /* Things will break if this is overridden */
154
+ /* 30px is the magic margin used to hide the element's real scrollbars */
155
+ /* See overflow: hidden in .CodeMirror */
156
+ margin-bottom: -30px; margin-right: -30px;
157
+ padding-bottom: 30px;
158
+ height: 100%;
159
+ outline: none; /* Prevent dragging from highlighting the element */
160
+ position: relative;
161
+ }
162
+ .CodeMirror-sizer {
163
+ position: relative;
164
+ border-right: 30px solid transparent;
165
+ }
166
+
167
+ /* The fake, visible scrollbars. Used to force redraw during scrolling
168
+ before actual scrolling happens, thus preventing shaking and
169
+ flickering artifacts. */
170
+ .CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
171
+ position: absolute;
172
+ z-index: 6;
173
+ display: none;
174
+ }
175
+ .CodeMirror-vscrollbar {
176
+ right: 0; top: 0;
177
+ overflow-x: hidden;
178
+ overflow-y: scroll;
179
+ }
180
+ .CodeMirror-hscrollbar {
181
+ bottom: 0; left: 0;
182
+ overflow-y: hidden;
183
+ overflow-x: scroll;
184
+ }
185
+ .CodeMirror-scrollbar-filler {
186
+ right: 0; bottom: 0;
187
+ }
188
+ .CodeMirror-gutter-filler {
189
+ left: 0; bottom: 0;
190
+ }
191
+
192
+ .CodeMirror-gutters {
193
+ position: absolute; left: 0; top: 0;
194
+ z-index: 3;
195
+ }
196
+ .CodeMirror-gutter {
197
+ white-space: normal;
198
+ height: 100%;
199
+ display: inline-block;
200
+ vertical-align: top;
201
+ margin-bottom: -30px;
202
+ /* Hack to make IE7 behave */
203
+ *zoom:1;
204
+ *display:inline;
205
+ }
206
+ .CodeMirror-gutter-wrapper {
207
+ position: absolute;
208
+ z-index: 4;
209
+ background: none !important;
210
+ border: none !important;
211
+ }
212
+ .CodeMirror-gutter-background {
213
+ position: absolute;
214
+ top: 0; bottom: 0;
215
+ z-index: 4;
216
+ }
217
+ .CodeMirror-gutter-elt {
218
+ position: absolute;
219
+ cursor: default;
220
+ z-index: 4;
221
+ }
222
+ .CodeMirror-gutter-wrapper {
223
+ -webkit-user-select: none;
224
+ -moz-user-select: none;
225
+ user-select: none;
226
+ }
227
+
228
+ .CodeMirror-lines {
229
+ cursor: text;
230
+ min-height: 1px; /* prevents collapsing before first draw */
231
+ }
232
+ .CodeMirror pre {
233
+ /* Reset some styles that the rest of the page might have set */
234
+ -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0;
235
+ border-width: 0;
236
+ background: transparent;
237
+ font-family: inherit;
238
+ font-size: inherit;
239
+ margin: 0;
240
+ white-space: pre;
241
+ word-wrap: normal;
242
+ line-height: inherit;
243
+ color: inherit;
244
+ z-index: 2;
245
+ position: relative;
246
+ overflow: visible;
247
+ -webkit-tap-highlight-color: transparent;
248
+ }
249
+ .CodeMirror-wrap pre {
250
+ word-wrap: break-word;
251
+ white-space: pre-wrap;
252
+ word-break: normal;
253
+ }
254
+
255
+ .CodeMirror-linebackground {
256
+ position: absolute;
257
+ left: 0; right: 0; top: 0; bottom: 0;
258
+ z-index: 0;
259
+ }
260
+
261
+ .CodeMirror-linewidget {
262
+ position: relative;
263
+ z-index: 2;
264
+ overflow: auto;
265
+ }
266
+
267
+ .CodeMirror-widget {}
268
+
269
+ .CodeMirror-code {
270
+ outline: none;
271
+ }
272
+
273
+ /* Force content-box sizing for the elements where we expect it */
274
+ .CodeMirror-scroll,
275
+ .CodeMirror-sizer,
276
+ .CodeMirror-gutter,
277
+ .CodeMirror-gutters,
278
+ .CodeMirror-linenumber {
279
+ -moz-box-sizing: content-box;
280
+ box-sizing: content-box;
281
+ }
282
+
283
+ .CodeMirror-measure {
284
+ position: absolute;
285
+ width: 100%;
286
+ height: 0;
287
+ overflow: hidden;
288
+ visibility: hidden;
289
+ }
290
+
291
+ .CodeMirror-cursor { position: absolute; }
292
+ .CodeMirror-measure pre { position: static; }
293
+
294
+ div.CodeMirror-cursors {
295
+ visibility: hidden;
296
+ position: relative;
297
+ z-index: 3;
298
+ }
299
+ div.CodeMirror-dragcursors {
300
+ visibility: visible;
301
+ }
302
+
303
+ .CodeMirror-focused div.CodeMirror-cursors {
304
+ visibility: visible;
305
+ }
306
+
307
+ .CodeMirror-selected { background: #d9d9d9; }
308
+ .CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
309
+ .CodeMirror-crosshair { cursor: crosshair; }
310
+ .CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; }
311
+ .CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; }
312
+
313
+ .cm-searching {
314
+ background: #ffa;
315
+ background: rgba(255, 255, 0, .4);
316
+ }
317
+
318
+ /* IE7 hack to prevent it from returning funny offsetTops on the spans */
319
+ .CodeMirror span { *vertical-align: text-bottom; }
320
+
321
+ /* Used to force a border model for a node */
322
+ .cm-force-border { padding-right: .1px; }
323
+
324
+ @media print {
325
+ /* Hide the cursor when printing */
326
+ .CodeMirror div.CodeMirror-cursors {
327
+ visibility: hidden;
328
+ }
329
+ }
330
+
331
+ /* See issue #2901 */
332
+ .cm-tab-wrap-hack:after { content: ''; }
333
+
334
+ /* Help users use markselection to safely style text background */
335
+ span.CodeMirror-selectedtext { background: none; }
codemirror/lib/codemirror.js ADDED
@@ -0,0 +1,8892 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // CodeMirror, copyright (c) by Marijn Haverbeke and others
2
+ // Distributed under an MIT license: http://codemirror.net/LICENSE
3
+
4
+ // This is CodeMirror (http://codemirror.net), a code editor
5
+ // implemented in JavaScript on top of the browser's DOM.
6
+ //
7
+ // You can find some technical background for some of the code below
8
+ // at http://marijnhaverbeke.nl/blog/#cm-internals .
9
+
10
+ (function(mod) {
11
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
12
+ module.exports = mod();
13
+ else if (typeof define == "function" && define.amd) // AMD
14
+ return define([], mod);
15
+ else // Plain browser env
16
+ (this || window).CodeMirror = mod();
17
+ })(function() {
18
+ "use strict";
19
+
20
+ // BROWSER SNIFFING
21
+
22
+ // Kludges for bugs and behavior differences that can't be feature
23
+ // detected are enabled based on userAgent etc sniffing.
24
+ var userAgent = navigator.userAgent;
25
+ var platform = navigator.platform;
26
+
27
+ var gecko = /gecko\/\d/i.test(userAgent);
28
+ var ie_upto10 = /MSIE \d/.test(userAgent);
29
+ var ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(userAgent);
30
+ var ie = ie_upto10 || ie_11up;
31
+ var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : ie_11up[1]);
32
+ var webkit = /WebKit\//.test(userAgent);
33
+ var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(userAgent);
34
+ var chrome = /Chrome\//.test(userAgent);
35
+ var presto = /Opera\//.test(userAgent);
36
+ var safari = /Apple Computer/.test(navigator.vendor);
37
+ var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(userAgent);
38
+ var phantom = /PhantomJS/.test(userAgent);
39
+
40
+ var ios = /AppleWebKit/.test(userAgent) && /Mobile\/\w+/.test(userAgent);
41
+ // This is woefully incomplete. Suggestions for alternative methods welcome.
42
+ var mobile = ios || /Android|webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(userAgent);
43
+ var mac = ios || /Mac/.test(platform);
44
+ var windows = /win/i.test(platform);
45
+
46
+ var presto_version = presto && userAgent.match(/Version\/(\d*\.\d*)/);
47
+ if (presto_version) presto_version = Number(presto_version[1]);
48
+ if (presto_version && presto_version >= 15) { presto = false; webkit = true; }
49
+ // Some browsers use the wrong event properties to signal cmd/ctrl on OS X
50
+ var flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11));
51
+ var captureRightClick = gecko || (ie && ie_version >= 9);
52
+
53
+ // Optimize some code when these features are not used.
54
+ var sawReadOnlySpans = false, sawCollapsedSpans = false;
55
+
56
+ // EDITOR CONSTRUCTOR
57
+
58
+ // A CodeMirror instance represents an editor. This is the object
59
+ // that user code is usually dealing with.
60
+
61
+ function CodeMirror(place, options) {
62
+ if (!(this instanceof CodeMirror)) return new CodeMirror(place, options);
63
+
64
+ this.options = options = options ? copyObj(options) : {};
65
+ // Determine effective options based on given values and defaults.
66
+ copyObj(defaults, options, false);
67
+ setGuttersForLineNumbers(options);
68
+
69
+ var doc = options.value;
70
+ if (typeof doc == "string") doc = new Doc(doc, options.mode, null, options.lineSeparator);
71
+ this.doc = doc;
72
+
73
+ var input = new CodeMirror.inputStyles[options.inputStyle](this);
74
+ var display = this.display = new Display(place, doc, input);
75
+ display.wrapper.CodeMirror = this;
76
+ updateGutters(this);
77
+ themeChanged(this);
78
+ if (options.lineWrapping)
79
+ this.display.wrapper.className += " CodeMirror-wrap";
80
+ if (options.autofocus && !mobile) display.input.focus();
81
+ initScrollbars(this);
82
+
83
+ this.state = {
84
+ keyMaps: [], // stores maps added by addKeyMap
85
+ overlays: [], // highlighting overlays, as added by addOverlay
86
+ modeGen: 0, // bumped when mode/overlay changes, used to invalidate highlighting info
87
+ overwrite: false,
88
+ delayingBlurEvent: false,
89
+ focused: false,
90
+ suppressEdits: false, // used to disable editing during key handlers when in readOnly mode
91
+ pasteIncoming: false, cutIncoming: false, // help recognize paste/cut edits in input.poll
92
+ selectingText: false,
93
+ draggingText: false,
94
+ highlight: new Delayed(), // stores highlight worker timeout
95
+ keySeq: null, // Unfinished key sequence
96
+ specialChars: null
97
+ };
98
+
99
+ var cm = this;
100
+
101
+ // Override magic textarea content restore that IE sometimes does
102
+ // on our hidden textarea on reload
103
+ if (ie && ie_version < 11) setTimeout(function() { cm.display.input.reset(true); }, 20);
104
+
105
+ registerEventHandlers(this);
106
+ ensureGlobalHandlers();
107
+
108
+ startOperation(this);
109
+ this.curOp.forceUpdate = true;
110
+ attachDoc(this, doc);
111
+
112
+ if ((options.autofocus && !mobile) || cm.hasFocus())
113
+ setTimeout(bind(onFocus, this), 20);
114
+ else
115
+ onBlur(this);
116
+
117
+ for (var opt in optionHandlers) if (optionHandlers.hasOwnProperty(opt))
118
+ optionHandlers[opt](this, options[opt], Init);
119
+ maybeUpdateLineNumberWidth(this);
120
+ if (options.finishInit) options.finishInit(this);
121
+ for (var i = 0; i < initHooks.length; ++i) initHooks[i](this);
122
+ endOperation(this);
123
+ // Suppress optimizelegibility in Webkit, since it breaks text
124
+ // measuring on line wrapping boundaries.
125
+ if (webkit && options.lineWrapping &&
126
+ getComputedStyle(display.lineDiv).textRendering == "optimizelegibility")
127
+ display.lineDiv.style.textRendering = "auto";
128
+ }
129
+
130
+ // DISPLAY CONSTRUCTOR
131
+
132
+ // The display handles the DOM integration, both for input reading
133
+ // and content drawing. It holds references to DOM nodes and
134
+ // display-related state.
135
+
136
+ function Display(place, doc, input) {
137
+ var d = this;
138
+ this.input = input;
139
+
140
+ // Covers bottom-right square when both scrollbars are present.
141
+ d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler");
142
+ d.scrollbarFiller.setAttribute("cm-not-content", "true");
143
+ // Covers bottom of gutter when coverGutterNextToScrollbar is on
144
+ // and h scrollbar is present.
145
+ d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler");
146
+ d.gutterFiller.setAttribute("cm-not-content", "true");
147
+ // Will contain the actual code, positioned to cover the viewport.
148
+ d.lineDiv = elt("div", null, "CodeMirror-code");
149
+ // Elements are added to these to represent selection and cursors.
150
+ d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1");
151
+ d.cursorDiv = elt("div", null, "CodeMirror-cursors");
152
+ // A visibility: hidden element used to find the size of things.
153
+ d.measure = elt("div", null, "CodeMirror-measure");
154
+ // When lines outside of the viewport are measured, they are drawn in this.
155
+ d.lineMeasure = elt("div", null, "CodeMirror-measure");
156
+ // Wraps everything that needs to exist inside the vertically-padded coordinate system
157
+ d.lineSpace = elt("div", [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv],
158
+ null, "position: relative; outline: none");
159
+ // Moved around its parent to cover visible view.
160
+ d.mover = elt("div", [elt("div", [d.lineSpace], "CodeMirror-lines")], null, "position: relative");
161
+ // Set to the height of the document, allowing scrolling.
162
+ d.sizer = elt("div", [d.mover], "CodeMirror-sizer");
163
+ d.sizerWidth = null;
164
+ // Behavior of elts with overflow: auto and padding is
165
+ // inconsistent across browsers. This is used to ensure the
166
+ // scrollable area is big enough.
167
+ d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerGap + "px; width: 1px;");
168
+ // Will contain the gutters, if any.
169
+ d.gutters = elt("div", null, "CodeMirror-gutters");
170
+ d.lineGutter = null;
171
+ // Actual scrollable element.
172
+ d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-scroll");
173
+ d.scroller.setAttribute("tabIndex", "-1");
174
+ // The element in which the editor lives.
175
+ d.wrapper = elt("div", [d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror");
176
+
177
+ // Work around IE7 z-index bug (not perfect, hence IE7 not really being supported)
178
+ if (ie && ie_version < 8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; }
179
+ if (!webkit && !(gecko && mobile)) d.scroller.draggable = true;
180
+
181
+ if (place) {
182
+ if (place.appendChild) place.appendChild(d.wrapper);
183
+ else place(d.wrapper);
184
+ }
185
+
186
+ // Current rendered range (may be bigger than the view window).
187
+ d.viewFrom = d.viewTo = doc.first;
188
+ d.reportedViewFrom = d.reportedViewTo = doc.first;
189
+ // Information about the rendered lines.
190
+ d.view = [];
191
+ d.renderedView = null;
192
+ // Holds info about a single rendered line when it was rendered
193
+ // for measurement, while not in view.
194
+ d.externalMeasured = null;
195
+ // Empty space (in pixels) above the view
196
+ d.viewOffset = 0;
197
+ d.lastWrapHeight = d.lastWrapWidth = 0;
198
+ d.updateLineNumbers = null;
199
+
200
+ d.nativeBarWidth = d.barHeight = d.barWidth = 0;
201
+ d.scrollbarsClipped = false;
202
+
203
+ // Used to only resize the line number gutter when necessary (when
204
+ // the amount of lines crosses a boundary that makes its width change)
205
+ d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null;
206
+ // Set to true when a non-horizontal-scrolling line widget is
207
+ // added. As an optimization, line widget aligning is skipped when
208
+ // this is false.
209
+ d.alignWidgets = false;
210
+
211
+ d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;
212
+
213
+ // Tracks the maximum line length so that the horizontal scrollbar
214
+ // can be kept static when scrolling.
215
+ d.maxLine = null;
216
+ d.maxLineLength = 0;
217
+ d.maxLineChanged = false;
218
+
219
+ // Used for measuring wheel scrolling granularity
220
+ d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null;
221
+
222
+ // True when shift is held down.
223
+ d.shift = false;
224
+
225
+ // Used to track whether anything happened since the context menu
226
+ // was opened.
227
+ d.selForContextMenu = null;
228
+
229
+ d.activeTouch = null;
230
+
231
+ input.init(d);
232
+ }
233
+
234
+ // STATE UPDATES
235
+
236
+ // Used to get the editor into a consistent state again when options change.
237
+
238
+ function loadMode(cm) {
239
+ cm.doc.mode = CodeMirror.getMode(cm.options, cm.doc.modeOption);
240
+ resetModeState(cm);
241
+ }
242
+
243
+ function resetModeState(cm) {
244
+ cm.doc.iter(function(line) {
245
+ if (line.stateAfter) line.stateAfter = null;
246
+ if (line.styles) line.styles = null;
247
+ });
248
+ cm.doc.frontier = cm.doc.first;
249
+ startWorker(cm, 100);
250
+ cm.state.modeGen++;
251
+ if (cm.curOp) regChange(cm);
252
+ }
253
+
254
+ function wrappingChanged(cm) {
255
+ if (cm.options.lineWrapping) {
256
+ addClass(cm.display.wrapper, "CodeMirror-wrap");
257
+ cm.display.sizer.style.minWidth = "";
258
+ cm.display.sizerWidth = null;
259
+ } else {
260
+ rmClass(cm.display.wrapper, "CodeMirror-wrap");
261
+ findMaxLine(cm);
262
+ }
263
+ estimateLineHeights(cm);
264
+ regChange(cm);
265
+ clearCaches(cm);
266
+ setTimeout(function(){updateScrollbars(cm);}, 100);
267
+ }
268
+
269
+ // Returns a function that estimates the height of a line, to use as
270
+ // first approximation until the line becomes visible (and is thus
271
+ // properly measurable).
272
+ function estimateHeight(cm) {
273
+ var th = textHeight(cm.display), wrapping = cm.options.lineWrapping;
274
+ var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3);
275
+ return function(line) {
276
+ if (lineIsHidden(cm.doc, line)) return 0;
277
+
278
+ var widgetsHeight = 0;
279
+ if (line.widgets) for (var i = 0; i < line.widgets.length; i++) {
280
+ if (line.widgets[i].height) widgetsHeight += line.widgets[i].height;
281
+ }
282
+
283
+ if (wrapping)
284
+ return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th;
285
+ else
286
+ return widgetsHeight + th;
287
+ };
288
+ }
289
+
290
+ function estimateLineHeights(cm) {
291
+ var doc = cm.doc, est = estimateHeight(cm);
292
+ doc.iter(function(line) {
293
+ var estHeight = est(line);
294
+ if (estHeight != line.height) updateLineHeight(line, estHeight);
295
+ });
296
+ }
297
+
298
+ function themeChanged(cm) {
299
+ cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") +
300
+ cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-");
301
+ clearCaches(cm);
302
+ }
303
+
304
+ function guttersChanged(cm) {
305
+ updateGutters(cm);
306
+ regChange(cm);
307
+ setTimeout(function(){alignHorizontally(cm);}, 20);
308
+ }
309
+
310
+ // Rebuild the gutter elements, ensure the margin to the left of the
311
+ // code matches their width.
312
+ function updateGutters(cm) {
313
+ var gutters = cm.display.gutters, specs = cm.options.gutters;
314
+ removeChildren(gutters);
315
+ for (var i = 0; i < specs.length; ++i) {
316
+ var gutterClass = specs[i];
317
+ var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + gutterClass));
318
+ if (gutterClass == "CodeMirror-linenumbers") {
319
+ cm.display.lineGutter = gElt;
320
+ gElt.style.width = (cm.display.lineNumWidth || 1) + "px";
321
+ }
322
+ }
323
+ gutters.style.display = i ? "" : "none";
324
+ updateGutterSpace(cm);
325
+ }
326
+
327
+ function updateGutterSpace(cm) {
328
+ var width = cm.display.gutters.offsetWidth;
329
+ cm.display.sizer.style.marginLeft = width + "px";
330
+ }
331
+
332
+ // Compute the character length of a line, taking into account
333
+ // collapsed ranges (see markText) that might hide parts, and join
334
+ // other lines onto it.
335
+ function lineLength(line) {
336
+ if (line.height == 0) return 0;
337
+ var len = line.text.length, merged, cur = line;
338
+ while (merged = collapsedSpanAtStart(cur)) {
339
+ var found = merged.find(0, true);
340
+ cur = found.from.line;
341
+ len += found.from.ch - found.to.ch;
342
+ }
343
+ cur = line;
344
+ while (merged = collapsedSpanAtEnd(cur)) {
345
+ var found = merged.find(0, true);
346
+ len -= cur.text.length - found.from.ch;
347
+ cur = found.to.line;
348
+ len += cur.text.length - found.to.ch;
349
+ }
350
+ return len;
351
+ }
352
+
353
+ // Find the longest line in the document.
354
+ function findMaxLine(cm) {
355
+ var d = cm.display, doc = cm.doc;
356
+ d.maxLine = getLine(doc, doc.first);
357
+ d.maxLineLength = lineLength(d.maxLine);
358
+ d.maxLineChanged = true;
359
+ doc.iter(function(line) {
360
+ var len = lineLength(line);
361
+ if (len > d.maxLineLength) {
362
+ d.maxLineLength = len;
363
+ d.maxLine = line;
364
+ }
365
+ });
366
+ }
367
+
368
+ // Make sure the gutters options contains the element
369
+ // "CodeMirror-linenumbers" when the lineNumbers option is true.
370
+ function setGuttersForLineNumbers(options) {
371
+ var found = indexOf(options.gutters, "CodeMirror-linenumbers");
372
+ if (found == -1 && options.lineNumbers) {
373
+ options.gutters = options.gutters.concat(["CodeMirror-linenumbers"]);
374
+ } else if (found > -1 && !options.lineNumbers) {
375
+ options.gutters = options.gutters.slice(0);
376
+ options.gutters.splice(found, 1);
377
+ }
378
+ }
379
+
380
+ // SCROLLBARS
381
+
382
+ // Prepare DOM reads needed to update the scrollbars. Done in one
383
+ // shot to minimize update/measure roundtrips.
384
+ function measureForScrollbars(cm) {
385
+ var d = cm.display, gutterW = d.gutters.offsetWidth;
386
+ var docH = Math.round(cm.doc.height + paddingVert(cm.display));
387
+ return {
388
+ clientHeight: d.scroller.clientHeight,
389
+ viewHeight: d.wrapper.clientHeight,
390
+ scrollWidth: d.scroller.scrollWidth, clientWidth: d.scroller.clientWidth,
391
+ viewWidth: d.wrapper.clientWidth,
392
+ barLeft: cm.options.fixedGutter ? gutterW : 0,
393
+ docHeight: docH,
394
+ scrollHeight: docH + scrollGap(cm) + d.barHeight,
395
+ nativeBarWidth: d.nativeBarWidth,
396
+ gutterWidth: gutterW
397
+ };
398
+ }
399
+
400
+ function NativeScrollbars(place, scroll, cm) {
401
+ this.cm = cm;
402
+ var vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar");
403
+ var horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar");
404
+ place(vert); place(horiz);
405
+
406
+ on(vert, "scroll", function() {
407
+ if (vert.clientHeight) scroll(vert.scrollTop, "vertical");
408
+ });
409
+ on(horiz, "scroll", function() {
410
+ if (horiz.clientWidth) scroll(horiz.scrollLeft, "horizontal");
411
+ });
412
+
413
+ this.checkedZeroWidth = false;
414
+ // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
415
+ if (ie && ie_version < 8) this.horiz.style.minHeight = this.vert.style.minWidth = "18px";
416
+ }
417
+
418
+ NativeScrollbars.prototype = copyObj({
419
+ update: function(measure) {
420
+ var needsH = measure.scrollWidth > measure.clientWidth + 1;
421
+ var needsV = measure.scrollHeight > measure.clientHeight + 1;
422
+ var sWidth = measure.nativeBarWidth;
423
+
424
+ if (needsV) {
425
+ this.vert.style.display = "block";
426
+ this.vert.style.bottom = needsH ? sWidth + "px" : "0";
427
+ var totalHeight = measure.viewHeight - (needsH ? sWidth : 0);
428
+ // A bug in IE8 can cause this value to be negative, so guard it.
429
+ this.vert.firstChild.style.height =
430
+ Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + "px";
431
+ } else {
432
+ this.vert.style.display = "";
433
+ this.vert.firstChild.style.height = "0";
434
+ }
435
+
436
+ if (needsH) {
437
+ this.horiz.style.display = "block";
438
+ this.horiz.style.right = needsV ? sWidth + "px" : "0";
439
+ this.horiz.style.left = measure.barLeft + "px";
440
+ var totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0);
441
+ this.horiz.firstChild.style.width =
442
+ (measure.scrollWidth - measure.clientWidth + totalWidth) + "px";
443
+ } else {
444
+ this.horiz.style.display = "";
445
+ this.horiz.firstChild.style.width = "0";
446
+ }
447
+
448
+ if (!this.checkedZeroWidth && measure.clientHeight > 0) {
449
+ if (sWidth == 0) this.zeroWidthHack();
450
+ this.checkedZeroWidth = true;
451
+ }
452
+
453
+ return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0};
454
+ },
455
+ setScrollLeft: function(pos) {
456
+ if (this.horiz.scrollLeft != pos) this.horiz.scrollLeft = pos;
457
+ if (this.disableHoriz) this.enableZeroWidthBar(this.horiz, this.disableHoriz);
458
+ },
459
+ setScrollTop: function(pos) {
460
+ if (this.vert.scrollTop != pos) this.vert.scrollTop = pos;
461
+ if (this.disableVert) this.enableZeroWidthBar(this.vert, this.disableVert);
462
+ },
463
+ zeroWidthHack: function() {
464
+ var w = mac && !mac_geMountainLion ? "12px" : "18px";
465
+ this.horiz.style.height = this.vert.style.width = w;
466
+ this.horiz.style.pointerEvents = this.vert.style.pointerEvents = "none";
467
+ this.disableHoriz = new Delayed;
468
+ this.disableVert = new Delayed;
469
+ },
470
+ enableZeroWidthBar: function(bar, delay) {
471
+ bar.style.pointerEvents = "auto";
472
+ function maybeDisable() {
473
+ // To find out whether the scrollbar is still visible, we
474
+ // check whether the element under the pixel in the bottom
475
+ // left corner of the scrollbar box is the scrollbar box
476
+ // itself (when the bar is still visible) or its filler child
477
+ // (when the bar is hidden). If it is still visible, we keep
478
+ // it enabled, if it's hidden, we disable pointer events.
479
+ var box = bar.getBoundingClientRect();
480
+ var elt = document.elementFromPoint(box.left + 1, box.bottom - 1);
481
+ if (elt != bar) bar.style.pointerEvents = "none";
482
+ else delay.set(1000, maybeDisable);
483
+ }
484
+ delay.set(1000, maybeDisable);
485
+ },
486
+ clear: function() {
487
+ var parent = this.horiz.parentNode;
488
+ parent.removeChild(this.horiz);
489
+ parent.removeChild(this.vert);
490
+ }
491
+ }, NativeScrollbars.prototype);
492
+
493
+ function NullScrollbars() {}
494
+
495
+ NullScrollbars.prototype = copyObj({
496
+ update: function() { return {bottom: 0, right: 0}; },
497
+ setScrollLeft: function() {},
498
+ setScrollTop: function() {},
499
+ clear: function() {}
500
+ }, NullScrollbars.prototype);
501
+
502
+ CodeMirror.scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbars};
503
+
504
+ function initScrollbars(cm) {
505
+ if (cm.display.scrollbars) {
506
+ cm.display.scrollbars.clear();
507
+ if (cm.display.scrollbars.addClass)
508
+ rmClass(cm.display.wrapper, cm.display.scrollbars.addClass);
509
+ }
510
+
511
+ cm.display.scrollbars = new CodeMirror.scrollbarModel[cm.options.scrollbarStyle](function(node) {
512
+ cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller);
513
+ // Prevent clicks in the scrollbars from killing focus
514
+ on(node, "mousedown", function() {
515
+ if (cm.state.focused) setTimeout(function() { cm.display.input.focus(); }, 0);
516
+ });
517
+ node.setAttribute("cm-not-content", "true");
518
+ }, function(pos, axis) {
519
+ if (axis == "horizontal") setScrollLeft(cm, pos);
520
+ else setScrollTop(cm, pos);
521
+ }, cm);
522
+ if (cm.display.scrollbars.addClass)
523
+ addClass(cm.display.wrapper, cm.display.scrollbars.addClass);
524
+ }
525
+
526
+ function updateScrollbars(cm, measure) {
527
+ if (!measure) measure = measureForScrollbars(cm);
528
+ var startWidth = cm.display.barWidth, startHeight = cm.display.barHeight;
529
+ updateScrollbarsInner(cm, measure);
530
+ for (var i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != cm.display.barHeight; i++) {
531
+ if (startWidth != cm.display.barWidth && cm.options.lineWrapping)
532
+ updateHeightsInViewport(cm);
533
+ updateScrollbarsInner(cm, measureForScrollbars(cm));
534
+ startWidth = cm.display.barWidth; startHeight = cm.display.barHeight;
535
+ }
536
+ }
537
+
538
+ // Re-synchronize the fake scrollbars with the actual size of the
539
+ // content.
540
+ function updateScrollbarsInner(cm, measure) {
541
+ var d = cm.display;
542
+ var sizes = d.scrollbars.update(measure);
543
+
544
+ d.sizer.style.paddingRight = (d.barWidth = sizes.right) + "px";
545
+ d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + "px";
546
+ d.heightForcer.style.borderBottom = sizes.bottom + "px solid transparent"
547
+
548
+ if (sizes.right && sizes.bottom) {
549
+ d.scrollbarFiller.style.display = "block";
550
+ d.scrollbarFiller.style.height = sizes.bottom + "px";
551
+ d.scrollbarFiller.style.width = sizes.right + "px";
552
+ } else d.scrollbarFiller.style.display = "";
553
+ if (sizes.bottom && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) {
554
+ d.gutterFiller.style.display = "block";
555
+ d.gutterFiller.style.height = sizes.bottom + "px";
556
+ d.gutterFiller.style.width = measure.gutterWidth + "px";
557
+ } else d.gutterFiller.style.display = "";
558
+ }
559
+
560
+ // Compute the lines that are visible in a given viewport (defaults
561
+ // the the current scroll position). viewport may contain top,
562
+ // height, and ensure (see op.scrollToPos) properties.
563
+ function visibleLines(display, doc, viewport) {
564
+ var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop;
565
+ top = Math.floor(top - paddingTop(display));
566
+ var bottom = viewport && viewport.bottom != null ? viewport.bottom : top + display.wrapper.clientHeight;
567
+
568
+ var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom);
569
+ // Ensure is a {from: {line, ch}, to: {line, ch}} object, and
570
+ // forces those lines into the viewport (if possible).
571
+ if (viewport && viewport.ensure) {
572
+ var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line;
573
+ if (ensureFrom < from) {
574
+ from = ensureFrom;
575
+ to = lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight);
576
+ } else if (Math.min(ensureTo, doc.lastLine()) >= to) {
577
+ from = lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight);
578
+ to = ensureTo;
579
+ }
580
+ }
581
+ return {from: from, to: Math.max(to, from + 1)};
582
+ }
583
+
584
+ // LINE NUMBERS
585
+
586
+ // Re-align line numbers and gutter marks to compensate for
587
+ // horizontal scrolling.
588
+ function alignHorizontally(cm) {
589
+ var display = cm.display, view = display.view;
590
+ if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) return;
591
+ var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft;
592
+ var gutterW = display.gutters.offsetWidth, left = comp + "px";
593
+ for (var i = 0; i < view.length; i++) if (!view[i].hidden) {
594
+ if (cm.options.fixedGutter && view[i].gutter)
595
+ view[i].gutter.style.left = left;
596
+ var align = view[i].alignable;
597
+ if (align) for (var j = 0; j < align.length; j++)
598
+ align[j].style.left = left;
599
+ }
600
+ if (cm.options.fixedGutter)
601
+ display.gutters.style.left = (comp + gutterW) + "px";
602
+ }
603
+
604
+ // Used to ensure that the line number gutter is still the right
605
+ // size for the current document size. Returns true when an update
606
+ // is needed.
607
+ function maybeUpdateLineNumberWidth(cm) {
608
+ if (!cm.options.lineNumbers) return false;
609
+ var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display;
610
+ if (last.length != display.lineNumChars) {
611
+ var test = display.measure.appendChild(elt("div", [elt("div", last)],
612
+ "CodeMirror-linenumber CodeMirror-gutter-elt"));
613
+ var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW;
614
+ display.lineGutter.style.width = "";
615
+ display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding) + 1;
616
+ display.lineNumWidth = display.lineNumInnerWidth + padding;
617
+ display.lineNumChars = display.lineNumInnerWidth ? last.length : -1;
618
+ display.lineGutter.style.width = display.lineNumWidth + "px";
619
+ updateGutterSpace(cm);
620
+ return true;
621
+ }
622
+ return false;
623
+ }
624
+
625
+ function lineNumberFor(options, i) {
626
+ return String(options.lineNumberFormatter(i + options.firstLineNumber));
627
+ }
628
+
629
+ // Computes display.scroller.scrollLeft + display.gutters.offsetWidth,
630
+ // but using getBoundingClientRect to get a sub-pixel-accurate
631
+ // result.
632
+ function compensateForHScroll(display) {
633
+ return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left;
634
+ }
635
+
636
+ // DISPLAY DRAWING
637
+
638
+ function DisplayUpdate(cm, viewport, force) {
639
+ var display = cm.display;
640
+
641
+ this.viewport = viewport;
642
+ // Store some values that we'll need later (but don't want to force a relayout for)
643
+ this.visible = visibleLines(display, cm.doc, viewport);
644
+ this.editorIsHidden = !display.wrapper.offsetWidth;
645
+ this.wrapperHeight = display.wrapper.clientHeight;
646
+ this.wrapperWidth = display.wrapper.clientWidth;
647
+ this.oldDisplayWidth = displayWidth(cm);
648
+ this.force = force;
649
+ this.dims = getDimensions(cm);
650
+ this.events = [];
651
+ }
652
+
653
+ DisplayUpdate.prototype.signal = function(emitter, type) {
654
+ if (hasHandler(emitter, type))
655
+ this.events.push(arguments);
656
+ };
657
+ DisplayUpdate.prototype.finish = function() {
658
+ for (var i = 0; i < this.events.length; i++)
659
+ signal.apply(null, this.events[i]);
660
+ };
661
+
662
+ function maybeClipScrollbars(cm) {
663
+ var display = cm.display;
664
+ if (!display.scrollbarsClipped && display.scroller.offsetWidth) {
665
+ display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.clientWidth;
666
+ display.heightForcer.style.height = scrollGap(cm) + "px";
667
+ display.sizer.style.marginBottom = -display.nativeBarWidth + "px";
668
+ display.sizer.style.borderRightWidth = scrollGap(cm) + "px";
669
+ display.scrollbarsClipped = true;
670
+ }
671
+ }
672
+
673
+ // Does the actual updating of the line display. Bails out
674
+ // (returning false) when there is nothing to be done and forced is
675
+ // false.
676
+ function updateDisplayIfNeeded(cm, update) {
677
+ var display = cm.display, doc = cm.doc;
678
+
679
+ if (update.editorIsHidden) {
680
+ resetView(cm);
681
+ return false;
682
+ }
683
+
684
+ // Bail out if the visible area is already rendered and nothing changed.
685
+ if (!update.force &&
686
+ update.visible.from >= display.viewFrom && update.visible.to <= display.viewTo &&
687
+ (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo) &&
688
+ display.renderedView == display.view && countDirtyView(cm) == 0)
689
+ return false;
690
+
691
+ if (maybeUpdateLineNumberWidth(cm)) {
692
+ resetView(cm);
693
+ update.dims = getDimensions(cm);
694
+ }
695
+
696
+ // Compute a suitable new viewport (from & to)
697
+ var end = doc.first + doc.size;
698
+ var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first);
699
+ var to = Math.min(end, update.visible.to + cm.options.viewportMargin);
700
+ if (display.viewFrom < from && from - display.viewFrom < 20) from = Math.max(doc.first, display.viewFrom);
701
+ if (display.viewTo > to && display.viewTo - to < 20) to = Math.min(end, display.viewTo);
702
+ if (sawCollapsedSpans) {
703
+ from = visualLineNo(cm.doc, from);
704
+ to = visualLineEndNo(cm.doc, to);
705
+ }
706
+
707
+ var different = from != display.viewFrom || to != display.viewTo ||
708
+ display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != update.wrapperWidth;
709
+ adjustView(cm, from, to);
710
+
711
+ display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom));
712
+ // Position the mover div to align with the current scroll position
713
+ cm.display.mover.style.top = display.viewOffset + "px";
714
+
715
+ var toUpdate = countDirtyView(cm);
716
+ if (!different && toUpdate == 0 && !update.force && display.renderedView == display.view &&
717
+ (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo))
718
+ return false;
719
+
720
+ // For big changes, we hide the enclosing element during the
721
+ // update, since that speeds up the operations on most browsers.
722
+ var focused = activeElt();
723
+ if (toUpdate > 4) display.lineDiv.style.display = "none";
724
+ patchDisplay(cm, display.updateLineNumbers, update.dims);
725
+ if (toUpdate > 4) display.lineDiv.style.display = "";
726
+ display.renderedView = display.view;
727
+ // There might have been a widget with a focused element that got
728
+ // hidden or updated, if so re-focus it.
729
+ if (focused && activeElt() != focused && focused.offsetHeight) focused.focus();
730
+
731
+ // Prevent selection and cursors from interfering with the scroll
732
+ // width and height.
733
+ removeChildren(display.cursorDiv);
734
+ removeChildren(display.selectionDiv);
735
+ display.gutters.style.height = display.sizer.style.minHeight = 0;
736
+
737
+ if (different) {
738
+ display.lastWrapHeight = update.wrapperHeight;
739
+ display.lastWrapWidth = update.wrapperWidth;
740
+ startWorker(cm, 400);
741
+ }
742
+
743
+ display.updateLineNumbers = null;
744
+
745
+ return true;
746
+ }
747
+
748
+ function postUpdateDisplay(cm, update) {
749
+ var viewport = update.viewport;
750
+ for (var first = true;; first = false) {
751
+ if (!first || !cm.options.lineWrapping || update.oldDisplayWidth == displayWidth(cm)) {
752
+ // Clip forced viewport to actual scrollable area.
753
+ if (viewport && viewport.top != null)
754
+ viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - displayHeight(cm), viewport.top)};
755
+ // Updated line heights might result in the drawn area not
756
+ // actually covering the viewport. Keep looping until it does.
757
+ update.visible = visibleLines(cm.display, cm.doc, viewport);
758
+ if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo)
759
+ break;
760
+ }
761
+ if (!updateDisplayIfNeeded(cm, update)) break;
762
+ updateHeightsInViewport(cm);
763
+ var barMeasure = measureForScrollbars(cm);
764
+ updateSelection(cm);
765
+ setDocumentHeight(cm, barMeasure);
766
+ updateScrollbars(cm, barMeasure);
767
+ }
768
+
769
+ update.signal(cm, "update", cm);
770
+ if (cm.display.viewFrom != cm.display.reportedViewFrom || cm.display.viewTo != cm.display.reportedViewTo) {
771
+ update.signal(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo);
772
+ cm.display.reportedViewFrom = cm.display.viewFrom; cm.display.reportedViewTo = cm.display.viewTo;
773
+ }
774
+ }
775
+
776
+ function updateDisplaySimple(cm, viewport) {
777
+ var update = new DisplayUpdate(cm, viewport);
778
+ if (updateDisplayIfNeeded(cm, update)) {
779
+ updateHeightsInViewport(cm);
780
+ postUpdateDisplay(cm, update);
781
+ var barMeasure = measureForScrollbars(cm);
782
+ updateSelection(cm);
783
+ setDocumentHeight(cm, barMeasure);
784
+ updateScrollbars(cm, barMeasure);
785
+ update.finish();
786
+ }
787
+ }
788
+
789
+ function setDocumentHeight(cm, measure) {
790
+ cm.display.sizer.style.minHeight = measure.docHeight + "px";
791
+ cm.display.heightForcer.style.top = measure.docHeight + "px";
792
+ cm.display.gutters.style.height = Math.max(measure.docHeight + cm.display.barHeight + scrollGap(cm),
793
+ measure.clientHeight) + "px";
794
+ }
795
+
796
+ // Read the actual heights of the rendered lines, and update their
797
+ // stored heights to match.
798
+ function updateHeightsInViewport(cm) {
799
+ var display = cm.display;
800
+ var prevBottom = display.lineDiv.offsetTop;
801
+ for (var i = 0; i < display.view.length; i++) {
802
+ var cur = display.view[i], height;
803
+ if (cur.hidden) continue;
804
+ if (ie && ie_version < 8) {
805
+ var bot = cur.node.offsetTop + cur.node.offsetHeight;
806
+ height = bot - prevBottom;
807
+ prevBottom = bot;
808
+ } else {
809
+ var box = cur.node.getBoundingClientRect();
810
+ height = box.bottom - box.top;
811
+ }
812
+ var diff = cur.line.height - height;
813
+ if (height < 2) height = textHeight(display);
814
+ if (diff > .001 || diff < -.001) {
815
+ updateLineHeight(cur.line, height);
816
+ updateWidgetHeight(cur.line);
817
+ if (cur.rest) for (var j = 0; j < cur.rest.length; j++)
818
+ updateWidgetHeight(cur.rest[j]);
819
+ }
820
+ }
821
+ }
822
+
823
+ // Read and store the height of line widgets associated with the
824
+ // given line.
825
+ function updateWidgetHeight(line) {
826
+ if (line.widgets) for (var i = 0; i < line.widgets.length; ++i)
827
+ line.widgets[i].height = line.widgets[i].node.parentNode.offsetHeight;
828
+ }
829
+
830
+ // Do a bulk-read of the DOM positions and sizes needed to draw the
831
+ // view, so that we don't interleave reading and writing to the DOM.
832
+ function getDimensions(cm) {
833
+ var d = cm.display, left = {}, width = {};
834
+ var gutterLeft = d.gutters.clientLeft;
835
+ for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) {
836
+ left[cm.options.gutters[i]] = n.offsetLeft + n.clientLeft + gutterLeft;
837
+ width[cm.options.gutters[i]] = n.clientWidth;
838
+ }
839
+ return {fixedPos: compensateForHScroll(d),
840
+ gutterTotalWidth: d.gutters.offsetWidth,
841
+ gutterLeft: left,
842
+ gutterWidth: width,
843
+ wrapperWidth: d.wrapper.clientWidth};
844
+ }
845
+
846
+ // Sync the actual display DOM structure with display.view, removing
847
+ // nodes for lines that are no longer in view, and creating the ones
848
+ // that are not there yet, and updating the ones that are out of
849
+ // date.
850
+ function patchDisplay(cm, updateNumbersFrom, dims) {
851
+ var display = cm.display, lineNumbers = cm.options.lineNumbers;
852
+ var container = display.lineDiv, cur = container.firstChild;
853
+
854
+ function rm(node) {
855
+ var next = node.nextSibling;
856
+ // Works around a throw-scroll bug in OS X Webkit
857
+ if (webkit && mac && cm.display.currentWheelTarget == node)
858
+ node.style.display = "none";
859
+ else
860
+ node.parentNode.removeChild(node);
861
+ return next;
862
+ }
863
+
864
+ var view = display.view, lineN = display.viewFrom;
865
+ // Loop over the elements in the view, syncing cur (the DOM nodes
866
+ // in display.lineDiv) with the view as we go.
867
+ for (var i = 0; i < view.length; i++) {
868
+ var lineView = view[i];
869
+ if (lineView.hidden) {
870
+ } else if (!lineView.node || lineView.node.parentNode != container) { // Not drawn yet
871
+ var node = buildLineElement(cm, lineView, lineN, dims);
872
+ container.insertBefore(node, cur);
873
+ } else { // Already drawn
874
+ while (cur != lineView.node) cur = rm(cur);
875
+ var updateNumber = lineNumbers && updateNumbersFrom != null &&
876
+ updateNumbersFrom <= lineN && lineView.lineNumber;
877
+ if (lineView.changes) {
878
+ if (indexOf(lineView.changes, "gutter") > -1) updateNumber = false;
879
+ updateLineForChanges(cm, lineView, lineN, dims);
880
+ }
881
+ if (updateNumber) {
882
+ removeChildren(lineView.lineNumber);
883
+ lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm.options, lineN)));
884
+ }
885
+ cur = lineView.node.nextSibling;
886
+ }
887
+ lineN += lineView.size;
888
+ }
889
+ while (cur) cur = rm(cur);
890
+ }
891
+
892
+ // When an aspect of a line changes, a string is added to
893
+ // lineView.changes. This updates the relevant part of the line's
894
+ // DOM structure.
895
+ function updateLineForChanges(cm, lineView, lineN, dims) {
896
+ for (var j = 0; j < lineView.changes.length; j++) {
897
+ var type = lineView.changes[j];
898
+ if (type == "text") updateLineText(cm, lineView);
899
+ else if (type == "gutter") updateLineGutter(cm, lineView, lineN, dims);
900
+ else if (type == "class") updateLineClasses(lineView);
901
+ else if (type == "widget") updateLineWidgets(cm, lineView, dims);
902
+ }
903
+ lineView.changes = null;
904
+ }
905
+
906
+ // Lines with gutter elements, widgets or a background class need to
907
+ // be wrapped, and have the extra elements added to the wrapper div
908
+ function ensureLineWrapped(lineView) {
909
+ if (lineView.node == lineView.text) {
910
+ lineView.node = elt("div", null, null, "position: relative");
911
+ if (lineView.text.parentNode)
912
+ lineView.text.parentNode.replaceChild(lineView.node, lineView.text);
913
+ lineView.node.appendChild(lineView.text);
914
+ if (ie && ie_version < 8) lineView.node.style.zIndex = 2;
915
+ }
916
+ return lineView.node;
917
+ }
918
+
919
+ function updateLineBackground(lineView) {
920
+ var cls = lineView.bgClass ? lineView.bgClass + " " + (lineView.line.bgClass || "") : lineView.line.bgClass;
921
+ if (cls) cls += " CodeMirror-linebackground";
922
+ if (lineView.background) {
923
+ if (cls) lineView.background.className = cls;
924
+ else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null; }
925
+ } else if (cls) {
926
+ var wrap = ensureLineWrapped(lineView);
927
+ lineView.background = wrap.insertBefore(elt("div", null, cls), wrap.firstChild);
928
+ }
929
+ }
930
+
931
+ // Wrapper around buildLineContent which will reuse the structure
932
+ // in display.externalMeasured when possible.
933
+ function getLineContent(cm, lineView) {
934
+ var ext = cm.display.externalMeasured;
935
+ if (ext && ext.line == lineView.line) {
936
+ cm.display.externalMeasured = null;
937
+ lineView.measure = ext.measure;
938
+ return ext.built;
939
+ }
940
+ return buildLineContent(cm, lineView);
941
+ }
942
+
943
+ // Redraw the line's text. Interacts with the background and text
944
+ // classes because the mode may output tokens that influence these
945
+ // classes.
946
+ function updateLineText(cm, lineView) {
947
+ var cls = lineView.text.className;
948
+ var built = getLineContent(cm, lineView);
949
+ if (lineView.text == lineView.node) lineView.node = built.pre;
950
+ lineView.text.parentNode.replaceChild(built.pre, lineView.text);
951
+ lineView.text = built.pre;
952
+ if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) {
953
+ lineView.bgClass = built.bgClass;
954
+ lineView.textClass = built.textClass;
955
+ updateLineClasses(lineView);
956
+ } else if (cls) {
957
+ lineView.text.className = cls;
958
+ }
959
+ }
960
+
961
+ function updateLineClasses(lineView) {
962
+ updateLineBackground(lineView);
963
+ if (lineView.line.wrapClass)
964
+ ensureLineWrapped(lineView).className = lineView.line.wrapClass;
965
+ else if (lineView.node != lineView.text)
966
+ lineView.node.className = "";
967
+ var textClass = lineView.textClass ? lineView.textClass + " " + (lineView.line.textClass || "") : lineView.line.textClass;
968
+ lineView.text.className = textClass || "";
969
+ }
970
+
971
+ function updateLineGutter(cm, lineView, lineN, dims) {
972
+ if (lineView.gutter) {
973
+ lineView.node.removeChild(lineView.gutter);
974
+ lineView.gutter = null;
975
+ }
976
+ if (lineView.gutterBackground) {
977
+ lineView.node.removeChild(lineView.gutterBackground);
978
+ lineView.gutterBackground = null;
979
+ }
980
+ if (lineView.line.gutterClass) {
981
+ var wrap = ensureLineWrapped(lineView);
982
+ lineView.gutterBackground = elt("div", null, "CodeMirror-gutter-background " + lineView.line.gutterClass,
983
+ "left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) +
984
+ "px; width: " + dims.gutterTotalWidth + "px");
985
+ wrap.insertBefore(lineView.gutterBackground, lineView.text);
986
+ }
987
+ var markers = lineView.line.gutterMarkers;
988
+ if (cm.options.lineNumbers || markers) {
989
+ var wrap = ensureLineWrapped(lineView);
990
+ var gutterWrap = lineView.gutter = elt("div", null, "CodeMirror-gutter-wrapper", "left: " +
991
+ (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px");
992
+ cm.display.input.setUneditable(gutterWrap);
993
+ wrap.insertBefore(gutterWrap, lineView.text);
994
+ if (lineView.line.gutterClass)
995
+ gutterWrap.className += " " + lineView.line.gutterClass;
996
+ if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"]))
997
+ lineView.lineNumber = gutterWrap.appendChild(
998
+ elt("div", lineNumberFor(cm.options, lineN),
999
+ "CodeMirror-linenumber CodeMirror-gutter-elt",
1000
+ "left: " + dims.gutterLeft["CodeMirror-linenumbers"] + "px; width: "
1001
+ + cm.display.lineNumInnerWidth + "px"));
1002
+ if (markers) for (var k = 0; k < cm.options.gutters.length; ++k) {
1003
+ var id = cm.options.gutters[k], found = markers.hasOwnProperty(id) && markers[id];
1004
+ if (found)
1005
+ gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt", "left: " +
1006
+ dims.gutterLeft[id] + "px; width: " + dims.gutterWidth[id] + "px"));
1007
+ }
1008
+ }
1009
+ }
1010
+
1011
+ function updateLineWidgets(cm, lineView, dims) {
1012
+ if (lineView.alignable) lineView.alignable = null;
1013
+ for (var node = lineView.node.firstChild, next; node; node = next) {
1014
+ var next = node.nextSibling;
1015
+ if (node.className == "CodeMirror-linewidget")
1016
+ lineView.node.removeChild(node);
1017
+ }
1018
+ insertLineWidgets(cm, lineView, dims);
1019
+ }
1020
+
1021
+ // Build a line's DOM representation from scratch
1022
+ function buildLineElement(cm, lineView, lineN, dims) {
1023
+ var built = getLineContent(cm, lineView);
1024
+ lineView.text = lineView.node = built.pre;
1025
+ if (built.bgClass) lineView.bgClass = built.bgClass;
1026
+ if (built.textClass) lineView.textClass = built.textClass;
1027
+
1028
+ updateLineClasses(lineView);
1029
+ updateLineGutter(cm, lineView, lineN, dims);
1030
+ insertLineWidgets(cm, lineView, dims);
1031
+ return lineView.node;
1032
+ }
1033
+
1034
+ // A lineView may contain multiple logical lines (when merged by
1035
+ // collapsed spans). The widgets for all of them need to be drawn.
1036
+ function insertLineWidgets(cm, lineView, dims) {
1037
+ insertLineWidgetsFor(cm, lineView.line, lineView, dims, true);
1038
+ if (lineView.rest) for (var i = 0; i < lineView.rest.length; i++)
1039
+ insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false);
1040
+ }
1041
+
1042
+ function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) {
1043
+ if (!line.widgets) return;
1044
+ var wrap = ensureLineWrapped(lineView);
1045
+ for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
1046
+ var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget");
1047
+ if (!widget.handleMouseEvents) node.setAttribute("cm-ignore-events", "true");
1048
+ positionLineWidget(widget, node, lineView, dims);
1049
+ cm.display.input.setUneditable(node);
1050
+ if (allowAbove && widget.above)
1051
+ wrap.insertBefore(node, lineView.gutter || lineView.text);
1052
+ else
1053
+ wrap.appendChild(node);
1054
+ signalLater(widget, "redraw");
1055
+ }
1056
+ }
1057
+
1058
+ function positionLineWidget(widget, node, lineView, dims) {
1059
+ if (widget.noHScroll) {
1060
+ (lineView.alignable || (lineView.alignable = [])).push(node);
1061
+ var width = dims.wrapperWidth;
1062
+ node.style.left = dims.fixedPos + "px";
1063
+ if (!widget.coverGutter) {
1064
+ width -= dims.gutterTotalWidth;
1065
+ node.style.paddingLeft = dims.gutterTotalWidth + "px";
1066
+ }
1067
+ node.style.width = width + "px";
1068
+ }
1069
+ if (widget.coverGutter) {
1070
+ node.style.zIndex = 5;
1071
+ node.style.position = "relative";
1072
+ if (!widget.noHScroll) node.style.marginLeft = -dims.gutterTotalWidth + "px";
1073
+ }
1074
+ }
1075
+
1076
+ // POSITION OBJECT
1077
+
1078
+ // A Pos instance represents a position within the text.
1079
+ var Pos = CodeMirror.Pos = function(line, ch) {
1080
+ if (!(this instanceof Pos)) return new Pos(line, ch);
1081
+ this.line = line; this.ch = ch;
1082
+ };
1083
+
1084
+ // Compare two positions, return 0 if they are the same, a negative
1085
+ // number when a is less, and a positive number otherwise.
1086
+ var cmp = CodeMirror.cmpPos = function(a, b) { return a.line - b.line || a.ch - b.ch; };
1087
+
1088
+ function copyPos(x) {return Pos(x.line, x.ch);}
1089
+ function maxPos(a, b) { return cmp(a, b) < 0 ? b : a; }
1090
+ function minPos(a, b) { return cmp(a, b) < 0 ? a : b; }
1091
+
1092
+ // INPUT HANDLING
1093
+
1094
+ function ensureFocus(cm) {
1095
+ if (!cm.state.focused) { cm.display.input.focus(); onFocus(cm); }
1096
+ }
1097
+
1098
+ // This will be set to an array of strings when copying, so that,
1099
+ // when pasting, we know what kind of selections the copied text
1100
+ // was made out of.
1101
+ var lastCopied = null;
1102
+
1103
+ function applyTextInput(cm, inserted, deleted, sel, origin) {
1104
+ var doc = cm.doc;
1105
+ cm.display.shift = false;
1106
+ if (!sel) sel = doc.sel;
1107
+
1108
+ var paste = cm.state.pasteIncoming || origin == "paste";
1109
+ var textLines = doc.splitLines(inserted), multiPaste = null;
1110
+ // When pasing N lines into N selections, insert one line per selection
1111
+ if (paste && sel.ranges.length > 1) {
1112
+ if (lastCopied && lastCopied.join("\n") == inserted) {
1113
+ if (sel.ranges.length % lastCopied.length == 0) {
1114
+ multiPaste = [];
1115
+ for (var i = 0; i < lastCopied.length; i++)
1116
+ multiPaste.push(doc.splitLines(lastCopied[i]));
1117
+ }
1118
+ } else if (textLines.length == sel.ranges.length) {
1119
+ multiPaste = map(textLines, function(l) { return [l]; });
1120
+ }
1121
+ }
1122
+
1123
+ // Normal behavior is to insert the new text into every selection
1124
+ for (var i = sel.ranges.length - 1; i >= 0; i--) {
1125
+ var range = sel.ranges[i];
1126
+ var from = range.from(), to = range.to();
1127
+ if (range.empty()) {
1128
+ if (deleted && deleted > 0) // Handle deletion
1129
+ from = Pos(from.line, from.ch - deleted);
1130
+ else if (cm.state.overwrite && !paste) // Handle overwrite
1131
+ to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length));
1132
+ }
1133
+ var updateInput = cm.curOp.updateInput;
1134
+ var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i % multiPaste.length] : textLines,
1135
+ origin: origin || (paste ? "paste" : cm.state.cutIncoming ? "cut" : "+input")};
1136
+ makeChange(cm.doc, changeEvent);
1137
+ signalLater(cm, "inputRead", cm, changeEvent);
1138
+ }
1139
+ if (inserted && !paste)
1140
+ triggerElectric(cm, inserted);
1141
+
1142
+ ensureCursorVisible(cm);
1143
+ cm.curOp.updateInput = updateInput;
1144
+ cm.curOp.typing = true;
1145
+ cm.state.pasteIncoming = cm.state.cutIncoming = false;
1146
+ }
1147
+
1148
+ function handlePaste(e, cm) {
1149
+ var pasted = e.clipboardData && e.clipboardData.getData("text/plain");
1150
+ if (pasted) {
1151
+ e.preventDefault();
1152
+ if (!cm.isReadOnly() && !cm.options.disableInput)
1153
+ runInOp(cm, function() { applyTextInput(cm, pasted, 0, null, "paste"); });
1154
+ return true;
1155
+ }
1156
+ }
1157
+
1158
+ function triggerElectric(cm, inserted) {
1159
+ // When an 'electric' character is inserted, immediately trigger a reindent
1160
+ if (!cm.options.electricChars || !cm.options.smartIndent) return;
1161
+ var sel = cm.doc.sel;
1162
+
1163
+ for (var i = sel.ranges.length - 1; i >= 0; i--) {
1164
+ var range = sel.ranges[i];
1165
+ if (range.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range.head.line)) continue;
1166
+ var mode = cm.getModeAt(range.head);
1167
+ var indented = false;
1168
+ if (mode.electricChars) {
1169
+ for (var j = 0; j < mode.electricChars.length; j++)
1170
+ if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) {
1171
+ indented = indentLine(cm, range.head.line, "smart");
1172
+ break;
1173
+ }
1174
+ } else if (mode.electricInput) {
1175
+ if (mode.electricInput.test(getLine(cm.doc, range.head.line).text.slice(0, range.head.ch)))
1176
+ indented = indentLine(cm, range.head.line, "smart");
1177
+ }
1178
+ if (indented) signalLater(cm, "electricInput", cm, range.head.line);
1179
+ }
1180
+ }
1181
+
1182
+ function copyableRanges(cm) {
1183
+ var text = [], ranges = [];
1184
+ for (var i = 0; i < cm.doc.sel.ranges.length; i++) {
1185
+ var line = cm.doc.sel.ranges[i].head.line;
1186
+ var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)};
1187
+ ranges.push(lineRange);
1188
+ text.push(cm.getRange(lineRange.anchor, lineRange.head));
1189
+ }
1190
+ return {text: text, ranges: ranges};
1191
+ }
1192
+
1193
+ function disableBrowserMagic(field) {
1194
+ field.setAttribute("autocorrect", "off");
1195
+ field.setAttribute("autocapitalize", "off");
1196
+ field.setAttribute("spellcheck", "false");
1197
+ }
1198
+
1199
+ // TEXTAREA INPUT STYLE
1200
+
1201
+ function TextareaInput(cm) {
1202
+ this.cm = cm;
1203
+ // See input.poll and input.reset
1204
+ this.prevInput = "";
1205
+
1206
+ // Flag that indicates whether we expect input to appear real soon
1207
+ // now (after some event like 'keypress' or 'input') and are
1208
+ // polling intensively.
1209
+ this.pollingFast = false;
1210
+ // Self-resetting timeout for the poller
1211
+ this.polling = new Delayed();
1212
+ // Tracks when input.reset has punted to just putting a short
1213
+ // string into the textarea instead of the full selection.
1214
+ this.inaccurateSelection = false;
1215
+ // Used to work around IE issue with selection being forgotten when focus moves away from textarea
1216
+ this.hasSelection = false;
1217
+ this.composing = null;
1218
+ };
1219
+
1220
+ function hiddenTextarea() {
1221
+ var te = elt("textarea", null, null, "position: absolute; padding: 0; width: 1px; height: 1em; outline: none");
1222
+ var div = elt("div", [te], null, "overflow: hidden; position: relative; width: 3px; height: 0px;");
1223
+ // The textarea is kept positioned near the cursor to prevent the
1224
+ // fact that it'll be scrolled into view on input from scrolling
1225
+ // our fake cursor out of view. On webkit, when wrap=off, paste is
1226
+ // very slow. So make the area wide instead.
1227
+ if (webkit) te.style.width = "1000px";
1228
+ else te.setAttribute("wrap", "off");
1229
+ // If border: 0; -- iOS fails to open keyboard (issue #1287)
1230
+ if (ios) te.style.border = "1px solid black";
1231
+ disableBrowserMagic(te);
1232
+ return div;
1233
+ }
1234
+
1235
+ TextareaInput.prototype = copyObj({
1236
+ init: function(display) {
1237
+ var input = this, cm = this.cm;
1238
+
1239
+ // Wraps and hides input textarea
1240
+ var div = this.wrapper = hiddenTextarea();
1241
+ // The semihidden textarea that is focused when the editor is
1242
+ // focused, and receives input.
1243
+ var te = this.textarea = div.firstChild;
1244
+ display.wrapper.insertBefore(div, display.wrapper.firstChild);
1245
+
1246
+ // Needed to hide big blue blinking cursor on Mobile Safari (doesn't seem to work in iOS 8 anymore)
1247
+ if (ios) te.style.width = "0px";
1248
+
1249
+ on(te, "input", function() {
1250
+ if (ie && ie_version >= 9 && input.hasSelection) input.hasSelection = null;
1251
+ input.poll();
1252
+ });
1253
+
1254
+ on(te, "paste", function(e) {
1255
+ if (signalDOMEvent(cm, e) || handlePaste(e, cm)) return
1256
+
1257
+ cm.state.pasteIncoming = true;
1258
+ input.fastPoll();
1259
+ });
1260
+
1261
+ function prepareCopyCut(e) {
1262
+ if (signalDOMEvent(cm, e)) return
1263
+ if (cm.somethingSelected()) {
1264
+ lastCopied = cm.getSelections();
1265
+ if (input.inaccurateSelection) {
1266
+ input.prevInput = "";
1267
+ input.inaccurateSelection = false;
1268
+ te.value = lastCopied.join("\n");
1269
+ selectInput(te);
1270
+ }
1271
+ } else if (!cm.options.lineWiseCopyCut) {
1272
+ return;
1273
+ } else {
1274
+ var ranges = copyableRanges(cm);
1275
+ lastCopied = ranges.text;
1276
+ if (e.type == "cut") {
1277
+ cm.setSelections(ranges.ranges, null, sel_dontScroll);
1278
+ } else {
1279
+ input.prevInput = "";
1280
+ te.value = ranges.text.join("\n");
1281
+ selectInput(te);
1282
+ }
1283
+ }
1284
+ if (e.type == "cut") cm.state.cutIncoming = true;
1285
+ }
1286
+ on(te, "cut", prepareCopyCut);
1287
+ on(te, "copy", prepareCopyCut);
1288
+
1289
+ on(display.scroller, "paste", function(e) {
1290
+ if (eventInWidget(display, e) || signalDOMEvent(cm, e)) return;
1291
+ cm.state.pasteIncoming = true;
1292
+ input.focus();
1293
+ });
1294
+
1295
+ // Prevent normal selection in the editor (we handle our own)
1296
+ on(display.lineSpace, "selectstart", function(e) {
1297
+ if (!eventInWidget(display, e)) e_preventDefault(e);
1298
+ });
1299
+
1300
+ on(te, "compositionstart", function() {
1301
+ var start = cm.getCursor("from");
1302
+ if (input.composing) input.composing.range.clear()
1303
+ input.composing = {
1304
+ start: start,
1305
+ range: cm.markText(start, cm.getCursor("to"), {className: "CodeMirror-composing"})
1306
+ };
1307
+ });
1308
+ on(te, "compositionend", function() {
1309
+ if (input.composing) {
1310
+ input.poll();
1311
+ input.composing.range.clear();
1312
+ input.composing = null;
1313
+ }
1314
+ });
1315
+ },
1316
+
1317
+ prepareSelection: function() {
1318
+ // Redraw the selection and/or cursor
1319
+ var cm = this.cm, display = cm.display, doc = cm.doc;
1320
+ var result = prepareSelection(cm);
1321
+
1322
+ // Move the hidden textarea near the cursor to prevent scrolling artifacts
1323
+ if (cm.options.moveInputWithCursor) {
1324
+ var headPos = cursorCoords(cm, doc.sel.primary().head, "div");
1325
+ var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect();
1326
+ result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
1327
+ headPos.top + lineOff.top - wrapOff.top));
1328
+ result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
1329
+ headPos.left + lineOff.left - wrapOff.left));
1330
+ }
1331
+
1332
+ return result;
1333
+ },
1334
+
1335
+ showSelection: function(drawn) {
1336
+ var cm = this.cm, display = cm.display;
1337
+ removeChildrenAndAdd(display.cursorDiv, drawn.cursors);
1338
+ removeChildrenAndAdd(display.selectionDiv, drawn.selection);
1339
+ if (drawn.teTop != null) {
1340
+ this.wrapper.style.top = drawn.teTop + "px";
1341
+ this.wrapper.style.left = drawn.teLeft + "px";
1342
+ }
1343
+ },
1344
+
1345
+ // Reset the input to correspond to the selection (or to be empty,
1346
+ // when not typing and nothing is selected)
1347
+ reset: function(typing) {
1348
+ if (this.contextMenuPending) return;
1349
+ var minimal, selected, cm = this.cm, doc = cm.doc;
1350
+ if (cm.somethingSelected()) {
1351
+ this.prevInput = "";
1352
+ var range = doc.sel.primary();
1353
+ minimal = hasCopyEvent &&
1354
+ (range.to().line - range.from().line > 100 || (selected = cm.getSelection()).length > 1000);
1355
+ var content = minimal ? "-" : selected || cm.getSelection();
1356
+ this.textarea.value = content;
1357
+ if (cm.state.focused) selectInput(this.textarea);
1358
+ if (ie && ie_version >= 9) this.hasSelection = content;
1359
+ } else if (!typing) {
1360
+ this.prevInput = this.textarea.value = "";
1361
+ if (ie && ie_version >= 9) this.hasSelection = null;
1362
+ }
1363
+ this.inaccurateSelection = minimal;
1364
+ },
1365
+
1366
+ getField: function() { return this.textarea; },
1367
+
1368
+ supportsTouch: function() { return false; },
1369
+
1370
+ focus: function() {
1371
+ if (this.cm.options.readOnly != "nocursor" && (!mobile || activeElt() != this.textarea)) {
1372
+ try { this.textarea.focus(); }
1373
+ catch (e) {} // IE8 will throw if the textarea is display: none or not in DOM
1374
+ }
1375
+ },
1376
+
1377
+ blur: function() { this.textarea.blur(); },
1378
+
1379
+ resetPosition: function() {
1380
+ this.wrapper.style.top = this.wrapper.style.left = 0;
1381
+ },
1382
+
1383
+ receivedFocus: function() { this.slowPoll(); },
1384
+
1385
+ // Poll for input changes, using the normal rate of polling. This
1386
+ // runs as long as the editor is focused.
1387
+ slowPoll: function() {
1388
+ var input = this;
1389
+ if (input.pollingFast) return;
1390
+ input.polling.set(this.cm.options.pollInterval, function() {
1391
+ input.poll();
1392
+ if (input.cm.state.focused) input.slowPoll();
1393
+ });
1394
+ },
1395
+
1396
+ // When an event has just come in that is likely to add or change
1397
+ // something in the input textarea, we poll faster, to ensure that
1398
+ // the change appears on the screen quickly.
1399
+ fastPoll: function() {
1400
+ var missed = false, input = this;
1401
+ input.pollingFast = true;
1402
+ function p() {
1403
+ var changed = input.poll();
1404
+ if (!changed && !missed) {missed = true; input.polling.set(60, p);}
1405
+ else {input.pollingFast = false; input.slowPoll();}
1406
+ }
1407
+ input.polling.set(20, p);
1408
+ },
1409
+
1410
+ // Read input from the textarea, and update the document to match.
1411
+ // When something is selected, it is present in the textarea, and
1412
+ // selected (unless it is huge, in which case a placeholder is
1413
+ // used). When nothing is selected, the cursor sits after previously
1414
+ // seen text (can be empty), which is stored in prevInput (we must
1415
+ // not reset the textarea when typing, because that breaks IME).
1416
+ poll: function() {
1417
+ var cm = this.cm, input = this.textarea, prevInput = this.prevInput;
1418
+ // Since this is called a *lot*, try to bail out as cheaply as
1419
+ // possible when it is clear that nothing happened. hasSelection
1420
+ // will be the case when there is a lot of text in the textarea,
1421
+ // in which case reading its value would be expensive.
1422
+ if (this.contextMenuPending || !cm.state.focused ||
1423
+ (hasSelection(input) && !prevInput && !this.composing) ||
1424
+ cm.isReadOnly() || cm.options.disableInput || cm.state.keySeq)
1425
+ return false;
1426
+
1427
+ var text = input.value;
1428
+ // If nothing changed, bail.
1429
+ if (text == prevInput && !cm.somethingSelected()) return false;
1430
+ // Work around nonsensical selection resetting in IE9/10, and
1431
+ // inexplicable appearance of private area unicode characters on
1432
+ // some key combos in Mac (#2689).
1433
+ if (ie && ie_version >= 9 && this.hasSelection === text ||
1434
+ mac && /[\uf700-\uf7ff]/.test(text)) {
1435
+ cm.display.input.reset();
1436
+ return false;
1437
+ }
1438
+
1439
+ if (cm.doc.sel == cm.display.selForContextMenu) {
1440
+ var first = text.charCodeAt(0);
1441
+ if (first == 0x200b && !prevInput) prevInput = "\u200b";
1442
+ if (first == 0x21da) { this.reset(); return this.cm.execCommand("undo"); }
1443
+ }
1444
+ // Find the part of the input that is actually new
1445
+ var same = 0, l = Math.min(prevInput.length, text.length);
1446
+ while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) ++same;
1447
+
1448
+ var self = this;
1449
+ runInOp(cm, function() {
1450
+ applyTextInput(cm, text.slice(same), prevInput.length - same,
1451
+ null, self.composing ? "*compose" : null);
1452
+
1453
+ // Don't leave long text in the textarea, since it makes further polling slow
1454
+ if (text.length > 1000 || text.indexOf("\n") > -1) input.value = self.prevInput = "";
1455
+ else self.prevInput = text;
1456
+
1457
+ if (self.composing) {
1458
+ self.composing.range.clear();
1459
+ self.composing.range = cm.markText(self.composing.start, cm.getCursor("to"),
1460
+ {className: "CodeMirror-composing"});
1461
+ }
1462
+ });
1463
+ return true;
1464
+ },
1465
+
1466
+ ensurePolled: function() {
1467
+ if (this.pollingFast && this.poll()) this.pollingFast = false;
1468
+ },
1469
+
1470
+ onKeyPress: function() {
1471
+ if (ie && ie_version >= 9) this.hasSelection = null;
1472
+ this.fastPoll();
1473
+ },
1474
+
1475
+ onContextMenu: function(e) {
1476
+ var input = this, cm = input.cm, display = cm.display, te = input.textarea;
1477
+ var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop;
1478
+ if (!pos || presto) return; // Opera is difficult.
1479
+
1480
+ // Reset the current text selection only if the click is done outside of the selection
1481
+ // and 'resetSelectionOnContextMenu' option is true.
1482
+ var reset = cm.options.resetSelectionOnContextMenu;
1483
+ if (reset && cm.doc.sel.contains(pos) == -1)
1484
+ operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll);
1485
+
1486
+ var oldCSS = te.style.cssText, oldWrapperCSS = input.wrapper.style.cssText;
1487
+ input.wrapper.style.cssText = "position: absolute"
1488
+ var wrapperBox = input.wrapper.getBoundingClientRect()
1489
+ te.style.cssText = "position: absolute; width: 30px; height: 30px; top: " + (e.clientY - wrapperBox.top - 5) +
1490
+ "px; left: " + (e.clientX - wrapperBox.left - 5) + "px; z-index: 1000; background: " +
1491
+ (ie ? "rgba(255, 255, 255, .05)" : "transparent") +
1492
+ "; outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);";
1493
+ if (webkit) var oldScrollY = window.scrollY; // Work around Chrome issue (#2712)
1494
+ display.input.focus();
1495
+ if (webkit) window.scrollTo(null, oldScrollY);
1496
+ display.input.reset();
1497
+ // Adds "Select all" to context menu in FF
1498
+ if (!cm.somethingSelected()) te.value = input.prevInput = " ";
1499
+ input.contextMenuPending = true;
1500
+ display.selForContextMenu = cm.doc.sel;
1501
+ clearTimeout(display.detectingSelectAll);
1502
+
1503
+ // Select-all will be greyed out if there's nothing to select, so
1504
+ // this adds a zero-width space so that we can later check whether
1505
+ // it got selected.
1506
+ function prepareSelectAllHack() {
1507
+ if (te.selectionStart != null) {
1508
+ var selected = cm.somethingSelected();
1509
+ var extval = "\u200b" + (selected ? te.value : "");
1510
+ te.value = "\u21da"; // Used to catch context-menu undo
1511
+ te.value = extval;
1512
+ input.prevInput = selected ? "" : "\u200b";
1513
+ te.selectionStart = 1; te.selectionEnd = extval.length;
1514
+ // Re-set this, in case some other handler touched the
1515
+ // selection in the meantime.
1516
+ display.selForContextMenu = cm.doc.sel;
1517
+ }
1518
+ }
1519
+ function rehide() {
1520
+ input.contextMenuPending = false;
1521
+ input.wrapper.style.cssText = oldWrapperCSS
1522
+ te.style.cssText = oldCSS;
1523
+ if (ie && ie_version < 9) display.scrollbars.setScrollTop(display.scroller.scrollTop = scrollPos);
1524
+
1525
+ // Try to detect the user choosing select-all
1526
+ if (te.selectionStart != null) {
1527
+ if (!ie || (ie && ie_version < 9)) prepareSelectAllHack();
1528
+ var i = 0, poll = function() {
1529
+ if (display.selForContextMenu == cm.doc.sel && te.selectionStart == 0 &&
1530
+ te.selectionEnd > 0 && input.prevInput == "\u200b")
1531
+ operation(cm, commands.selectAll)(cm);
1532
+ else if (i++ < 10) display.detectingSelectAll = setTimeout(poll, 500);
1533
+ else display.input.reset();
1534
+ };
1535
+ display.detectingSelectAll = setTimeout(poll, 200);
1536
+ }
1537
+ }
1538
+
1539
+ if (ie && ie_version >= 9) prepareSelectAllHack();
1540
+ if (captureRightClick) {
1541
+ e_stop(e);
1542
+ var mouseup = function() {
1543
+ off(window, "mouseup", mouseup);
1544
+ setTimeout(rehide, 20);
1545
+ };
1546
+ on(window, "mouseup", mouseup);
1547
+ } else {
1548
+ setTimeout(rehide, 50);
1549
+ }
1550
+ },
1551
+
1552
+ readOnlyChanged: function(val) {
1553
+ if (!val) this.reset();
1554
+ },
1555
+
1556
+ setUneditable: nothing,
1557
+
1558
+ needsContentAttribute: false
1559
+ }, TextareaInput.prototype);
1560
+
1561
+ // CONTENTEDITABLE INPUT STYLE
1562
+
1563
+ function ContentEditableInput(cm) {
1564
+ this.cm = cm;
1565
+ this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null;
1566
+ this.polling = new Delayed();
1567
+ this.gracePeriod = false;
1568
+ }
1569
+
1570
+ ContentEditableInput.prototype = copyObj({
1571
+ init: function(display) {
1572
+ var input = this, cm = input.cm;
1573
+ var div = input.div = display.lineDiv;
1574
+ disableBrowserMagic(div);
1575
+
1576
+ on(div, "paste", function(e) {
1577
+ if (!signalDOMEvent(cm, e)) handlePaste(e, cm);
1578
+ })
1579
+
1580
+ on(div, "compositionstart", function(e) {
1581
+ var data = e.data;
1582
+ input.composing = {sel: cm.doc.sel, data: data, startData: data};
1583
+ if (!data) return;
1584
+ var prim = cm.doc.sel.primary();
1585
+ var line = cm.getLine(prim.head.line);
1586
+ var found = line.indexOf(data, Math.max(0, prim.head.ch - data.length));
1587
+ if (found > -1 && found <= prim.head.ch)
1588
+ input.composing.sel = simpleSelection(Pos(prim.head.line, found),
1589
+ Pos(prim.head.line, found + data.length));
1590
+ });
1591
+ on(div, "compositionupdate", function(e) {
1592
+ input.composing.data = e.data;
1593
+ });
1594
+ on(div, "compositionend", function(e) {
1595
+ var ours = input.composing;
1596
+ if (!ours) return;
1597
+ if (e.data != ours.startData && !/\u200b/.test(e.data))
1598
+ ours.data = e.data;
1599
+ // Need a small delay to prevent other code (input event,
1600
+ // selection polling) from doing damage when fired right after
1601
+ // compositionend.
1602
+ setTimeout(function() {
1603
+ if (!ours.handled)
1604
+ input.applyComposition(ours);
1605
+ if (input.composing == ours)
1606
+ input.composing = null;
1607
+ }, 50);
1608
+ });
1609
+
1610
+ on(div, "touchstart", function() {
1611
+ input.forceCompositionEnd();
1612
+ });
1613
+
1614
+ on(div, "input", function() {
1615
+ if (input.composing) return;
1616
+ if (cm.isReadOnly() || !input.pollContent())
1617
+ runInOp(input.cm, function() {regChange(cm);});
1618
+ });
1619
+
1620
+ function onCopyCut(e) {
1621
+ if (signalDOMEvent(cm, e)) return
1622
+ if (cm.somethingSelected()) {
1623
+ lastCopied = cm.getSelections();
1624
+ if (e.type == "cut") cm.replaceSelection("", null, "cut");
1625
+ } else if (!cm.options.lineWiseCopyCut) {
1626
+ return;
1627
+ } else {
1628
+ var ranges = copyableRanges(cm);
1629
+ lastCopied = ranges.text;
1630
+ if (e.type == "cut") {
1631
+ cm.operation(function() {
1632
+ cm.setSelections(ranges.ranges, 0, sel_dontScroll);
1633
+ cm.replaceSelection("", null, "cut");
1634
+ });
1635
+ }
1636
+ }
1637
+ // iOS exposes the clipboard API, but seems to discard content inserted into it
1638
+ if (e.clipboardData && !ios) {
1639
+ e.preventDefault();
1640
+ e.clipboardData.clearData();
1641
+ e.clipboardData.setData("text/plain", lastCopied.join("\n"));
1642
+ } else {
1643
+ // Old-fashioned briefly-focus-a-textarea hack
1644
+ var kludge = hiddenTextarea(), te = kludge.firstChild;
1645
+ cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild);
1646
+ te.value = lastCopied.join("\n");
1647
+ var hadFocus = document.activeElement;
1648
+ selectInput(te);
1649
+ setTimeout(function() {
1650
+ cm.display.lineSpace.removeChild(kludge);
1651
+ hadFocus.focus();
1652
+ }, 50);
1653
+ }
1654
+ }
1655
+ on(div, "copy", onCopyCut);
1656
+ on(div, "cut", onCopyCut);
1657
+ },
1658
+
1659
+ prepareSelection: function() {
1660
+ var result = prepareSelection(this.cm, false);
1661
+ result.focus = this.cm.state.focused;
1662
+ return result;
1663
+ },
1664
+
1665
+ showSelection: function(info) {
1666
+ if (!info || !this.cm.display.view.length) return;
1667
+ if (info.focus) this.showPrimarySelection();
1668
+ this.showMultipleSelections(info);
1669
+ },
1670
+
1671
+ showPrimarySelection: function() {
1672
+ var sel = window.getSelection(), prim = this.cm.doc.sel.primary();
1673
+ var curAnchor = domToPos(this.cm, sel.anchorNode, sel.anchorOffset);
1674
+ var curFocus = domToPos(this.cm, sel.focusNode, sel.focusOffset);
1675
+ if (curAnchor && !curAnchor.bad && curFocus && !curFocus.bad &&
1676
+ cmp(minPos(curAnchor, curFocus), prim.from()) == 0 &&
1677
+ cmp(maxPos(curAnchor, curFocus), prim.to()) == 0)
1678
+ return;
1679
+
1680
+ var start = posToDOM(this.cm, prim.from());
1681
+ var end = posToDOM(this.cm, prim.to());
1682
+ if (!start && !end) return;
1683
+
1684
+ var view = this.cm.display.view;
1685
+ var old = sel.rangeCount && sel.getRangeAt(0);
1686
+ if (!start) {
1687
+ start = {node: view[0].measure.map[2], offset: 0};
1688
+ } else if (!end) { // FIXME dangerously hacky
1689
+ var measure = view[view.length - 1].measure;
1690
+ var map = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map;
1691
+ end = {node: map[map.length - 1], offset: map[map.length - 2] - map[map.length - 3]};
1692
+ }
1693
+
1694
+ try { var rng = range(start.node, start.offset, end.offset, end.node); }
1695
+ catch(e) {} // Our model of the DOM might be outdated, in which case the range we try to set can be impossible
1696
+ if (rng) {
1697
+ if (!gecko && this.cm.state.focused) {
1698
+ sel.collapse(start.node, start.offset);
1699
+ if (!rng.collapsed) sel.addRange(rng);
1700
+ } else {
1701
+ sel.removeAllRanges();
1702
+ sel.addRange(rng);
1703
+ }
1704
+ if (old && sel.anchorNode == null) sel.addRange(old);
1705
+ else if (gecko) this.startGracePeriod();
1706
+ }
1707
+ this.rememberSelection();
1708
+ },
1709
+
1710
+ startGracePeriod: function() {
1711
+ var input = this;
1712
+ clearTimeout(this.gracePeriod);
1713
+ this.gracePeriod = setTimeout(function() {
1714
+ input.gracePeriod = false;
1715
+ if (input.selectionChanged())
1716
+ input.cm.operation(function() { input.cm.curOp.selectionChanged = true; });
1717
+ }, 20);
1718
+ },
1719
+
1720
+ showMultipleSelections: function(info) {
1721
+ removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors);
1722
+ removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection);
1723
+ },
1724
+
1725
+ rememberSelection: function() {
1726
+ var sel = window.getSelection();
1727
+ this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset;
1728
+ this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset;
1729
+ },
1730
+
1731
+ selectionInEditor: function() {
1732
+ var sel = window.getSelection();
1733
+ if (!sel.rangeCount) return false;
1734
+ var node = sel.getRangeAt(0).commonAncestorContainer;
1735
+ return contains(this.div, node);
1736
+ },
1737
+
1738
+ focus: function() {
1739
+ if (this.cm.options.readOnly != "nocursor") this.div.focus();
1740
+ },
1741
+ blur: function() { this.div.blur(); },
1742
+ getField: function() { return this.div; },
1743
+
1744
+ supportsTouch: function() { return true; },
1745
+
1746
+ receivedFocus: function() {
1747
+ var input = this;
1748
+ if (this.selectionInEditor())
1749
+ this.pollSelection();
1750
+ else
1751
+ runInOp(this.cm, function() { input.cm.curOp.selectionChanged = true; });
1752
+
1753
+ function poll() {
1754
+ if (input.cm.state.focused) {
1755
+ input.pollSelection();
1756
+ input.polling.set(input.cm.options.pollInterval, poll);
1757
+ }
1758
+ }
1759
+ this.polling.set(this.cm.options.pollInterval, poll);
1760
+ },
1761
+
1762
+ selectionChanged: function() {
1763
+ var sel = window.getSelection();
1764
+ return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset ||
1765
+ sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset;
1766
+ },
1767
+
1768
+ pollSelection: function() {
1769
+ if (!this.composing && !this.gracePeriod && this.selectionChanged()) {
1770
+ var sel = window.getSelection(), cm = this.cm;
1771
+ this.rememberSelection();
1772
+ var anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset);
1773
+ var head = domToPos(cm, sel.focusNode, sel.focusOffset);
1774
+ if (anchor && head) runInOp(cm, function() {
1775
+ setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll);
1776
+ if (anchor.bad || head.bad) cm.curOp.selectionChanged = true;
1777
+ });
1778
+ }
1779
+ },
1780
+
1781
+ pollContent: function() {
1782
+ var cm = this.cm, display = cm.display, sel = cm.doc.sel.primary();
1783
+ var from = sel.from(), to = sel.to();
1784
+ if (from.line < display.viewFrom || to.line > display.viewTo - 1) return false;
1785
+
1786
+ var fromIndex;
1787
+ if (from.line == display.viewFrom || (fromIndex = findViewIndex(cm, from.line)) == 0) {
1788
+ var fromLine = lineNo(display.view[0].line);
1789
+ var fromNode = display.view[0].node;
1790
+ } else {
1791
+ var fromLine = lineNo(display.view[fromIndex].line);
1792
+ var fromNode = display.view[fromIndex - 1].node.nextSibling;
1793
+ }
1794
+ var toIndex = findViewIndex(cm, to.line);
1795
+ if (toIndex == display.view.length - 1) {
1796
+ var toLine = display.viewTo - 1;
1797
+ var toNode = display.lineDiv.lastChild;
1798
+ } else {
1799
+ var toLine = lineNo(display.view[toIndex + 1].line) - 1;
1800
+ var toNode = display.view[toIndex + 1].node.previousSibling;
1801
+ }
1802
+
1803
+ var newText = cm.doc.splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine));
1804
+ var oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm.doc, toLine).text.length));
1805
+ while (newText.length > 1 && oldText.length > 1) {
1806
+ if (lst(newText) == lst(oldText)) { newText.pop(); oldText.pop(); toLine--; }
1807
+ else if (newText[0] == oldText[0]) { newText.shift(); oldText.shift(); fromLine++; }
1808
+ else break;
1809
+ }
1810
+
1811
+ var cutFront = 0, cutEnd = 0;
1812
+ var newTop = newText[0], oldTop = oldText[0], maxCutFront = Math.min(newTop.length, oldTop.length);
1813
+ while (cutFront < maxCutFront && newTop.charCodeAt(cutFront) == oldTop.charCodeAt(cutFront))
1814
+ ++cutFront;
1815
+ var newBot = lst(newText), oldBot = lst(oldText);
1816
+ var maxCutEnd = Math.min(newBot.length - (newText.length == 1 ? cutFront : 0),
1817
+ oldBot.length - (oldText.length == 1 ? cutFront : 0));
1818
+ while (cutEnd < maxCutEnd &&
1819
+ newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1))
1820
+ ++cutEnd;
1821
+
1822
+ newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd);
1823
+ newText[0] = newText[0].slice(cutFront);
1824
+
1825
+ var chFrom = Pos(fromLine, cutFront);
1826
+ var chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0);
1827
+ if (newText.length > 1 || newText[0] || cmp(chFrom, chTo)) {
1828
+ replaceRange(cm.doc, newText, chFrom, chTo, "+input");
1829
+ return true;
1830
+ }
1831
+ },
1832
+
1833
+ ensurePolled: function() {
1834
+ this.forceCompositionEnd();
1835
+ },
1836
+ reset: function() {
1837
+ this.forceCompositionEnd();
1838
+ },
1839
+ forceCompositionEnd: function() {
1840
+ if (!this.composing || this.composing.handled) return;
1841
+ this.applyComposition(this.composing);
1842
+ this.composing.handled = true;
1843
+ this.div.blur();
1844
+ this.div.focus();
1845
+ },
1846
+ applyComposition: function(composing) {
1847
+ if (this.cm.isReadOnly())
1848
+ operation(this.cm, regChange)(this.cm)
1849
+ else if (composing.data && composing.data != composing.startData)
1850
+ operation(this.cm, applyTextInput)(this.cm, composing.data, 0, composing.sel);
1851
+ },
1852
+
1853
+ setUneditable: function(node) {
1854
+ node.contentEditable = "false"
1855
+ },
1856
+
1857
+ onKeyPress: function(e) {
1858
+ e.preventDefault();
1859
+ if (!this.cm.isReadOnly())
1860
+ operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode), 0);
1861
+ },
1862
+
1863
+ readOnlyChanged: function(val) {
1864
+ this.div.contentEditable = String(val != "nocursor")
1865
+ },
1866
+
1867
+ onContextMenu: nothing,
1868
+ resetPosition: nothing,
1869
+
1870
+ needsContentAttribute: true
1871
+ }, ContentEditableInput.prototype);
1872
+
1873
+ function posToDOM(cm, pos) {
1874
+ var view = findViewForLine(cm, pos.line);
1875
+ if (!view || view.hidden) return null;
1876
+ var line = getLine(cm.doc, pos.line);
1877
+ var info = mapFromLineView(view, line, pos.line);
1878
+
1879
+ var order = getOrder(line), side = "left";
1880
+ if (order) {
1881
+ var partPos = getBidiPartAt(order, pos.ch);
1882
+ side = partPos % 2 ? "right" : "left";
1883
+ }
1884
+ var result = nodeAndOffsetInLineMap(info.map, pos.ch, side);
1885
+ result.offset = result.collapse == "right" ? result.end : result.start;
1886
+ return result;
1887
+ }
1888
+
1889
+ function badPos(pos, bad) { if (bad) pos.bad = true; return pos; }
1890
+
1891
+ function domToPos(cm, node, offset) {
1892
+ var lineNode;
1893
+ if (node == cm.display.lineDiv) {
1894
+ lineNode = cm.display.lineDiv.childNodes[offset];
1895
+ if (!lineNode) return badPos(cm.clipPos(Pos(cm.display.viewTo - 1)), true);
1896
+ node = null; offset = 0;
1897
+ } else {
1898
+ for (lineNode = node;; lineNode = lineNode.parentNode) {
1899
+ if (!lineNode || lineNode == cm.display.lineDiv) return null;
1900
+ if (lineNode.parentNode && lineNode.parentNode == cm.display.lineDiv) break;
1901
+ }
1902
+ }
1903
+ for (var i = 0; i < cm.display.view.length; i++) {
1904
+ var lineView = cm.display.view[i];
1905
+ if (lineView.node == lineNode)
1906
+ return locateNodeInLineView(lineView, node, offset);
1907
+ }
1908
+ }
1909
+
1910
+ function locateNodeInLineView(lineView, node, offset) {
1911
+ var wrapper = lineView.text.firstChild, bad = false;
1912
+ if (!node || !contains(wrapper, node)) return badPos(Pos(lineNo(lineView.line), 0), true);
1913
+ if (node == wrapper) {
1914
+ bad = true;
1915
+ node = wrapper.childNodes[offset];
1916
+ offset = 0;
1917
+ if (!node) {
1918
+ var line = lineView.rest ? lst(lineView.rest) : lineView.line;
1919
+ return badPos(Pos(lineNo(line), line.text.length), bad);
1920
+ }
1921
+ }
1922
+
1923
+ var textNode = node.nodeType == 3 ? node : null, topNode = node;
1924
+ if (!textNode && node.childNodes.length == 1 && node.firstChild.nodeType == 3) {
1925
+ textNode = node.firstChild;
1926
+ if (offset) offset = textNode.nodeValue.length;
1927
+ }
1928
+ while (topNode.parentNode != wrapper) topNode = topNode.parentNode;
1929
+ var measure = lineView.measure, maps = measure.maps;
1930
+
1931
+ function find(textNode, topNode, offset) {
1932
+ for (var i = -1; i < (maps ? maps.length : 0); i++) {
1933
+ var map = i < 0 ? measure.map : maps[i];
1934
+ for (var j = 0; j < map.length; j += 3) {
1935
+ var curNode = map[j + 2];
1936
+ if (curNode == textNode || curNode == topNode) {
1937
+ var line = lineNo(i < 0 ? lineView.line : lineView.rest[i]);
1938
+ var ch = map[j] + offset;
1939
+ if (offset < 0 || curNode != textNode) ch = map[j + (offset ? 1 : 0)];
1940
+ return Pos(line, ch);
1941
+ }
1942
+ }
1943
+ }
1944
+ }
1945
+ var found = find(textNode, topNode, offset);
1946
+ if (found) return badPos(found, bad);
1947
+
1948
+ // FIXME this is all really shaky. might handle the few cases it needs to handle, but likely to cause problems
1949
+ for (var after = topNode.nextSibling, dist = textNode ? textNode.nodeValue.length - offset : 0; after; after = after.nextSibling) {
1950
+ found = find(after, after.firstChild, 0);
1951
+ if (found)
1952
+ return badPos(Pos(found.line, found.ch - dist), bad);
1953
+ else
1954
+ dist += after.textContent.length;
1955
+ }
1956
+ for (var before = topNode.previousSibling, dist = offset; before; before = before.previousSibling) {
1957
+ found = find(before, before.firstChild, -1);
1958
+ if (found)
1959
+ return badPos(Pos(found.line, found.ch + dist), bad);
1960
+ else
1961
+ dist += after.textContent.length;
1962
+ }
1963
+ }
1964
+
1965
+ function domTextBetween(cm, from, to, fromLine, toLine) {
1966
+ var text = "", closing = false, lineSep = cm.doc.lineSeparator();
1967
+ function recognizeMarker(id) { return function(marker) { return marker.id == id; }; }
1968
+ function walk(node) {
1969
+ if (node.nodeType == 1) {
1970
+ var cmText = node.getAttribute("cm-text");
1971
+ if (cmText != null) {
1972
+ if (cmText == "") cmText = node.textContent.replace(/\u200b/g, "");
1973
+ text += cmText;
1974
+ return;
1975
+ }
1976
+ var markerID = node.getAttribute("cm-marker"), range;
1977
+ if (markerID) {
1978
+ var found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recognizeMarker(+markerID));
1979
+ if (found.length && (range = found[0].find()))
1980
+ text += getBetween(cm.doc, range.from, range.to).join(lineSep);
1981
+ return;
1982
+ }
1983
+ if (node.getAttribute("contenteditable") == "false") return;
1984
+ for (var i = 0; i < node.childNodes.length; i++)
1985
+ walk(node.childNodes[i]);
1986
+ if (/^(pre|div|p)$/i.test(node.nodeName))
1987
+ closing = true;
1988
+ } else if (node.nodeType == 3) {
1989
+ var val = node.nodeValue;
1990
+ if (!val) return;
1991
+ if (closing) {
1992
+ text += lineSep;
1993
+ closing = false;
1994
+ }
1995
+ text += val;
1996
+ }
1997
+ }
1998
+ for (;;) {
1999
+ walk(from);
2000
+ if (from == to) break;
2001
+ from = from.nextSibling;
2002
+ }
2003
+ return text;
2004
+ }
2005
+
2006
+ CodeMirror.inputStyles = {"textarea": TextareaInput, "contenteditable": ContentEditableInput};
2007
+
2008
+ // SELECTION / CURSOR
2009
+
2010
+ // Selection objects are immutable. A new one is created every time
2011
+ // the selection changes. A selection is one or more non-overlapping
2012
+ // (and non-touching) ranges, sorted, and an integer that indicates
2013
+ // which one is the primary selection (the one that's scrolled into
2014
+ // view, that getCursor returns, etc).
2015
+ function Selection(ranges, primIndex) {
2016
+ this.ranges = ranges;
2017
+ this.primIndex = primIndex;
2018
+ }
2019
+
2020
+ Selection.prototype = {
2021
+ primary: function() { return this.ranges[this.primIndex]; },
2022
+ equals: function(other) {
2023
+ if (other == this) return true;
2024
+ if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) return false;
2025
+ for (var i = 0; i < this.ranges.length; i++) {
2026
+ var here = this.ranges[i], there = other.ranges[i];
2027
+ if (cmp(here.anchor, there.anchor) != 0 || cmp(here.head, there.head) != 0) return false;
2028
+ }
2029
+ return true;
2030
+ },
2031
+ deepCopy: function() {
2032
+ for (var out = [], i = 0; i < this.ranges.length; i++)
2033
+ out[i] = new Range(copyPos(this.ranges[i].anchor), copyPos(this.ranges[i].head));
2034
+ return new Selection(out, this.primIndex);
2035
+ },
2036
+ somethingSelected: function() {
2037
+ for (var i = 0; i < this.ranges.length; i++)
2038
+ if (!this.ranges[i].empty()) return true;
2039
+ return false;
2040
+ },
2041
+ contains: function(pos, end) {
2042
+ if (!end) end = pos;
2043
+ for (var i = 0; i < this.ranges.length; i++) {
2044
+ var range = this.ranges[i];
2045
+ if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0)
2046
+ return i;
2047
+ }
2048
+ return -1;
2049
+ }
2050
+ };
2051
+
2052
+ function Range(anchor, head) {
2053
+ this.anchor = anchor; this.head = head;
2054
+ }
2055
+
2056
+ Range.prototype = {
2057
+ from: function() { return minPos(this.anchor, this.head); },
2058
+ to: function() { return maxPos(this.anchor, this.head); },
2059
+ empty: function() {
2060
+ return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch;
2061
+ }
2062
+ };
2063
+
2064
+ // Take an unsorted, potentially overlapping set of ranges, and
2065
+ // build a selection out of it. 'Consumes' ranges array (modifying
2066
+ // it).
2067
+ function normalizeSelection(ranges, primIndex) {
2068
+ var prim = ranges[primIndex];
2069
+ ranges.sort(function(a, b) { return cmp(a.from(), b.from()); });
2070
+ primIndex = indexOf(ranges, prim);
2071
+ for (var i = 1; i < ranges.length; i++) {
2072
+ var cur = ranges[i], prev = ranges[i - 1];
2073
+ if (cmp(prev.to(), cur.from()) >= 0) {
2074
+ var from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to());
2075
+ var inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head;
2076
+ if (i <= primIndex) --primIndex;
2077
+ ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to));
2078
+ }
2079
+ }
2080
+ return new Selection(ranges, primIndex);
2081
+ }
2082
+
2083
+ function simpleSelection(anchor, head) {
2084
+ return new Selection([new Range(anchor, head || anchor)], 0);
2085
+ }
2086
+
2087
+ // Most of the external API clips given positions to make sure they
2088
+ // actually exist within the document.
2089
+ function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1));}
2090
+ function clipPos(doc, pos) {
2091
+ if (pos.line < doc.first) return Pos(doc.first, 0);
2092
+ var last = doc.first + doc.size - 1;
2093
+ if (pos.line > last) return Pos(last, getLine(doc, last).text.length);
2094
+ return clipToLen(pos, getLine(doc, pos.line).text.length);
2095
+ }
2096
+ function clipToLen(pos, linelen) {
2097
+ var ch = pos.ch;
2098
+ if (ch == null || ch > linelen) return Pos(pos.line, linelen);
2099
+ else if (ch < 0) return Pos(pos.line, 0);
2100
+ else return pos;
2101
+ }
2102
+ function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size;}
2103
+ function clipPosArray(doc, array) {
2104
+ for (var out = [], i = 0; i < array.length; i++) out[i] = clipPos(doc, array[i]);
2105
+ return out;
2106
+ }
2107
+
2108
+ // SELECTION UPDATES
2109
+
2110
+ // The 'scroll' parameter given to many of these indicated whether
2111
+ // the new cursor position should be scrolled into view after
2112
+ // modifying the selection.
2113
+
2114
+ // If shift is held or the extend flag is set, extends a range to
2115
+ // include a given position (and optionally a second position).
2116
+ // Otherwise, simply returns the range between the given positions.
2117
+ // Used for cursor motion and such.
2118
+ function extendRange(doc, range, head, other) {
2119
+ if (doc.cm && doc.cm.display.shift || doc.extend) {
2120
+ var anchor = range.anchor;
2121
+ if (other) {
2122
+ var posBefore = cmp(head, anchor) < 0;
2123
+ if (posBefore != (cmp(other, anchor) < 0)) {
2124
+ anchor = head;
2125
+ head = other;
2126
+ } else if (posBefore != (cmp(head, other) < 0)) {
2127
+ head = other;
2128
+ }
2129
+ }
2130
+ return new Range(anchor, head);
2131
+ } else {
2132
+ return new Range(other || head, head);
2133
+ }
2134
+ }
2135
+
2136
+ // Extend the primary selection range, discard the rest.
2137
+ function extendSelection(doc, head, other, options) {
2138
+ setSelection(doc, new Selection([extendRange(doc, doc.sel.primary(), head, other)], 0), options);
2139
+ }
2140
+
2141
+ // Extend all selections (pos is an array of selections with length
2142
+ // equal the number of selections)
2143
+ function extendSelections(doc, heads, options) {
2144
+ for (var out = [], i = 0; i < doc.sel.ranges.length; i++)
2145
+ out[i] = extendRange(doc, doc.sel.ranges[i], heads[i], null);
2146
+ var newSel = normalizeSelection(out, doc.sel.primIndex);
2147
+ setSelection(doc, newSel, options);
2148
+ }
2149
+
2150
+ // Updates a single range in the selection.
2151
+ function replaceOneSelection(doc, i, range, options) {
2152
+ var ranges = doc.sel.ranges.slice(0);
2153
+ ranges[i] = range;
2154
+ setSelection(doc, normalizeSelection(ranges, doc.sel.primIndex), options);
2155
+ }
2156
+
2157
+ // Reset the selection to a single range.
2158
+ function setSimpleSelection(doc, anchor, head, options) {
2159
+ setSelection(doc, simpleSelection(anchor, head), options);
2160
+ }
2161
+
2162
+ // Give beforeSelectionChange handlers a change to influence a
2163
+ // selection update.
2164
+ function filterSelectionChange(doc, sel, options) {
2165
+ var obj = {
2166
+ ranges: sel.ranges,
2167
+ update: function(ranges) {
2168
+ this.ranges = [];
2169
+ for (var i = 0; i < ranges.length; i++)
2170
+ this.ranges[i] = new Range(clipPos(doc, ranges[i].anchor),
2171
+ clipPos(doc, ranges[i].head));
2172
+ },
2173
+ origin: options && options.origin
2174
+ };
2175
+ signal(doc, "beforeSelectionChange", doc, obj);
2176
+ if (doc.cm) signal(doc.cm, "beforeSelectionChange", doc.cm, obj);
2177
+ if (obj.ranges != sel.ranges) return normalizeSelection(obj.ranges, obj.ranges.length - 1);
2178
+ else return sel;
2179
+ }
2180
+
2181
+ function setSelectionReplaceHistory(doc, sel, options) {
2182
+ var done = doc.history.done, last = lst(done);
2183
+ if (last && last.ranges) {
2184
+ done[done.length - 1] = sel;
2185
+ setSelectionNoUndo(doc, sel, options);
2186
+ } else {
2187
+ setSelection(doc, sel, options);
2188
+ }
2189
+ }
2190
+
2191
+ // Set a new selection.
2192
+ function setSelection(doc, sel, options) {
2193
+ setSelectionNoUndo(doc, sel, options);
2194
+ addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options);
2195
+ }
2196
+
2197
+ function setSelectionNoUndo(doc, sel, options) {
2198
+ if (hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange"))
2199
+ sel = filterSelectionChange(doc, sel, options);
2200
+
2201
+ var bias = options && options.bias ||
2202
+ (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1);
2203
+ setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true));
2204
+
2205
+ if (!(options && options.scroll === false) && doc.cm)
2206
+ ensureCursorVisible(doc.cm);
2207
+ }
2208
+
2209
+ function setSelectionInner(doc, sel) {
2210
+ if (sel.equals(doc.sel)) return;
2211
+
2212
+ doc.sel = sel;
2213
+
2214
+ if (doc.cm) {
2215
+ doc.cm.curOp.updateInput = doc.cm.curOp.selectionChanged = true;
2216
+ signalCursorActivity(doc.cm);
2217
+ }
2218
+ signalLater(doc, "cursorActivity", doc);
2219
+ }
2220
+
2221
+ // Verify that the selection does not partially select any atomic
2222
+ // marked ranges.
2223
+ function reCheckSelection(doc) {
2224
+ setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false), sel_dontScroll);
2225
+ }
2226
+
2227
+ // Return a selection that does not partially select any atomic
2228
+ // ranges.
2229
+ function skipAtomicInSelection(doc, sel, bias, mayClear) {
2230
+ var out;
2231
+ for (var i = 0; i < sel.ranges.length; i++) {
2232
+ var range = sel.ranges[i];
2233
+ var old = sel.ranges.length == doc.sel.ranges.length && doc.sel.ranges[i];
2234
+ var newAnchor = skipAtomic(doc, range.anchor, old && old.anchor, bias, mayClear);
2235
+ var newHead = skipAtomic(doc, range.head, old && old.head, bias, mayClear);
2236
+ if (out || newAnchor != range.anchor || newHead != range.head) {
2237
+ if (!out) out = sel.ranges.slice(0, i);
2238
+ out[i] = new Range(newAnchor, newHead);
2239
+ }
2240
+ }
2241
+ return out ? normalizeSelection(out, sel.primIndex) : sel;
2242
+ }
2243
+
2244
+ function skipAtomicInner(doc, pos, oldPos, dir, mayClear) {
2245
+ var line = getLine(doc, pos.line);
2246
+ if (line.markedSpans) for (var i = 0; i < line.markedSpans.length; ++i) {
2247
+ var sp = line.markedSpans[i], m = sp.marker;
2248
+ if ((sp.from == null || (m.inclusiveLeft ? sp.from <= pos.ch : sp.from < pos.ch)) &&
2249
+ (sp.to == null || (m.inclusiveRight ? sp.to >= pos.ch : sp.to > pos.ch))) {
2250
+ if (mayClear) {
2251
+ signal(m, "beforeCursorEnter");
2252
+ if (m.explicitlyCleared) {
2253
+ if (!line.markedSpans) break;
2254
+ else {--i; continue;}
2255
+ }
2256
+ }
2257
+ if (!m.atomic) continue;
2258
+
2259
+ if (oldPos) {
2260
+ var near = m.find(dir < 0 ? 1 : -1), diff;
2261
+ if (dir < 0 ? m.inclusiveRight : m.inclusiveLeft) near = movePos(doc, near, -dir, line);
2262
+ if (near && near.line == pos.line && (diff = cmp(near, oldPos)) && (dir < 0 ? diff < 0 : diff > 0))
2263
+ return skipAtomicInner(doc, near, pos, dir, mayClear);
2264
+ }
2265
+
2266
+ var far = m.find(dir < 0 ? -1 : 1);
2267
+ if (dir < 0 ? m.inclusiveLeft : m.inclusiveRight) far = movePos(doc, far, dir, line);
2268
+ return far ? skipAtomicInner(doc, far, pos, dir, mayClear) : null;
2269
+ }
2270
+ }
2271
+ return pos;
2272
+ }
2273
+
2274
+ // Ensure a given position is not inside an atomic range.
2275
+ function skipAtomic(doc, pos, oldPos, bias, mayClear) {
2276
+ var dir = bias || 1;
2277
+ var found = skipAtomicInner(doc, pos, oldPos, dir, mayClear) ||
2278
+ (!mayClear && skipAtomicInner(doc, pos, oldPos, dir, true)) ||
2279
+ skipAtomicInner(doc, pos, oldPos, -dir, mayClear) ||
2280
+ (!mayClear && skipAtomicInner(doc, pos, oldPos, -dir, true));
2281
+ if (!found) {
2282
+ doc.cantEdit = true;
2283
+ return Pos(doc.first, 0);
2284
+ }
2285
+ return found;
2286
+ }
2287
+
2288
+ function movePos(doc, pos, dir, line) {
2289
+ if (dir < 0 && pos.ch == 0) {
2290
+ if (pos.line > doc.first) return clipPos(doc, Pos(pos.line - 1));
2291
+ else return null;
2292
+ } else if (dir > 0 && pos.ch == (line || getLine(doc, pos.line)).text.length) {
2293
+ if (pos.line < doc.first + doc.size - 1) return Pos(pos.line + 1, 0);
2294
+ else return null;
2295
+ } else {
2296
+ return new Pos(pos.line, pos.ch + dir);
2297
+ }
2298
+ }
2299
+
2300
+ // SELECTION DRAWING
2301
+
2302
+ function updateSelection(cm) {
2303
+ cm.display.input.showSelection(cm.display.input.prepareSelection());
2304
+ }
2305
+
2306
+ function prepareSelection(cm, primary) {
2307
+ var doc = cm.doc, result = {};
2308
+ var curFragment = result.cursors = document.createDocumentFragment();
2309
+ var selFragment = result.selection = document.createDocumentFragment();
2310
+
2311
+ for (var i = 0; i < doc.sel.ranges.length; i++) {
2312
+ if (primary === false && i == doc.sel.primIndex) continue;
2313
+ var range = doc.sel.ranges[i];
2314
+ var collapsed = range.empty();
2315
+ if (collapsed || cm.options.showCursorWhenSelecting)
2316
+ drawSelectionCursor(cm, range.head, curFragment);
2317
+ if (!collapsed)
2318
+ drawSelectionRange(cm, range, selFragment);
2319
+ }
2320
+ return result;
2321
+ }
2322
+
2323
+ // Draws a cursor for the given range
2324
+ function drawSelectionCursor(cm, head, output) {
2325
+ var pos = cursorCoords(cm, head, "div", null, null, !cm.options.singleCursorHeightPerLine);
2326
+
2327
+ var cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor"));
2328
+ cursor.style.left = pos.left + "px";
2329
+ cursor.style.top = pos.top + "px";
2330
+ cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px";
2331
+
2332
+ if (pos.other) {
2333
+ // Secondary cursor, shown when on a 'jump' in bi-directional text
2334
+ var otherCursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor"));
2335
+ otherCursor.style.display = "";
2336
+ otherCursor.style.left = pos.other.left + "px";
2337
+ otherCursor.style.top = pos.other.top + "px";
2338
+ otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px";
2339
+ }
2340
+ }
2341
+
2342
+ // Draws the given range as a highlighted selection
2343
+ function drawSelectionRange(cm, range, output) {
2344
+ var display = cm.display, doc = cm.doc;
2345
+ var fragment = document.createDocumentFragment();
2346
+ var padding = paddingH(cm.display), leftSide = padding.left;
2347
+ var rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right;
2348
+
2349
+ function add(left, top, width, bottom) {
2350
+ if (top < 0) top = 0;
2351
+ top = Math.round(top);
2352
+ bottom = Math.round(bottom);
2353
+ fragment.appendChild(elt("div", null, "CodeMirror-selected", "position: absolute; left: " + left +
2354
+ "px; top: " + top + "px; width: " + (width == null ? rightSide - left : width) +
2355
+ "px; height: " + (bottom - top) + "px"));
2356
+ }
2357
+
2358
+ function drawForLine(line, fromArg, toArg) {
2359
+ var lineObj = getLine(doc, line);
2360
+ var lineLen = lineObj.text.length;
2361
+ var start, end;
2362
+ function coords(ch, bias) {
2363
+ return charCoords(cm, Pos(line, ch), "div", lineObj, bias);
2364
+ }
2365
+
2366
+ iterateBidiSections(getOrder(lineObj), fromArg || 0, toArg == null ? lineLen : toArg, function(from, to, dir) {
2367
+ var leftPos = coords(from, "left"), rightPos, left, right;
2368
+ if (from == to) {
2369
+ rightPos = leftPos;
2370
+ left = right = leftPos.left;
2371
+ } else {
2372
+ rightPos = coords(to - 1, "right");
2373
+ if (dir == "rtl") { var tmp = leftPos; leftPos = rightPos; rightPos = tmp; }
2374
+ left = leftPos.left;
2375
+ right = rightPos.right;
2376
+ }
2377
+ if (fromArg == null && from == 0) left = leftSide;
2378
+ if (rightPos.top - leftPos.top > 3) { // Different lines, draw top part
2379
+ add(left, leftPos.top, null, leftPos.bottom);
2380
+ left = leftSide;
2381
+ if (leftPos.bottom < rightPos.top) add(left, leftPos.bottom, null, rightPos.top);
2382
+ }
2383
+ if (toArg == null && to == lineLen) right = rightSide;
2384
+ if (!start || leftPos.top < start.top || leftPos.top == start.top && leftPos.left < start.left)
2385
+ start = leftPos;
2386
+ if (!end || rightPos.bottom > end.bottom || rightPos.bottom == end.bottom && rightPos.right > end.right)
2387
+ end = rightPos;
2388
+ if (left < leftSide + 1) left = leftSide;
2389
+ add(left, rightPos.top, right - left, rightPos.bottom);
2390
+ });
2391
+ return {start: start, end: end};
2392
+ }
2393
+
2394
+ var sFrom = range.from(), sTo = range.to();
2395
+ if (sFrom.line == sTo.line) {
2396
+ drawForLine(sFrom.line, sFrom.ch, sTo.ch);
2397
+ } else {
2398
+ var fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line);
2399
+ var singleVLine = visualLine(fromLine) == visualLine(toLine);
2400
+ var leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end;
2401
+ var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start;
2402
+ if (singleVLine) {
2403
+ if (leftEnd.top < rightStart.top - 2) {
2404
+ add(leftEnd.right, leftEnd.top, null, leftEnd.bottom);
2405
+ add(leftSide, rightStart.top, rightStart.left, rightStart.bottom);
2406
+ } else {
2407
+ add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom);
2408
+ }
2409
+ }
2410
+ if (leftEnd.bottom < rightStart.top)
2411
+ add(leftSide, leftEnd.bottom, null, rightStart.top);
2412
+ }
2413
+
2414
+ output.appendChild(fragment);
2415
+ }
2416
+
2417
+ // Cursor-blinking
2418
+ function restartBlink(cm) {
2419
+ if (!cm.state.focused) return;
2420
+ var display = cm.display;
2421
+ clearInterval(display.blinker);
2422
+ var on = true;
2423
+ display.cursorDiv.style.visibility = "";
2424
+ if (cm.options.cursorBlinkRate > 0)
2425
+ display.blinker = setInterval(function() {
2426
+ display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden";
2427
+ }, cm.options.cursorBlinkRate);
2428
+ else if (cm.options.cursorBlinkRate < 0)
2429
+ display.cursorDiv.style.visibility = "hidden";
2430
+ }
2431
+
2432
+ // HIGHLIGHT WORKER
2433
+
2434
+ function startWorker(cm, time) {
2435
+ if (cm.doc.mode.startState && cm.doc.frontier < cm.display.viewTo)
2436
+ cm.state.highlight.set(time, bind(highlightWorker, cm));
2437
+ }
2438
+
2439
+ function highlightWorker(cm) {
2440
+ var doc = cm.doc;
2441
+ if (doc.frontier < doc.first) doc.frontier = doc.first;
2442
+ if (doc.frontier >= cm.display.viewTo) return;
2443
+ var end = +new Date + cm.options.workTime;
2444
+ var state = copyState(doc.mode, getStateBefore(cm, doc.frontier));
2445
+ var changedLines = [];
2446
+
2447
+ doc.iter(doc.frontier, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function(line) {
2448
+ if (doc.frontier >= cm.display.viewFrom) { // Visible
2449
+ var oldStyles = line.styles, tooLong = line.text.length > cm.options.maxHighlightLength;
2450
+ var highlighted = highlightLine(cm, line, tooLong ? copyState(doc.mode, state) : state, true);
2451
+ line.styles = highlighted.styles;
2452
+ var oldCls = line.styleClasses, newCls = highlighted.classes;
2453
+ if (newCls) line.styleClasses = newCls;
2454
+ else if (oldCls) line.styleClasses = null;
2455
+ var ischange = !oldStyles || oldStyles.length != line.styles.length ||
2456
+ oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgClass || oldCls.textClass != newCls.textClass);
2457
+ for (var i = 0; !ischange && i < oldStyles.length; ++i) ischange = oldStyles[i] != line.styles[i];
2458
+ if (ischange) changedLines.push(doc.frontier);
2459
+ line.stateAfter = tooLong ? state : copyState(doc.mode, state);
2460
+ } else {
2461
+ if (line.text.length <= cm.options.maxHighlightLength)
2462
+ processLine(cm, line.text, state);
2463
+ line.stateAfter = doc.frontier % 5 == 0 ? copyState(doc.mode, state) : null;
2464
+ }
2465
+ ++doc.frontier;
2466
+ if (+new Date > end) {
2467
+ startWorker(cm, cm.options.workDelay);
2468
+ return true;
2469
+ }
2470
+ });
2471
+ if (changedLines.length) runInOp(cm, function() {
2472
+ for (var i = 0; i < changedLines.length; i++)
2473
+ regLineChange(cm, changedLines[i], "text");
2474
+ });
2475
+ }
2476
+
2477
+ // Finds the line to start with when starting a parse. Tries to
2478
+ // find a line with a stateAfter, so that it can start with a
2479
+ // valid state. If that fails, it returns the line with the
2480
+ // smallest indentation, which tends to need the least context to
2481
+ // parse correctly.
2482
+ function findStartLine(cm, n, precise) {
2483
+ var minindent, minline, doc = cm.doc;
2484
+ var lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100);
2485
+ for (var search = n; search > lim; --search) {
2486
+ if (search <= doc.first) return doc.first;
2487
+ var line = getLine(doc, search - 1);
2488
+ if (line.stateAfter && (!precise || search <= doc.frontier)) return search;
2489
+ var indented = countColumn(line.text, null, cm.options.tabSize);
2490
+ if (minline == null || minindent > indented) {
2491
+ minline = search - 1;
2492
+ minindent = indented;
2493
+ }
2494
+ }
2495
+ return minline;
2496
+ }
2497
+
2498
+ function getStateBefore(cm, n, precise) {
2499
+ var doc = cm.doc, display = cm.display;
2500
+ if (!doc.mode.startState) return true;
2501
+ var pos = findStartLine(cm, n, precise), state = pos > doc.first && getLine(doc, pos-1).stateAfter;
2502
+ if (!state) state = startState(doc.mode);
2503
+ else state = copyState(doc.mode, state);
2504
+ doc.iter(pos, n, function(line) {
2505
+ processLine(cm, line.text, state);
2506
+ var save = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo;
2507
+ line.stateAfter = save ? copyState(doc.mode, state) : null;
2508
+ ++pos;
2509
+ });
2510
+ if (precise) doc.frontier = pos;
2511
+ return state;
2512
+ }
2513
+
2514
+ // POSITION MEASUREMENT
2515
+
2516
+ function paddingTop(display) {return display.lineSpace.offsetTop;}
2517
+ function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight;}
2518
+ function paddingH(display) {
2519
+ if (display.cachedPaddingH) return display.cachedPaddingH;
2520
+ var e = removeChildrenAndAdd(display.measure, elt("pre", "x"));
2521
+ var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle;
2522
+ var data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)};
2523
+ if (!isNaN(data.left) && !isNaN(data.right)) display.cachedPaddingH = data;
2524
+ return data;
2525
+ }
2526
+
2527
+ function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth; }
2528
+ function displayWidth(cm) {
2529
+ return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth;
2530
+ }
2531
+ function displayHeight(cm) {
2532
+ return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeight;
2533
+ }
2534
+
2535
+ // Ensure the lineView.wrapping.heights array is populated. This is
2536
+ // an array of bottom offsets for the lines that make up a drawn
2537
+ // line. When lineWrapping is on, there might be more than one
2538
+ // height.
2539
+ function ensureLineHeights(cm, lineView, rect) {
2540
+ var wrapping = cm.options.lineWrapping;
2541
+ var curWidth = wrapping && displayWidth(cm);
2542
+ if (!lineView.measure.heights || wrapping && lineView.measure.width != curWidth) {
2543
+ var heights = lineView.measure.heights = [];
2544
+ if (wrapping) {
2545
+ lineView.measure.width = curWidth;
2546
+ var rects = lineView.text.firstChild.getClientRects();
2547
+ for (var i = 0; i < rects.length - 1; i++) {
2548
+ var cur = rects[i], next = rects[i + 1];
2549
+ if (Math.abs(cur.bottom - next.bottom) > 2)
2550
+ heights.push((cur.bottom + next.top) / 2 - rect.top);
2551
+ }
2552
+ }
2553
+ heights.push(rect.bottom - rect.top);
2554
+ }
2555
+ }
2556
+
2557
+ // Find a line map (mapping character offsets to text nodes) and a
2558
+ // measurement cache for the given line number. (A line view might
2559
+ // contain multiple lines when collapsed ranges are present.)
2560
+ function mapFromLineView(lineView, line, lineN) {
2561
+ if (lineView.line == line)
2562
+ return {map: lineView.measure.map, cache: lineView.measure.cache};
2563
+ for (var i = 0; i < lineView.rest.length; i++)
2564
+ if (lineView.rest[i] == line)
2565
+ return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]};
2566
+ for (var i = 0; i < lineView.rest.length; i++)
2567
+ if (lineNo(lineView.rest[i]) > lineN)
2568
+ return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i], before: true};
2569
+ }
2570
+
2571
+ // Render a line into the hidden node display.externalMeasured. Used
2572
+ // when measurement is needed for a line that's not in the viewport.
2573
+ function updateExternalMeasurement(cm, line) {
2574
+ line = visualLine(line);
2575
+ var lineN = lineNo(line);
2576
+ var view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN);
2577
+ view.lineN = lineN;
2578
+ var built = view.built = buildLineContent(cm, view);
2579
+ view.text = built.pre;
2580
+ removeChildrenAndAdd(cm.display.lineMeasure, built.pre);
2581
+ return view;
2582
+ }
2583
+
2584
+ // Get a {top, bottom, left, right} box (in line-local coordinates)
2585
+ // for a given character.
2586
+ function measureChar(cm, line, ch, bias) {
2587
+ return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias);
2588
+ }
2589
+
2590
+ // Find a line view that corresponds to the given line number.
2591
+ function findViewForLine(cm, lineN) {
2592
+ if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo)
2593
+ return cm.display.view[findViewIndex(cm, lineN)];
2594
+ var ext = cm.display.externalMeasured;
2595
+ if (ext && lineN >= ext.lineN && lineN < ext.lineN + ext.size)
2596
+ return ext;
2597
+ }
2598
+
2599
+ // Measurement can be split in two steps, the set-up work that
2600
+ // applies to the whole line, and the measurement of the actual
2601
+ // character. Functions like coordsChar, that need to do a lot of
2602
+ // measurements in a row, can thus ensure that the set-up work is
2603
+ // only done once.
2604
+ function prepareMeasureForLine(cm, line) {
2605
+ var lineN = lineNo(line);
2606
+ var view = findViewForLine(cm, lineN);
2607
+ if (view && !view.text) {
2608
+ view = null;
2609
+ } else if (view && view.changes) {
2610
+ updateLineForChanges(cm, view, lineN, getDimensions(cm));
2611
+ cm.curOp.forceUpdate = true;
2612
+ }
2613
+ if (!view)
2614
+ view = updateExternalMeasurement(cm, line);
2615
+
2616
+ var info = mapFromLineView(view, line, lineN);
2617
+ return {
2618
+ line: line, view: view, rect: null,
2619
+ map: info.map, cache: info.cache, before: info.before,
2620
+ hasHeights: false
2621
+ };
2622
+ }
2623
+
2624
+ // Given a prepared measurement object, measures the position of an
2625
+ // actual character (or fetches it from the cache).
2626
+ function measureCharPrepared(cm, prepared, ch, bias, varHeight) {
2627
+ if (prepared.before) ch = -1;
2628
+ var key = ch + (bias || ""), found;
2629
+ if (prepared.cache.hasOwnProperty(key)) {
2630
+ found = prepared.cache[key];
2631
+ } else {
2632
+ if (!prepared.rect)
2633
+ prepared.rect = prepared.view.text.getBoundingClientRect();
2634
+ if (!prepared.hasHeights) {
2635
+ ensureLineHeights(cm, prepared.view, prepared.rect);
2636
+ prepared.hasHeights = true;
2637
+ }
2638
+ found = measureCharInner(cm, prepared, ch, bias);
2639
+ if (!found.bogus) prepared.cache[key] = found;
2640
+ }
2641
+ return {left: found.left, right: found.right,
2642
+ top: varHeight ? found.rtop : found.top,
2643
+ bottom: varHeight ? found.rbottom : found.bottom};
2644
+ }
2645
+
2646
+ var nullRect = {left: 0, right: 0, top: 0, bottom: 0};
2647
+
2648
+ function nodeAndOffsetInLineMap(map, ch, bias) {
2649
+ var node, start, end, collapse;
2650
+ // First, search the line map for the text node corresponding to,
2651
+ // or closest to, the target character.
2652
+ for (var i = 0; i < map.length; i += 3) {
2653
+ var mStart = map[i], mEnd = map[i + 1];
2654
+ if (ch < mStart) {
2655
+ start = 0; end = 1;
2656
+ collapse = "left";
2657
+ } else if (ch < mEnd) {
2658
+ start = ch - mStart;
2659
+ end = start + 1;
2660
+ } else if (i == map.length - 3 || ch == mEnd && map[i + 3] > ch) {
2661
+ end = mEnd - mStart;
2662
+ start = end - 1;
2663
+ if (ch >= mEnd) collapse = "right";
2664
+ }
2665
+ if (start != null) {
2666
+ node = map[i + 2];
2667
+ if (mStart == mEnd && bias == (node.insertLeft ? "left" : "right"))
2668
+ collapse = bias;
2669
+ if (bias == "left" && start == 0)
2670
+ while (i && map[i - 2] == map[i - 3] && map[i - 1].insertLeft) {
2671
+ node = map[(i -= 3) + 2];
2672
+ collapse = "left";
2673
+ }
2674
+ if (bias == "right" && start == mEnd - mStart)
2675
+ while (i < map.length - 3 && map[i + 3] == map[i + 4] && !map[i + 5].insertLeft) {
2676
+ node = map[(i += 3) + 2];
2677
+ collapse = "right";
2678
+ }
2679
+ break;
2680
+ }
2681
+ }
2682
+ return {node: node, start: start, end: end, collapse: collapse, coverStart: mStart, coverEnd: mEnd};
2683
+ }
2684
+
2685
+ function measureCharInner(cm, prepared, ch, bias) {
2686
+ var place = nodeAndOffsetInLineMap(prepared.map, ch, bias);
2687
+ var node = place.node, start = place.start, end = place.end, collapse = place.collapse;
2688
+
2689
+ var rect;
2690
+ if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates.
2691
+ for (var i = 0; i < 4; i++) { // Retry a maximum of 4 times when nonsense rectangles are returned
2692
+ while (start && isExtendingChar(prepared.line.text.charAt(place.coverStart + start))) --start;
2693
+ while (place.coverStart + end < place.coverEnd && isExtendingChar(prepared.line.text.charAt(place.coverStart + end))) ++end;
2694
+ if (ie && ie_version < 9 && start == 0 && end == place.coverEnd - place.coverStart) {
2695
+ rect = node.parentNode.getBoundingClientRect();
2696
+ } else if (ie && cm.options.lineWrapping) {
2697
+ var rects = range(node, start, end).getClientRects();
2698
+ if (rects.length)
2699
+ rect = rects[bias == "right" ? rects.length - 1 : 0];
2700
+ else
2701
+ rect = nullRect;
2702
+ } else {
2703
+ rect = range(node, start, end).getBoundingClientRect() || nullRect;
2704
+ }
2705
+ if (rect.left || rect.right || start == 0) break;
2706
+ end = start;
2707
+ start = start - 1;
2708
+ collapse = "right";
2709
+ }
2710
+ if (ie && ie_version < 11) rect = maybeUpdateRectForZooming(cm.display.measure, rect);
2711
+ } else { // If it is a widget, simply get the box for the whole widget.
2712
+ if (start > 0) collapse = bias = "right";
2713
+ var rects;
2714
+ if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1)
2715
+ rect = rects[bias == "right" ? rects.length - 1 : 0];
2716
+ else
2717
+ rect = node.getBoundingClientRect();
2718
+ }
2719
+ if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) {
2720
+ var rSpan = node.parentNode.getClientRects()[0];
2721
+ if (rSpan)
2722
+ rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top: rSpan.top, bottom: rSpan.bottom};
2723
+ else
2724
+ rect = nullRect;
2725
+ }
2726
+
2727
+ var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top;
2728
+ var mid = (rtop + rbot) / 2;
2729
+ var heights = prepared.view.measure.heights;
2730
+ for (var i = 0; i < heights.length - 1; i++)
2731
+ if (mid < heights[i]) break;
2732
+ var top = i ? heights[i - 1] : 0, bot = heights[i];
2733
+ var result = {left: (collapse == "right" ? rect.right : rect.left) - prepared.rect.left,
2734
+ right: (collapse == "left" ? rect.left : rect.right) - prepared.rect.left,
2735
+ top: top, bottom: bot};
2736
+ if (!rect.left && !rect.right) result.bogus = true;
2737
+ if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbottom = rbot; }
2738
+
2739
+ return result;
2740
+ }
2741
+
2742
+ // Work around problem with bounding client rects on ranges being
2743
+ // returned incorrectly when zoomed on IE10 and below.
2744
+ function maybeUpdateRectForZooming(measure, rect) {
2745
+ if (!window.screen || screen.logicalXDPI == null ||
2746
+ screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure))
2747
+ return rect;
2748
+ var scaleX = screen.logicalXDPI / screen.deviceXDPI;
2749
+ var scaleY = screen.logicalYDPI / screen.deviceYDPI;
2750
+ return {left: rect.left * scaleX, right: rect.right * scaleX,
2751
+ top: rect.top * scaleY, bottom: rect.bottom * scaleY};
2752
+ }
2753
+
2754
+ function clearLineMeasurementCacheFor(lineView) {
2755
+ if (lineView.measure) {
2756
+ lineView.measure.cache = {};
2757
+ lineView.measure.heights = null;
2758
+ if (lineView.rest) for (var i = 0; i < lineView.rest.length; i++)
2759
+ lineView.measure.caches[i] = {};
2760
+ }
2761
+ }
2762
+
2763
+ function clearLineMeasurementCache(cm) {
2764
+ cm.display.externalMeasure = null;
2765
+ removeChildren(cm.display.lineMeasure);
2766
+ for (var i = 0; i < cm.display.view.length; i++)
2767
+ clearLineMeasurementCacheFor(cm.display.view[i]);
2768
+ }
2769
+
2770
+ function clearCaches(cm) {
2771
+ clearLineMeasurementCache(cm);
2772
+ cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cachedPaddingH = null;
2773
+ if (!cm.options.lineWrapping) cm.display.maxLineChanged = true;
2774
+ cm.display.lineNumChars = null;
2775
+ }
2776
+
2777
+ function pageScrollX() { return window.pageXOffset || (document.documentElement || document.body).scrollLeft; }
2778
+ function pageScrollY() { return window.pageYOffset || (document.documentElement || document.body).scrollTop; }
2779
+
2780
+ // Converts a {top, bottom, left, right} box from line-local
2781
+ // coordinates into another coordinate system. Context may be one of
2782
+ // "line", "div" (display.lineDiv), "local"/null (editor), "window",
2783
+ // or "page".
2784
+ function intoCoordSystem(cm, lineObj, rect, context) {
2785
+ if (lineObj.widgets) for (var i = 0; i < lineObj.widgets.length; ++i) if (lineObj.widgets[i].above) {
2786
+ var size = widgetHeight(lineObj.widgets[i]);
2787
+ rect.top += size; rect.bottom += size;
2788
+ }
2789
+ if (context == "line") return rect;
2790
+ if (!context) context = "local";
2791
+ var yOff = heightAtLine(lineObj);
2792
+ if (context == "local") yOff += paddingTop(cm.display);
2793
+ else yOff -= cm.display.viewOffset;
2794
+ if (context == "page" || context == "window") {
2795
+ var lOff = cm.display.lineSpace.getBoundingClientRect();
2796
+ yOff += lOff.top + (context == "window" ? 0 : pageScrollY());
2797
+ var xOff = lOff.left + (context == "window" ? 0 : pageScrollX());
2798
+ rect.left += xOff; rect.right += xOff;
2799
+ }
2800
+ rect.top += yOff; rect.bottom += yOff;
2801
+ return rect;
2802
+ }
2803
+
2804
+ // Coverts a box from "div" coords to another coordinate system.
2805
+ // Context may be "window", "page", "div", or "local"/null.
2806
+ function fromCoordSystem(cm, coords, context) {
2807
+ if (context == "div") return coords;
2808
+ var left = coords.left, top = coords.top;
2809
+ // First move into "page" coordinate system
2810
+ if (context == "page") {
2811
+ left -= pageScrollX();
2812
+ top -= pageScrollY();
2813
+ } else if (context == "local" || !context) {
2814
+ var localBox = cm.display.sizer.getBoundingClientRect();
2815
+ left += localBox.left;
2816
+ top += localBox.top;
2817
+ }
2818
+
2819
+ var lineSpaceBox = cm.display.lineSpace.getBoundingClientRect();
2820
+ return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top};
2821
+ }
2822
+
2823
+ function charCoords(cm, pos, context, lineObj, bias) {
2824
+ if (!lineObj) lineObj = getLine(cm.doc, pos.line);
2825
+ return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, bias), context);
2826
+ }
2827
+
2828
+ // Returns a box for a given cursor position, which may have an
2829
+ // 'other' property containing the position of the secondary cursor
2830
+ // on a bidi boundary.
2831
+ function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) {
2832
+ lineObj = lineObj || getLine(cm.doc, pos.line);
2833
+ if (!preparedMeasure) preparedMeasure = prepareMeasureForLine(cm, lineObj);
2834
+ function get(ch, right) {
2835
+ var m = measureCharPrepared(cm, preparedMeasure, ch, right ? "right" : "left", varHeight);
2836
+ if (right) m.left = m.right; else m.right = m.left;
2837
+ return intoCoordSystem(cm, lineObj, m, context);
2838
+ }
2839
+ function getBidi(ch, partPos) {
2840
+ var part = order[partPos], right = part.level % 2;
2841
+ if (ch == bidiLeft(part) && partPos && part.level < order[partPos - 1].level) {
2842
+ part = order[--partPos];
2843
+ ch = bidiRight(part) - (part.level % 2 ? 0 : 1);
2844
+ right = true;
2845
+ } else if (ch == bidiRight(part) && partPos < order.length - 1 && part.level < order[partPos + 1].level) {
2846
+ part = order[++partPos];
2847
+ ch = bidiLeft(part) - part.level % 2;
2848
+ right = false;
2849
+ }
2850
+ if (right && ch == part.to && ch > part.from) return get(ch - 1);
2851
+ return get(ch, right);
2852
+ }
2853
+ var order = getOrder(lineObj), ch = pos.ch;
2854
+ if (!order) return get(ch);
2855
+ var partPos = getBidiPartAt(order, ch);
2856
+ var val = getBidi(ch, partPos);
2857
+ if (bidiOther != null) val.other = getBidi(ch, bidiOther);
2858
+ return val;
2859
+ }
2860
+
2861
+ // Used to cheaply estimate the coordinates for a position. Used for
2862
+ // intermediate scroll updates.
2863
+ function estimateCoords(cm, pos) {
2864
+ var left = 0, pos = clipPos(cm.doc, pos);
2865
+ if (!cm.options.lineWrapping) left = charWidth(cm.display) * pos.ch;
2866
+ var lineObj = getLine(cm.doc, pos.line);
2867
+ var top = heightAtLine(lineObj) + paddingTop(cm.display);
2868
+ return {left: left, right: left, top: top, bottom: top + lineObj.height};
2869
+ }
2870
+
2871
+ // Positions returned by coordsChar contain some extra information.
2872
+ // xRel is the relative x position of the input coordinates compared
2873
+ // to the found position (so xRel > 0 means the coordinates are to
2874
+ // the right of the character position, for example). When outside
2875
+ // is true, that means the coordinates lie outside the line's
2876
+ // vertical range.
2877
+ function PosWithInfo(line, ch, outside, xRel) {
2878
+ var pos = Pos(line, ch);
2879
+ pos.xRel = xRel;
2880
+ if (outside) pos.outside = true;
2881
+ return pos;
2882
+ }
2883
+
2884
+ // Compute the character position closest to the given coordinates.
2885
+ // Input must be lineSpace-local ("div" coordinate system).
2886
+ function coordsChar(cm, x, y) {
2887
+ var doc = cm.doc;
2888
+ y += cm.display.viewOffset;
2889
+ if (y < 0) return PosWithInfo(doc.first, 0, true, -1);
2890
+ var lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1;
2891
+ if (lineN > last)
2892
+ return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, true, 1);
2893
+ if (x < 0) x = 0;
2894
+
2895
+ var lineObj = getLine(doc, lineN);
2896
+ for (;;) {
2897
+ var found = coordsCharInner(cm, lineObj, lineN, x, y);
2898
+ var merged = collapsedSpanAtEnd(lineObj);
2899
+ var mergedPos = merged && merged.find(0, true);
2900
+ if (merged && (found.ch > mergedPos.from.ch || found.ch == mergedPos.from.ch && found.xRel > 0))
2901
+ lineN = lineNo(lineObj = mergedPos.to.line);
2902
+ else
2903
+ return found;
2904
+ }
2905
+ }
2906
+
2907
+ function coordsCharInner(cm, lineObj, lineNo, x, y) {
2908
+ var innerOff = y - heightAtLine(lineObj);
2909
+ var wrongLine = false, adjust = 2 * cm.display.wrapper.clientWidth;
2910
+ var preparedMeasure = prepareMeasureForLine(cm, lineObj);
2911
+
2912
+ function getX(ch) {
2913
+ var sp = cursorCoords(cm, Pos(lineNo, ch), "line", lineObj, preparedMeasure);
2914
+ wrongLine = true;
2915
+ if (innerOff > sp.bottom) return sp.left - adjust;
2916
+ else if (innerOff < sp.top) return sp.left + adjust;
2917
+ else wrongLine = false;
2918
+ return sp.left;
2919
+ }
2920
+
2921
+ var bidi = getOrder(lineObj), dist = lineObj.text.length;
2922
+ var from = lineLeft(lineObj), to = lineRight(lineObj);
2923
+ var fromX = getX(from), fromOutside = wrongLine, toX = getX(to), toOutside = wrongLine;
2924
+
2925
+ if (x > toX) return PosWithInfo(lineNo, to, toOutside, 1);
2926
+ // Do a binary search between these bounds.
2927
+ for (;;) {
2928
+ if (bidi ? to == from || to == moveVisually(lineObj, from, 1) : to - from <= 1) {
2929
+ var ch = x < fromX || x - fromX <= toX - x ? from : to;
2930
+ var xDiff = x - (ch == from ? fromX : toX);
2931
+ while (isExtendingChar(lineObj.text.charAt(ch))) ++ch;
2932
+ var pos = PosWithInfo(lineNo, ch, ch == from ? fromOutside : toOutside,
2933
+ xDiff < -1 ? -1 : xDiff > 1 ? 1 : 0);
2934
+ return pos;
2935
+ }
2936
+ var step = Math.ceil(dist / 2), middle = from + step;
2937
+ if (bidi) {
2938
+ middle = from;
2939
+ for (var i = 0; i < step; ++i) middle = moveVisually(lineObj, middle, 1);
2940
+ }
2941
+ var middleX = getX(middle);
2942
+ if (middleX > x) {to = middle; toX = middleX; if (toOutside = wrongLine) toX += 1000; dist = step;}
2943
+ else {from = middle; fromX = middleX; fromOutside = wrongLine; dist -= step;}
2944
+ }
2945
+ }
2946
+
2947
+ var measureText;
2948
+ // Compute the default text height.
2949
+ function textHeight(display) {
2950
+ if (display.cachedTextHeight != null) return display.cachedTextHeight;
2951
+ if (measureText == null) {
2952
+ measureText = elt("pre");
2953
+ // Measure a bunch of lines, for browsers that compute
2954
+ // fractional heights.
2955
+ for (var i = 0; i < 49; ++i) {
2956
+ measureText.appendChild(document.createTextNode("x"));
2957
+ measureText.appendChild(elt("br"));
2958
+ }
2959
+ measureText.appendChild(document.createTextNode("x"));
2960
+ }
2961
+ removeChildrenAndAdd(display.measure, measureText);
2962
+ var height = measureText.offsetHeight / 50;
2963
+ if (height > 3) display.cachedTextHeight = height;
2964
+ removeChildren(display.measure);
2965
+ return height || 1;
2966
+ }
2967
+
2968
+ // Compute the default character width.
2969
+ function charWidth(display) {
2970
+ if (display.cachedCharWidth != null) return display.cachedCharWidth;
2971
+ var anchor = elt("span", "xxxxxxxxxx");
2972
+ var pre = elt("pre", [anchor]);
2973
+ removeChildrenAndAdd(display.measure, pre);
2974
+ var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10;
2975
+ if (width > 2) display.cachedCharWidth = width;
2976
+ return width || 10;
2977
+ }
2978
+
2979
+ // OPERATIONS
2980
+
2981
+ // Operations are used to wrap a series of changes to the editor
2982
+ // state in such a way that each change won't have to update the
2983
+ // cursor and display (which would be awkward, slow, and
2984
+ // error-prone). Instead, display updates are batched and then all
2985
+ // combined and executed at once.
2986
+
2987
+ var operationGroup = null;
2988
+
2989
+ var nextOpId = 0;
2990
+ // Start a new operation.
2991
+ function startOperation(cm) {
2992
+ cm.curOp = {
2993
+ cm: cm,
2994
+ viewChanged: false, // Flag that indicates that lines might need to be redrawn
2995
+ startHeight: cm.doc.height, // Used to detect need to update scrollbar
2996
+ forceUpdate: false, // Used to force a redraw
2997
+ updateInput: null, // Whether to reset the input textarea
2998
+ typing: false, // Whether this reset should be careful to leave existing text (for compositing)
2999
+ changeObjs: null, // Accumulated changes, for firing change events
3000
+ cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on
3001
+ cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already
3002
+ selectionChanged: false, // Whether the selection needs to be redrawn
3003
+ updateMaxLine: false, // Set when the widest line needs to be determined anew
3004
+ scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet
3005
+ scrollToPos: null, // Used to scroll to a specific position
3006
+ focus: false,
3007
+ id: ++nextOpId // Unique ID
3008
+ };
3009
+ if (operationGroup) {
3010
+ operationGroup.ops.push(cm.curOp);
3011
+ } else {
3012
+ cm.curOp.ownsGroup = operationGroup = {
3013
+ ops: [cm.curOp],
3014
+ delayedCallbacks: []
3015
+ };
3016
+ }
3017
+ }
3018
+
3019
+ function fireCallbacksForOps(group) {
3020
+ // Calls delayed callbacks and cursorActivity handlers until no
3021
+ // new ones appear
3022
+ var callbacks = group.delayedCallbacks, i = 0;
3023
+ do {
3024
+ for (; i < callbacks.length; i++)
3025
+ callbacks[i].call(null);
3026
+ for (var j = 0; j < group.ops.length; j++) {
3027
+ var op = group.ops[j];
3028
+ if (op.cursorActivityHandlers)
3029
+ while (op.cursorActivityCalled < op.cursorActivityHandlers.length)
3030
+ op.cursorActivityHandlers[op.cursorActivityCalled++].call(null, op.cm);
3031
+ }
3032
+ } while (i < callbacks.length);
3033
+ }
3034
+
3035
+ // Finish an operation, updating the display and signalling delayed events
3036
+ function endOperation(cm) {
3037
+ var op = cm.curOp, group = op.ownsGroup;
3038
+ if (!group) return;
3039
+
3040
+ try { fireCallbacksForOps(group); }
3041
+ finally {
3042
+ operationGroup = null;
3043
+ for (var i = 0; i < group.ops.length; i++)
3044
+ group.ops[i].cm.curOp = null;
3045
+ endOperations(group);
3046
+ }
3047
+ }
3048
+
3049
+ // The DOM updates done when an operation finishes are batched so
3050
+ // that the minimum number of relayouts are required.
3051
+ function endOperations(group) {
3052
+ var ops = group.ops;
3053
+ for (var i = 0; i < ops.length; i++) // Read DOM
3054
+ endOperation_R1(ops[i]);
3055
+ for (var i = 0; i < ops.length; i++) // Write DOM (maybe)
3056
+ endOperation_W1(ops[i]);
3057
+ for (var i = 0; i < ops.length; i++) // Read DOM
3058
+ endOperation_R2(ops[i]);
3059
+ for (var i = 0; i < ops.length; i++) // Write DOM (maybe)
3060
+ endOperation_W2(ops[i]);
3061
+ for (var i = 0; i < ops.length; i++) // Read DOM
3062
+ endOperation_finish(ops[i]);
3063
+ }
3064
+
3065
+ function endOperation_R1(op) {
3066
+ var cm = op.cm, display = cm.display;
3067
+ maybeClipScrollbars(cm);
3068
+ if (op.updateMaxLine) findMaxLine(cm);
3069
+
3070
+ op.mustUpdate = op.viewChanged || op.forceUpdate || op.scrollTop != null ||
3071
+ op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom ||
3072
+ op.scrollToPos.to.line >= display.viewTo) ||
3073
+ display.maxLineChanged && cm.options.lineWrapping;
3074
+ op.update = op.mustUpdate &&
3075
+ new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate);
3076
+ }
3077
+
3078
+ function endOperation_W1(op) {
3079
+ op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update);
3080
+ }
3081
+
3082
+ function endOperation_R2(op) {
3083
+ var cm = op.cm, display = cm.display;
3084
+ if (op.updatedDisplay) updateHeightsInViewport(cm);
3085
+
3086
+ op.barMeasure = measureForScrollbars(cm);
3087
+
3088
+ // If the max line changed since it was last measured, measure it,
3089
+ // and ensure the document's width matches it.
3090
+ // updateDisplay_W2 will use these properties to do the actual resizing
3091
+ if (display.maxLineChanged && !cm.options.lineWrapping) {
3092
+ op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.length).left + 3;
3093
+ cm.display.sizerWidth = op.adjustWidthTo;
3094
+ op.barMeasure.scrollWidth =
3095
+ Math.max(display.scroller.clientWidth, display.sizer.offsetLeft + op.adjustWidthTo + scrollGap(cm) + cm.display.barWidth);
3096
+ op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm));
3097
+ }
3098
+
3099
+ if (op.updatedDisplay || op.selectionChanged)
3100
+ op.preparedSelection = display.input.prepareSelection();
3101
+ }
3102
+
3103
+ function endOperation_W2(op) {
3104
+ var cm = op.cm;
3105
+
3106
+ if (op.adjustWidthTo != null) {
3107
+ cm.display.sizer.style.minWidth = op.adjustWidthTo + "px";
3108
+ if (op.maxScrollLeft < cm.doc.scrollLeft)
3109
+ setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true);
3110
+ cm.display.maxLineChanged = false;
3111
+ }
3112
+
3113
+ if (op.preparedSelection)
3114
+ cm.display.input.showSelection(op.preparedSelection);
3115
+ if (op.updatedDisplay)
3116
+ setDocumentHeight(cm, op.barMeasure);
3117
+ if (op.updatedDisplay || op.startHeight != cm.doc.height)
3118
+ updateScrollbars(cm, op.barMeasure);
3119
+
3120
+ if (op.selectionChanged) restartBlink(cm);
3121
+
3122
+ if (cm.state.focused && op.updateInput)
3123
+ cm.display.input.reset(op.typing);
3124
+ if (op.focus && op.focus == activeElt() && (!document.hasFocus || document.hasFocus()))
3125
+ ensureFocus(op.cm);
3126
+ }
3127
+
3128
+ function endOperation_finish(op) {
3129
+ var cm = op.cm, display = cm.display, doc = cm.doc;
3130
+
3131
+ if (op.updatedDisplay) postUpdateDisplay(cm, op.update);
3132
+
3133
+ // Abort mouse wheel delta measurement, when scrolling explicitly
3134
+ if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos))
3135
+ display.wheelStartX = display.wheelStartY = null;
3136
+
3137
+ // Propagate the scroll position to the actual DOM scroller
3138
+ if (op.scrollTop != null && (display.scroller.scrollTop != op.scrollTop || op.forceScroll)) {
3139
+ doc.scrollTop = Math.max(0, Math.min(display.scroller.scrollHeight - display.scroller.clientHeight, op.scrollTop));
3140
+ display.scrollbars.setScrollTop(doc.scrollTop);
3141
+ display.scroller.scrollTop = doc.scrollTop;
3142
+ }
3143
+ if (op.scrollLeft != null && (display.scroller.scrollLeft != op.scrollLeft || op.forceScroll)) {
3144
+ doc.scrollLeft = Math.max(0, Math.min(display.scroller.scrollWidth - display.scroller.clientWidth, op.scrollLeft));
3145
+ display.scrollbars.setScrollLeft(doc.scrollLeft);
3146
+ display.scroller.scrollLeft = doc.scrollLeft;
3147
+ alignHorizontally(cm);
3148
+ }
3149
+ // If we need to scroll a specific position into view, do so.
3150
+ if (op.scrollToPos) {
3151
+ var coords = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from),
3152
+ clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin);
3153
+ if (op.scrollToPos.isCursor && cm.state.focused) maybeScrollWindow(cm, coords);
3154
+ }
3155
+
3156
+ // Fire events for markers that are hidden/unidden by editing or
3157
+ // undoing
3158
+ var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers;
3159
+ if (hidden) for (var i = 0; i < hidden.length; ++i)
3160
+ if (!hidden[i].lines.length) signal(hidden[i], "hide");
3161
+ if (unhidden) for (var i = 0; i < unhidden.length; ++i)
3162
+ if (unhidden[i].lines.length) signal(unhidden[i], "unhide");
3163
+
3164
+ if (display.wrapper.offsetHeight)
3165
+ doc.scrollTop = cm.display.scroller.scrollTop;
3166
+
3167
+ // Fire change events, and delayed event handlers
3168
+ if (op.changeObjs)
3169
+ signal(cm, "changes", cm, op.changeObjs);
3170
+ if (op.update)
3171
+ op.update.finish();
3172
+ }
3173
+
3174
+ // Run the given function in an operation
3175
+ function runInOp(cm, f) {
3176
+ if (cm.curOp) return f();
3177
+ startOperation(cm);
3178
+ try { return f(); }
3179
+ finally { endOperation(cm); }
3180
+ }
3181
+ // Wraps a function in an operation. Returns the wrapped function.
3182
+ function operation(cm, f) {
3183
+ return function() {
3184
+ if (cm.curOp) return f.apply(cm, arguments);
3185
+ startOperation(cm);
3186
+ try { return f.apply(cm, arguments); }
3187
+ finally { endOperation(cm); }
3188
+ };
3189
+ }
3190
+ // Used to add methods to editor and doc instances, wrapping them in
3191
+ // operations.
3192
+ function methodOp(f) {
3193
+ return function() {
3194
+ if (this.curOp) return f.apply(this, arguments);
3195
+ startOperation(this);
3196
+ try { return f.apply(this, arguments); }
3197
+ finally { endOperation(this); }
3198
+ };
3199
+ }
3200
+ function docMethodOp(f) {
3201
+ return function() {
3202
+ var cm = this.cm;
3203
+ if (!cm || cm.curOp) return f.apply(this, arguments);
3204
+ startOperation(cm);
3205
+ try { return f.apply(this, arguments); }
3206
+ finally { endOperation(cm); }
3207
+ };
3208
+ }
3209
+
3210
+ // VIEW TRACKING
3211
+
3212
+ // These objects are used to represent the visible (currently drawn)
3213
+ // part of the document. A LineView may correspond to multiple
3214
+ // logical lines, if those are connected by collapsed ranges.
3215
+ function LineView(doc, line, lineN) {
3216
+ // The starting line
3217
+ this.line = line;
3218
+ // Continuing lines, if any
3219
+ this.rest = visualLineContinued(line);
3220
+ // Number of logical lines in this visual line
3221
+ this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1;
3222
+ this.node = this.text = null;
3223
+ this.hidden = lineIsHidden(doc, line);
3224
+ }
3225
+
3226
+ // Create a range of LineView objects for the given lines.
3227
+ function buildViewArray(cm, from, to) {
3228
+ var array = [], nextPos;
3229
+ for (var pos = from; pos < to; pos = nextPos) {
3230
+ var view = new LineView(cm.doc, getLine(cm.doc, pos), pos);
3231
+ nextPos = pos + view.size;
3232
+ array.push(view);
3233
+ }
3234
+ return array;
3235
+ }
3236
+
3237
+ // Updates the display.view data structure for a given change to the
3238
+ // document. From and to are in pre-change coordinates. Lendiff is
3239
+ // the amount of lines added or subtracted by the change. This is
3240
+ // used for changes that span multiple lines, or change the way
3241
+ // lines are divided into visual lines. regLineChange (below)
3242
+ // registers single-line changes.
3243
+ function regChange(cm, from, to, lendiff) {
3244
+ if (from == null) from = cm.doc.first;
3245
+ if (to == null) to = cm.doc.first + cm.doc.size;
3246
+ if (!lendiff) lendiff = 0;
3247
+
3248
+ var display = cm.display;
3249
+ if (lendiff && to < display.viewTo &&
3250
+ (display.updateLineNumbers == null || display.updateLineNumbers > from))
3251
+ display.updateLineNumbers = from;
3252
+
3253
+ cm.curOp.viewChanged = true;
3254
+
3255
+ if (from >= display.viewTo) { // Change after
3256
+ if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo)
3257
+ resetView(cm);
3258
+ } else if (to <= display.viewFrom) { // Change before
3259
+ if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.viewFrom) {
3260
+ resetView(cm);
3261
+ } else {
3262
+ display.viewFrom += lendiff;
3263
+ display.viewTo += lendiff;
3264
+ }
3265
+ } else if (from <= display.viewFrom && to >= display.viewTo) { // Full overlap
3266
+ resetView(cm);
3267
+ } else if (from <= display.viewFrom) { // Top overlap
3268
+ var cut = viewCuttingPoint(cm, to, to + lendiff, 1);
3269
+ if (cut) {
3270
+ display.view = display.view.slice(cut.index);
3271
+ display.viewFrom = cut.lineN;
3272
+ display.viewTo += lendiff;
3273
+ } else {
3274
+ resetView(cm);
3275
+ }
3276
+ } else if (to >= display.viewTo) { // Bottom overlap
3277
+ var cut = viewCuttingPoint(cm, from, from, -1);
3278
+ if (cut) {
3279
+ display.view = display.view.slice(0, cut.index);
3280
+ display.viewTo = cut.lineN;
3281
+ } else {
3282
+ resetView(cm);
3283
+ }
3284
+ } else { // Gap in the middle
3285
+ var cutTop = viewCuttingPoint(cm, from, from, -1);
3286
+ var cutBot = viewCuttingPoint(cm, to, to + lendiff, 1);
3287
+ if (cutTop && cutBot) {
3288
+ display.view = display.view.slice(0, cutTop.index)
3289
+ .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN))
3290
+ .concat(display.view.slice(cutBot.index));
3291
+ display.viewTo += lendiff;
3292
+ } else {
3293
+ resetView(cm);
3294
+ }
3295
+ }
3296
+
3297
+ var ext = display.externalMeasured;
3298
+ if (ext) {
3299
+ if (to < ext.lineN)
3300
+ ext.lineN += lendiff;
3301
+ else if (from < ext.lineN + ext.size)
3302
+ display.externalMeasured = null;
3303
+ }
3304
+ }
3305
+
3306
+ // Register a change to a single line. Type must be one of "text",
3307
+ // "gutter", "class", "widget"
3308
+ function regLineChange(cm, line, type) {
3309
+ cm.curOp.viewChanged = true;
3310
+ var display = cm.display, ext = cm.display.externalMeasured;
3311
+ if (ext && line >= ext.lineN && line < ext.lineN + ext.size)
3312
+ display.externalMeasured = null;
3313
+
3314
+ if (line < display.viewFrom || line >= display.viewTo) return;
3315
+ var lineView = display.view[findViewIndex(cm, line)];
3316
+ if (lineView.node == null) return;
3317
+ var arr = lineView.changes || (lineView.changes = []);
3318
+ if (indexOf(arr, type) == -1) arr.push(type);
3319
+ }
3320
+
3321
+ // Clear the view.
3322
+ function resetView(cm) {
3323
+ cm.display.viewFrom = cm.display.viewTo = cm.doc.first;
3324
+ cm.display.view = [];
3325
+ cm.display.viewOffset = 0;
3326
+ }
3327
+
3328
+ // Find the view element corresponding to a given line. Return null
3329
+ // when the line isn't visible.
3330
+ function findViewIndex(cm, n) {
3331
+ if (n >= cm.display.viewTo) return null;
3332
+ n -= cm.display.viewFrom;
3333
+ if (n < 0) return null;
3334
+ var view = cm.display.view;
3335
+ for (var i = 0; i < view.length; i++) {
3336
+ n -= view[i].size;
3337
+ if (n < 0) return i;
3338
+ }
3339
+ }
3340
+
3341
+ function viewCuttingPoint(cm, oldN, newN, dir) {
3342
+ var index = findViewIndex(cm, oldN), diff, view = cm.display.view;
3343
+ if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size)
3344
+ return {index: index, lineN: newN};
3345
+ for (var i = 0, n = cm.display.viewFrom; i < index; i++)
3346
+ n += view[i].size;
3347
+ if (n != oldN) {
3348
+ if (dir > 0) {
3349
+ if (index == view.length - 1) return null;
3350
+ diff = (n + view[index].size) - oldN;
3351
+ index++;
3352
+ } else {
3353
+ diff = n - oldN;
3354
+ }
3355
+ oldN += diff; newN += diff;
3356
+ }
3357
+ while (visualLineNo(cm.doc, newN) != newN) {
3358
+ if (index == (dir < 0 ? 0 : view.length - 1)) return null;
3359
+ newN += dir * view[index - (dir < 0 ? 1 : 0)].size;
3360
+ index += dir;
3361
+ }
3362
+ return {index: index, lineN: newN};
3363
+ }
3364
+
3365
+ // Force the view to cover a given range, adding empty view element
3366
+ // or clipping off existing ones as needed.
3367
+ function adjustView(cm, from, to) {
3368
+ var display = cm.display, view = display.view;
3369
+ if (view.length == 0 || from >= display.viewTo || to <= display.viewFrom) {
3370
+ display.view = buildViewArray(cm, from, to);
3371
+ display.viewFrom = from;
3372
+ } else {
3373
+ if (display.viewFrom > from)
3374
+ display.view = buildViewArray(cm, from, display.viewFrom).concat(display.view);
3375
+ else if (display.viewFrom < from)
3376
+ display.view = display.view.slice(findViewIndex(cm, from));
3377
+ display.viewFrom = from;
3378
+ if (display.viewTo < to)
3379
+ display.view = display.view.concat(buildViewArray(cm, display.viewTo, to));
3380
+ else if (display.viewTo > to)
3381
+ display.view = display.view.slice(0, findViewIndex(cm, to));
3382
+ }
3383
+ display.viewTo = to;
3384
+ }
3385
+
3386
+ // Count the number of lines in the view whose DOM representation is
3387
+ // out of date (or nonexistent).
3388
+ function countDirtyView(cm) {
3389
+ var view = cm.display.view, dirty = 0;
3390
+ for (var i = 0; i < view.length; i++) {
3391
+ var lineView = view[i];
3392
+ if (!lineView.hidden && (!lineView.node || lineView.changes)) ++dirty;
3393
+ }
3394
+ return dirty;
3395
+ }
3396
+
3397
+ // EVENT HANDLERS
3398
+
3399
+ // Attach the necessary event handlers when initializing the editor
3400
+ function registerEventHandlers(cm) {
3401
+ var d = cm.display;
3402
+ on(d.scroller, "mousedown", operation(cm, onMouseDown));
3403
+ // Older IE's will not fire a second mousedown for a double click
3404
+ if (ie && ie_version < 11)
3405
+ on(d.scroller, "dblclick", operation(cm, function(e) {
3406
+ if (signalDOMEvent(cm, e)) return;
3407
+ var pos = posFromMouse(cm, e);
3408
+ if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) return;
3409
+ e_preventDefault(e);
3410
+ var word = cm.findWordAt(pos);
3411
+ extendSelection(cm.doc, word.anchor, word.head);
3412
+ }));
3413
+ else
3414
+ on(d.scroller, "dblclick", function(e) { signalDOMEvent(cm, e) || e_preventDefault(e); });
3415
+ // Some browsers fire contextmenu *after* opening the menu, at
3416
+ // which point we can't mess with it anymore. Context menu is
3417
+ // handled in onMouseDown for these browsers.
3418
+ if (!captureRightClick) on(d.scroller, "contextmenu", function(e) {onContextMenu(cm, e);});
3419
+
3420
+ // Used to suppress mouse event handling when a touch happens
3421
+ var touchFinished, prevTouch = {end: 0};
3422
+ function finishTouch() {
3423
+ if (d.activeTouch) {
3424
+ touchFinished = setTimeout(function() {d.activeTouch = null;}, 1000);
3425
+ prevTouch = d.activeTouch;
3426
+ prevTouch.end = +new Date;
3427
+ }
3428
+ };
3429
+ function isMouseLikeTouchEvent(e) {
3430
+ if (e.touches.length != 1) return false;
3431
+ var touch = e.touches[0];
3432
+ return touch.radiusX <= 1 && touch.radiusY <= 1;
3433
+ }
3434
+ function farAway(touch, other) {
3435
+ if (other.left == null) return true;
3436
+ var dx = other.left - touch.left, dy = other.top - touch.top;
3437
+ return dx * dx + dy * dy > 20 * 20;
3438
+ }
3439
+ on(d.scroller, "touchstart", function(e) {
3440
+ if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e)) {
3441
+ clearTimeout(touchFinished);
3442
+ var now = +new Date;
3443
+ d.activeTouch = {start: now, moved: false,
3444
+ prev: now - prevTouch.end <= 300 ? prevTouch : null};
3445
+ if (e.touches.length == 1) {
3446
+ d.activeTouch.left = e.touches[0].pageX;
3447
+ d.activeTouch.top = e.touches[0].pageY;
3448
+ }
3449
+ }
3450
+ });
3451
+ on(d.scroller, "touchmove", function() {
3452
+ if (d.activeTouch) d.activeTouch.moved = true;
3453
+ });
3454
+ on(d.scroller, "touchend", function(e) {
3455
+ var touch = d.activeTouch;
3456
+ if (touch && !eventInWidget(d, e) && touch.left != null &&
3457
+ !touch.moved && new Date - touch.start < 300) {
3458
+ var pos = cm.coordsChar(d.activeTouch, "page"), range;
3459
+ if (!touch.prev || farAway(touch, touch.prev)) // Single tap
3460
+ range = new Range(pos, pos);
3461
+ else if (!touch.prev.prev || farAway(touch, touch.prev.prev)) // Double tap
3462
+ range = cm.findWordAt(pos);
3463
+ else // Triple tap
3464
+ range = new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0)));
3465
+ cm.setSelection(range.anchor, range.head);
3466
+ cm.focus();
3467
+ e_preventDefault(e);
3468
+ }
3469
+ finishTouch();
3470
+ });
3471
+ on(d.scroller, "touchcancel", finishTouch);
3472
+
3473
+ // Sync scrolling between fake scrollbars and real scrollable
3474
+ // area, ensure viewport is updated when scrolling.
3475
+ on(d.scroller, "scroll", function() {
3476
+ if (d.scroller.clientHeight) {
3477
+ setScrollTop(cm, d.scroller.scrollTop);
3478
+ setScrollLeft(cm, d.scroller.scrollLeft, true);
3479
+ signal(cm, "scroll", cm);
3480
+ }
3481
+ });
3482
+
3483
+ // Listen to wheel events in order to try and update the viewport on time.
3484
+ on(d.scroller, "mousewheel", function(e){onScrollWheel(cm, e);});
3485
+ on(d.scroller, "DOMMouseScroll", function(e){onScrollWheel(cm, e);});
3486
+
3487
+ // Prevent wrapper from ever scrolling
3488
+ on(d.wrapper, "scroll", function() { d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; });
3489
+
3490
+ d.dragFunctions = {
3491
+ enter: function(e) {if (!signalDOMEvent(cm, e)) e_stop(e);},
3492
+ over: function(e) {if (!signalDOMEvent(cm, e)) { onDragOver(cm, e); e_stop(e); }},
3493
+ start: function(e){onDragStart(cm, e);},
3494
+ drop: operation(cm, onDrop),
3495
+ leave: function() {clearDragCursor(cm);}
3496
+ };
3497
+
3498
+ var inp = d.input.getField();
3499
+ on(inp, "keyup", function(e) { onKeyUp.call(cm, e); });
3500
+ on(inp, "keydown", operation(cm, onKeyDown));
3501
+ on(inp, "keypress", operation(cm, onKeyPress));
3502
+ on(inp, "focus", bind(onFocus, cm));
3503
+ on(inp, "blur", bind(onBlur, cm));
3504
+ }
3505
+
3506
+ function dragDropChanged(cm, value, old) {
3507
+ var wasOn = old && old != CodeMirror.Init;
3508
+ if (!value != !wasOn) {
3509
+ var funcs = cm.display.dragFunctions;
3510
+ var toggle = value ? on : off;
3511
+ toggle(cm.display.scroller, "dragstart", funcs.start);
3512
+ toggle(cm.display.scroller, "dragenter", funcs.enter);
3513
+ toggle(cm.display.scroller, "dragover", funcs.over);
3514
+ toggle(cm.display.scroller, "dragleave", funcs.leave);
3515
+ toggle(cm.display.scroller, "drop", funcs.drop);
3516
+ }
3517
+ }
3518
+
3519
+ // Called when the window resizes
3520
+ function onResize(cm) {
3521
+ var d = cm.display;
3522
+ if (d.lastWrapHeight == d.wrapper.clientHeight && d.lastWrapWidth == d.wrapper.clientWidth)
3523
+ return;
3524
+ // Might be a text scaling operation, clear size caches.
3525
+ d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;
3526
+ d.scrollbarsClipped = false;
3527
+ cm.setSize();
3528
+ }
3529
+
3530
+ // MOUSE EVENTS
3531
+
3532
+ // Return true when the given mouse event happened in a widget
3533
+ function eventInWidget(display, e) {
3534
+ for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {
3535
+ if (!n || (n.nodeType == 1 && n.getAttribute("cm-ignore-events") == "true") ||
3536
+ (n.parentNode == display.sizer && n != display.mover))
3537
+ return true;
3538
+ }
3539
+ }
3540
+
3541
+ // Given a mouse event, find the corresponding position. If liberal
3542
+ // is false, it checks whether a gutter or scrollbar was clicked,
3543
+ // and returns null if it was. forRect is used by rectangular
3544
+ // selections, and tries to estimate a character position even for
3545
+ // coordinates beyond the right of the text.
3546
+ function posFromMouse(cm, e, liberal, forRect) {
3547
+ var display = cm.display;
3548
+ if (!liberal && e_target(e).getAttribute("cm-not-content") == "true") return null;
3549
+
3550
+ var x, y, space = display.lineSpace.getBoundingClientRect();
3551
+ // Fails unpredictably on IE[67] when mouse is dragged around quickly.
3552
+ try { x = e.clientX - space.left; y = e.clientY - space.top; }
3553
+ catch (e) { return null; }
3554
+ var coords = coordsChar(cm, x, y), line;
3555
+ if (forRect && coords.xRel == 1 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) {
3556
+ var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length;
3557
+ coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff));
3558
+ }
3559
+ return coords;
3560
+ }
3561
+
3562
+ // A mouse down can be a single click, double click, triple click,
3563
+ // start of selection drag, start of text drag, new cursor
3564
+ // (ctrl-click), rectangle drag (alt-drag), or xwin
3565
+ // middle-click-paste. Or it might be a click on something we should
3566
+ // not interfere with, such as a scrollbar or widget.
3567
+ function onMouseDown(e) {
3568
+ var cm = this, display = cm.display;
3569
+ if (signalDOMEvent(cm, e) || display.activeTouch && display.input.supportsTouch()) return;
3570
+ display.shift = e.shiftKey;
3571
+
3572
+ if (eventInWidget(display, e)) {
3573
+ if (!webkit) {
3574
+ // Briefly turn off draggability, to allow widgets to do
3575
+ // normal dragging things.
3576
+ display.scroller.draggable = false;
3577
+ setTimeout(function(){display.scroller.draggable = true;}, 100);
3578
+ }
3579
+ return;
3580
+ }
3581
+ if (clickInGutter(cm, e)) return;
3582
+ var start = posFromMouse(cm, e);
3583
+ window.focus();
3584
+
3585
+ switch (e_button(e)) {
3586
+ case 1:
3587
+ // #3261: make sure, that we're not starting a second selection
3588
+ if (cm.state.selectingText)
3589
+ cm.state.selectingText(e);
3590
+ else if (start)
3591
+ leftButtonDown(cm, e, start);
3592
+ else if (e_target(e) == display.scroller)
3593
+ e_preventDefault(e);
3594
+ break;
3595
+ case 2:
3596
+ if (webkit) cm.state.lastMiddleDown = +new Date;
3597
+ if (start) extendSelection(cm.doc, start);
3598
+ setTimeout(function() {display.input.focus();}, 20);
3599
+ e_preventDefault(e);
3600
+ break;
3601
+ case 3:
3602
+ if (captureRightClick) onContextMenu(cm, e);
3603
+ else delayBlurEvent(cm);
3604
+ break;
3605
+ }
3606
+ }
3607
+
3608
+ var lastClick, lastDoubleClick;
3609
+ function leftButtonDown(cm, e, start) {
3610
+ if (ie) setTimeout(bind(ensureFocus, cm), 0);
3611
+ else cm.curOp.focus = activeElt();
3612
+
3613
+ var now = +new Date, type;
3614
+ if (lastDoubleClick && lastDoubleClick.time > now - 400 && cmp(lastDoubleClick.pos, start) == 0) {
3615
+ type = "triple";
3616
+ } else if (lastClick && lastClick.time > now - 400 && cmp(lastClick.pos, start) == 0) {
3617
+ type = "double";
3618
+ lastDoubleClick = {time: now, pos: start};
3619
+ } else {
3620
+ type = "single";
3621
+ lastClick = {time: now, pos: start};
3622
+ }
3623
+
3624
+ var sel = cm.doc.sel, modifier = mac ? e.metaKey : e.ctrlKey, contained;
3625
+ if (cm.options.dragDrop && dragAndDrop && !cm.isReadOnly() &&
3626
+ type == "single" && (contained = sel.contains(start)) > -1 &&
3627
+ (cmp((contained = sel.ranges[contained]).from(), start) < 0 || start.xRel > 0) &&
3628
+ (cmp(contained.to(), start) > 0 || start.xRel < 0))
3629
+ leftButtonStartDrag(cm, e, start, modifier);
3630
+ else
3631
+ leftButtonSelect(cm, e, start, type, modifier);
3632
+ }
3633
+
3634
+ // Start a text drag. When it ends, see if any dragging actually
3635
+ // happen, and treat as a click if it didn't.
3636
+ function leftButtonStartDrag(cm, e, start, modifier) {
3637
+ var display = cm.display, startTime = +new Date;
3638
+ var dragEnd = operation(cm, function(e2) {
3639
+ if (webkit) display.scroller.draggable = false;
3640
+ cm.state.draggingText = false;
3641
+ off(document, "mouseup", dragEnd);
3642
+ off(display.scroller, "drop", dragEnd);
3643
+ if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) {
3644
+ e_preventDefault(e2);
3645
+ if (!modifier && +new Date - 200 < startTime)
3646
+ extendSelection(cm.doc, start);
3647
+ // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081)
3648
+ if (webkit || ie && ie_version == 9)
3649
+ setTimeout(function() {document.body.focus(); display.input.focus();}, 20);
3650
+ else
3651
+ display.input.focus();
3652
+ }
3653
+ });
3654
+ // Let the drag handler handle this.
3655
+ if (webkit) display.scroller.draggable = true;
3656
+ cm.state.draggingText = dragEnd;
3657
+ // IE's approach to draggable
3658
+ if (display.scroller.dragDrop) display.scroller.dragDrop();
3659
+ on(document, "mouseup", dragEnd);
3660
+ on(display.scroller, "drop", dragEnd);
3661
+ }
3662
+
3663
+ // Normal selection, as opposed to text dragging.
3664
+ function leftButtonSelect(cm, e, start, type, addNew) {
3665
+ var display = cm.display, doc = cm.doc;
3666
+ e_preventDefault(e);
3667
+
3668
+ var ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges;
3669
+ if (addNew && !e.shiftKey) {
3670
+ ourIndex = doc.sel.contains(start);
3671
+ if (ourIndex > -1)
3672
+ ourRange = ranges[ourIndex];
3673
+ else
3674
+ ourRange = new Range(start, start);
3675
+ } else {
3676
+ ourRange = doc.sel.primary();
3677
+ ourIndex = doc.sel.primIndex;
3678
+ }
3679
+
3680
+ if (e.altKey) {
3681
+ type = "rect";
3682
+ if (!addNew) ourRange = new Range(start, start);
3683
+ start = posFromMouse(cm, e, true, true);
3684
+ ourIndex = -1;
3685
+ } else if (type == "double") {
3686
+ var word = cm.findWordAt(start);
3687
+ if (cm.display.shift || doc.extend)
3688
+ ourRange = extendRange(doc, ourRange, word.anchor, word.head);
3689
+ else
3690
+ ourRange = word;
3691
+ } else if (type == "triple") {
3692
+ var line = new Range(Pos(start.line, 0), clipPos(doc, Pos(start.line + 1, 0)));
3693
+ if (cm.display.shift || doc.extend)
3694
+ ourRange = extendRange(doc, ourRange, line.anchor, line.head);
3695
+ else
3696
+ ourRange = line;
3697
+ } else {
3698
+ ourRange = extendRange(doc, ourRange, start);
3699
+ }
3700
+
3701
+ if (!addNew) {
3702
+ ourIndex = 0;
3703
+ setSelection(doc, new Selection([ourRange], 0), sel_mouse);
3704
+ startSel = doc.sel;
3705
+ } else if (ourIndex == -1) {
3706
+ ourIndex = ranges.length;
3707
+ setSelection(doc, normalizeSelection(ranges.concat([ourRange]), ourIndex),
3708
+ {scroll: false, origin: "*mouse"});
3709
+ } else if (ranges.length > 1 && ranges[ourIndex].empty() && type == "single" && !e.shiftKey) {
3710
+ setSelection(doc, normalizeSelection(ranges.slice(0, ourIndex).concat(ranges.slice(ourIndex + 1)), 0),
3711
+ {scroll: false, origin: "*mouse"});
3712
+ startSel = doc.sel;
3713
+ } else {
3714
+ replaceOneSelection(doc, ourIndex, ourRange, sel_mouse);
3715
+ }
3716
+
3717
+ var lastPos = start;
3718
+ function extendTo(pos) {
3719
+ if (cmp(lastPos, pos) == 0) return;
3720
+ lastPos = pos;
3721
+
3722
+ if (type == "rect") {
3723
+ var ranges = [], tabSize = cm.options.tabSize;
3724
+ var startCol = countColumn(getLine(doc, start.line).text, start.ch, tabSize);
3725
+ var posCol = countColumn(getLine(doc, pos.line).text, pos.ch, tabSize);
3726
+ var left = Math.min(startCol, posCol), right = Math.max(startCol, posCol);
3727
+ for (var line = Math.min(start.line, pos.line), end = Math.min(cm.lastLine(), Math.max(start.line, pos.line));
3728
+ line <= end; line++) {
3729
+ var text = getLine(doc, line).text, leftPos = findColumn(text, left, tabSize);
3730
+ if (left == right)
3731
+ ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos)));
3732
+ else if (text.length > leftPos)
3733
+ ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize))));
3734
+ }
3735
+ if (!ranges.length) ranges.push(new Range(start, start));
3736
+ setSelection(doc, normalizeSelection(startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex),
3737
+ {origin: "*mouse", scroll: false});
3738
+ cm.scrollIntoView(pos);
3739
+ } else {
3740
+ var oldRange = ourRange;
3741
+ var anchor = oldRange.anchor, head = pos;
3742
+ if (type != "single") {
3743
+ if (type == "double")
3744
+ var range = cm.findWordAt(pos);
3745
+ else
3746
+ var range = new Range(Pos(pos.line, 0), clipPos(doc, Pos(pos.line + 1, 0)));
3747
+ if (cmp(range.anchor, anchor) > 0) {
3748
+ head = range.head;
3749
+ anchor = minPos(oldRange.from(), range.anchor);
3750
+ } else {
3751
+ head = range.anchor;
3752
+ anchor = maxPos(oldRange.to(), range.head);
3753
+ }
3754
+ }
3755
+ var ranges = startSel.ranges.slice(0);
3756
+ ranges[ourIndex] = new Range(clipPos(doc, anchor), head);
3757
+ setSelection(doc, normalizeSelection(ranges, ourIndex), sel_mouse);
3758
+ }
3759
+ }
3760
+
3761
+ var editorSize = display.wrapper.getBoundingClientRect();
3762
+ // Used to ensure timeout re-tries don't fire when another extend
3763
+ // happened in the meantime (clearTimeout isn't reliable -- at
3764
+ // least on Chrome, the timeouts still happen even when cleared,
3765
+ // if the clear happens after their scheduled firing time).
3766
+ var counter = 0;
3767
+
3768
+ function extend(e) {
3769
+ var curCount = ++counter;
3770
+ var cur = posFromMouse(cm, e, true, type == "rect");
3771
+ if (!cur) return;
3772
+ if (cmp(cur, lastPos) != 0) {
3773
+ cm.curOp.focus = activeElt();
3774
+ extendTo(cur);
3775
+ var visible = visibleLines(display, doc);
3776
+ if (cur.line >= visible.to || cur.line < visible.from)
3777
+ setTimeout(operation(cm, function(){if (counter == curCount) extend(e);}), 150);
3778
+ } else {
3779
+ var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0;
3780
+ if (outside) setTimeout(operation(cm, function() {
3781
+ if (counter != curCount) return;
3782
+ display.scroller.scrollTop += outside;
3783
+ extend(e);
3784
+ }), 50);
3785
+ }
3786
+ }
3787
+
3788
+ function done(e) {
3789
+ cm.state.selectingText = false;
3790
+ counter = Infinity;
3791
+ e_preventDefault(e);
3792
+ display.input.focus();
3793
+ off(document, "mousemove", move);
3794
+ off(document, "mouseup", up);
3795
+ doc.history.lastSelOrigin = null;
3796
+ }
3797
+
3798
+ var move = operation(cm, function(e) {
3799
+ if (!e_button(e)) done(e);
3800
+ else extend(e);
3801
+ });
3802
+ var up = operation(cm, done);
3803
+ cm.state.selectingText = up;
3804
+ on(document, "mousemove", move);
3805
+ on(document, "mouseup", up);
3806
+ }
3807
+
3808
+ // Determines whether an event happened in the gutter, and fires the
3809
+ // handlers for the corresponding event.
3810
+ function gutterEvent(cm, e, type, prevent) {
3811
+ try { var mX = e.clientX, mY = e.clientY; }
3812
+ catch(e) { return false; }
3813
+ if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) return false;
3814
+ if (prevent) e_preventDefault(e);
3815
+
3816
+ var display = cm.display;
3817
+ var lineBox = display.lineDiv.getBoundingClientRect();
3818
+
3819
+ if (mY > lineBox.bottom || !hasHandler(cm, type)) return e_defaultPrevented(e);
3820
+ mY -= lineBox.top - display.viewOffset;
3821
+
3822
+ for (var i = 0; i < cm.options.gutters.length; ++i) {
3823
+ var g = display.gutters.childNodes[i];
3824
+ if (g && g.getBoundingClientRect().right >= mX) {
3825
+ var line = lineAtHeight(cm.doc, mY);
3826
+ var gutter = cm.options.gutters[i];
3827
+ signal(cm, type, cm, line, gutter, e);
3828
+ return e_defaultPrevented(e);
3829
+ }
3830
+ }
3831
+ }
3832
+
3833
+ function clickInGutter(cm, e) {
3834
+ return gutterEvent(cm, e, "gutterClick", true);
3835
+ }
3836
+
3837
+ // Kludge to work around strange IE behavior where it'll sometimes
3838
+ // re-fire a series of drag-related events right after the drop (#1551)
3839
+ var lastDrop = 0;
3840
+
3841
+ function onDrop(e) {
3842
+ var cm = this;
3843
+ clearDragCursor(cm);
3844
+ if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e))
3845
+ return;
3846
+ e_preventDefault(e);
3847
+ if (ie) lastDrop = +new Date;
3848
+ var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files;
3849
+ if (!pos || cm.isReadOnly()) return;
3850
+ // Might be a file drop, in which case we simply extract the text
3851
+ // and insert it.
3852
+ if (files && files.length && window.FileReader && window.File) {
3853
+ var n = files.length, text = Array(n), read = 0;
3854
+ var loadFile = function(file, i) {
3855
+ if (cm.options.allowDropFileTypes &&
3856
+ indexOf(cm.options.allowDropFileTypes, file.type) == -1)
3857
+ return;
3858
+
3859
+ var reader = new FileReader;
3860
+ reader.onload = operation(cm, function() {
3861
+ var content = reader.result;
3862
+ if (/[\x00-\x08\x0e-\x1f]{2}/.test(content)) content = "";
3863
+ text[i] = content;
3864
+ if (++read == n) {
3865
+ pos = clipPos(cm.doc, pos);
3866
+ var change = {from: pos, to: pos,
3867
+ text: cm.doc.splitLines(text.join(cm.doc.lineSeparator())),
3868
+ origin: "paste"};
3869
+ makeChange(cm.doc, change);
3870
+ setSelectionReplaceHistory(cm.doc, simpleSelection(pos, changeEnd(change)));
3871
+ }
3872
+ });
3873
+ reader.readAsText(file);
3874
+ };
3875
+ for (var i = 0; i < n; ++i) loadFile(files[i], i);
3876
+ } else { // Normal drop
3877
+ // Don't do a replace if the drop happened inside of the selected text.
3878
+ if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) {
3879
+ cm.state.draggingText(e);
3880
+ // Ensure the editor is re-focused
3881
+ setTimeout(function() {cm.display.input.focus();}, 20);
3882
+ return;
3883
+ }
3884
+ try {
3885
+ var text = e.dataTransfer.getData("Text");
3886
+ if (text) {
3887
+ if (cm.state.draggingText && !(mac ? e.altKey : e.ctrlKey))
3888
+ var selected = cm.listSelections();
3889
+ setSelectionNoUndo(cm.doc, simpleSelection(pos, pos));
3890
+ if (selected) for (var i = 0; i < selected.length; ++i)
3891
+ replaceRange(cm.doc, "", selected[i].anchor, selected[i].head, "drag");
3892
+ cm.replaceSelection(text, "around", "paste");
3893
+ cm.display.input.focus();
3894
+ }
3895
+ }
3896
+ catch(e){}
3897
+ }
3898
+ }
3899
+
3900
+ function onDragStart(cm, e) {
3901
+ if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return; }
3902
+ if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) return;
3903
+
3904
+ e.dataTransfer.setData("Text", cm.getSelection());
3905
+
3906
+ // Use dummy image instead of default browsers image.
3907
+ // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
3908
+ if (e.dataTransfer.setDragImage && !safari) {
3909
+ var img = elt("img", null, null, "position: fixed; left: 0; top: 0;");
3910
+ img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
3911
+ if (presto) {
3912
+ img.width = img.height = 1;
3913
+ cm.display.wrapper.appendChild(img);
3914
+ // Force a relayout, or Opera won't use our image for some obscure reason
3915
+ img._top = img.offsetTop;
3916
+ }
3917
+ e.dataTransfer.setDragImage(img, 0, 0);
3918
+ if (presto) img.parentNode.removeChild(img);
3919
+ }
3920
+ }
3921
+
3922
+ function onDragOver(cm, e) {
3923
+ var pos = posFromMouse(cm, e);
3924
+ if (!pos) return;
3925
+ var frag = document.createDocumentFragment();
3926
+ drawSelectionCursor(cm, pos, frag);
3927
+ if (!cm.display.dragCursor) {
3928
+ cm.display.dragCursor = elt("div", null, "CodeMirror-cursors CodeMirror-dragcursors");
3929
+ cm.display.lineSpace.insertBefore(cm.display.dragCursor, cm.display.cursorDiv);
3930
+ }
3931
+ removeChildrenAndAdd(cm.display.dragCursor, frag);
3932
+ }
3933
+
3934
+ function clearDragCursor(cm) {
3935
+ if (cm.display.dragCursor) {
3936
+ cm.display.lineSpace.removeChild(cm.display.dragCursor);
3937
+ cm.display.dragCursor = null;
3938
+ }
3939
+ }
3940
+
3941
+ // SCROLL EVENTS
3942
+
3943
+ // Sync the scrollable area and scrollbars, ensure the viewport
3944
+ // covers the visible area.
3945
+ function setScrollTop(cm, val) {
3946
+ if (Math.abs(cm.doc.scrollTop - val) < 2) return;
3947
+ cm.doc.scrollTop = val;
3948
+ if (!gecko) updateDisplaySimple(cm, {top: val});
3949
+ if (cm.display.scroller.scrollTop != val) cm.display.scroller.scrollTop = val;
3950
+ cm.display.scrollbars.setScrollTop(val);
3951
+ if (gecko) updateDisplaySimple(cm);
3952
+ startWorker(cm, 100);
3953
+ }
3954
+ // Sync scroller and scrollbar, ensure the gutter elements are
3955
+ // aligned.
3956
+ function setScrollLeft(cm, val, isScroller) {
3957
+ if (isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) return;
3958
+ val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth);
3959
+ cm.doc.scrollLeft = val;
3960
+ alignHorizontally(cm);
3961
+ if (cm.display.scroller.scrollLeft != val) cm.display.scroller.scrollLeft = val;
3962
+ cm.display.scrollbars.setScrollLeft(val);
3963
+ }
3964
+
3965
+ // Since the delta values reported on mouse wheel events are
3966
+ // unstandardized between browsers and even browser versions, and
3967
+ // generally horribly unpredictable, this code starts by measuring
3968
+ // the scroll effect that the first few mouse wheel events have,
3969
+ // and, from that, detects the way it can convert deltas to pixel
3970
+ // offsets afterwards.
3971
+ //
3972
+ // The reason we want to know the amount a wheel event will scroll
3973
+ // is that it gives us a chance to update the display before the
3974
+ // actual scrolling happens, reducing flickering.
3975
+
3976
+ var wheelSamples = 0, wheelPixelsPerUnit = null;
3977
+ // Fill in a browser-detected starting value on browsers where we
3978
+ // know one. These don't have to be accurate -- the result of them
3979
+ // being wrong would just be a slight flicker on the first wheel
3980
+ // scroll (if it is large enough).
3981
+ if (ie) wheelPixelsPerUnit = -.53;
3982
+ else if (gecko) wheelPixelsPerUnit = 15;
3983
+ else if (chrome) wheelPixelsPerUnit = -.7;
3984
+ else if (safari) wheelPixelsPerUnit = -1/3;
3985
+
3986
+ var wheelEventDelta = function(e) {
3987
+ var dx = e.wheelDeltaX, dy = e.wheelDeltaY;
3988
+ if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) dx = e.detail;
3989
+ if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) dy = e.detail;
3990
+ else if (dy == null) dy = e.wheelDelta;
3991
+ return {x: dx, y: dy};
3992
+ };
3993
+ CodeMirror.wheelEventPixels = function(e) {
3994
+ var delta = wheelEventDelta(e);
3995
+ delta.x *= wheelPixelsPerUnit;
3996
+ delta.y *= wheelPixelsPerUnit;
3997
+ return delta;
3998
+ };
3999
+
4000
+ function onScrollWheel(cm, e) {
4001
+ var delta = wheelEventDelta(e), dx = delta.x, dy = delta.y;
4002
+
4003
+ var display = cm.display, scroll = display.scroller;
4004
+ // Quit if there's nothing to scroll here
4005
+ var canScrollX = scroll.scrollWidth > scroll.clientWidth;
4006
+ var canScrollY = scroll.scrollHeight > scroll.clientHeight;
4007
+ if (!(dx && canScrollX || dy && canScrollY)) return;
4008
+
4009
+ // Webkit browsers on OS X abort momentum scrolls when the target
4010
+ // of the scroll event is removed from the scrollable element.
4011
+ // This hack (see related code in patchDisplay) makes sure the
4012
+ // element is kept around.
4013
+ if (dy && mac && webkit) {
4014
+ outer: for (var cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) {
4015
+ for (var i = 0; i < view.length; i++) {
4016
+ if (view[i].node == cur) {
4017
+ cm.display.currentWheelTarget = cur;
4018
+ break outer;
4019
+ }
4020
+ }
4021
+ }
4022
+ }
4023
+
4024
+ // On some browsers, horizontal scrolling will cause redraws to
4025
+ // happen before the gutter has been realigned, causing it to
4026
+ // wriggle around in a most unseemly way. When we have an
4027
+ // estimated pixels/delta value, we just handle horizontal
4028
+ // scrolling entirely here. It'll be slightly off from native, but
4029
+ // better than glitching out.
4030
+ if (dx && !gecko && !presto && wheelPixelsPerUnit != null) {
4031
+ if (dy && canScrollY)
4032
+ setScrollTop(cm, Math.max(0, Math.min(scroll.scrollTop + dy * wheelPixelsPerUnit, scroll.scrollHeight - scroll.clientHeight)));
4033
+ setScrollLeft(cm, Math.max(0, Math.min(scroll.scrollLeft + dx * wheelPixelsPerUnit, scroll.scrollWidth - scroll.clientWidth)));
4034
+ // Only prevent default scrolling if vertical scrolling is
4035
+ // actually possible. Otherwise, it causes vertical scroll
4036
+ // jitter on OSX trackpads when deltaX is small and deltaY
4037
+ // is large (issue #3579)
4038
+ if (!dy || (dy && canScrollY))
4039
+ e_preventDefault(e);
4040
+ display.wheelStartX = null; // Abort measurement, if in progress
4041
+ return;
4042
+ }
4043
+
4044
+ // 'Project' the visible viewport to cover the area that is being
4045
+ // scrolled into view (if we know enough to estimate it).
4046
+ if (dy && wheelPixelsPerUnit != null) {
4047
+ var pixels = dy * wheelPixelsPerUnit;
4048
+ var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight;
4049
+ if (pixels < 0) top = Math.max(0, top + pixels - 50);
4050
+ else bot = Math.min(cm.doc.height, bot + pixels + 50);
4051
+ updateDisplaySimple(cm, {top: top, bottom: bot});
4052
+ }
4053
+
4054
+ if (wheelSamples < 20) {
4055
+ if (display.wheelStartX == null) {
4056
+ display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop;
4057
+ display.wheelDX = dx; display.wheelDY = dy;
4058
+ setTimeout(function() {
4059
+ if (display.wheelStartX == null) return;
4060
+ var movedX = scroll.scrollLeft - display.wheelStartX;
4061
+ var movedY = scroll.scrollTop - display.wheelStartY;
4062
+ var sample = (movedY && display.wheelDY && movedY / display.wheelDY) ||
4063
+ (movedX && display.wheelDX && movedX / display.wheelDX);
4064
+ display.wheelStartX = display.wheelStartY = null;
4065
+ if (!sample) return;
4066
+ wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1);
4067
+ ++wheelSamples;
4068
+ }, 200);
4069
+ } else {
4070
+ display.wheelDX += dx; display.wheelDY += dy;
4071
+ }
4072
+ }
4073
+ }
4074
+
4075
+ // KEY EVENTS
4076
+
4077
+ // Run a handler that was bound to a key.
4078
+ function doHandleBinding(cm, bound, dropShift) {
4079
+ if (typeof bound == "string") {
4080
+ bound = commands[bound];
4081
+ if (!bound) return false;
4082
+ }
4083
+ // Ensure previous input has been read, so that the handler sees a
4084
+ // consistent view of the document
4085
+ cm.display.input.ensurePolled();
4086
+ var prevShift = cm.display.shift, done = false;
4087
+ try {
4088
+ if (cm.isReadOnly()) cm.state.suppressEdits = true;
4089
+ if (dropShift) cm.display.shift = false;
4090
+ done = bound(cm) != Pass;
4091
+ } finally {
4092
+ cm.display.shift = prevShift;
4093
+ cm.state.suppressEdits = false;
4094
+ }
4095
+ return done;
4096
+ }
4097
+
4098
+ function lookupKeyForEditor(cm, name, handle) {
4099
+ for (var i = 0; i < cm.state.keyMaps.length; i++) {
4100
+ var result = lookupKey(name, cm.state.keyMaps[i], handle, cm);
4101
+ if (result) return result;
4102
+ }
4103
+ return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle, cm))
4104
+ || lookupKey(name, cm.options.keyMap, handle, cm);
4105
+ }
4106
+
4107
+ var stopSeq = new Delayed;
4108
+ function dispatchKey(cm, name, e, handle) {
4109
+ var seq = cm.state.keySeq;
4110
+ if (seq) {
4111
+ if (isModifierKey(name)) return "handled";
4112
+ stopSeq.set(50, function() {
4113
+ if (cm.state.keySeq == seq) {
4114
+ cm.state.keySeq = null;
4115
+ cm.display.input.reset();
4116
+ }
4117
+ });
4118
+ name = seq + " " + name;
4119
+ }
4120
+ var result = lookupKeyForEditor(cm, name, handle);
4121
+
4122
+ if (result == "multi")
4123
+ cm.state.keySeq = name;
4124
+ if (result == "handled")
4125
+ signalLater(cm, "keyHandled", cm, name, e);
4126
+
4127
+ if (result == "handled" || result == "multi") {
4128
+ e_preventDefault(e);
4129
+ restartBlink(cm);
4130
+ }
4131
+
4132
+ if (seq && !result && /\'$/.test(name)) {
4133
+ e_preventDefault(e);
4134
+ return true;
4135
+ }
4136
+ return !!result;
4137
+ }
4138
+
4139
+ // Handle a key from the keydown event.
4140
+ function handleKeyBinding(cm, e) {
4141
+ var name = keyName(e, true);
4142
+ if (!name) return false;
4143
+
4144
+ if (e.shiftKey && !cm.state.keySeq) {
4145
+ // First try to resolve full name (including 'Shift-'). Failing
4146
+ // that, see if there is a cursor-motion command (starting with
4147
+ // 'go') bound to the keyname without 'Shift-'.
4148
+ return dispatchKey(cm, "Shift-" + name, e, function(b) {return doHandleBinding(cm, b, true);})
4149
+ || dispatchKey(cm, name, e, function(b) {
4150
+ if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion)
4151
+ return doHandleBinding(cm, b);
4152
+ });
4153
+ } else {
4154
+ return dispatchKey(cm, name, e, function(b) { return doHandleBinding(cm, b); });
4155
+ }
4156
+ }
4157
+
4158
+ // Handle a key from the keypress event
4159
+ function handleCharBinding(cm, e, ch) {
4160
+ return dispatchKey(cm, "'" + ch + "'", e,
4161
+ function(b) { return doHandleBinding(cm, b, true); });
4162
+ }
4163
+
4164
+ var lastStoppedKey = null;
4165
+ function onKeyDown(e) {
4166
+ var cm = this;
4167
+ cm.curOp.focus = activeElt();
4168
+ if (signalDOMEvent(cm, e)) return;
4169
+ // IE does strange things with escape.
4170
+ if (ie && ie_version < 11 && e.keyCode == 27) e.returnValue = false;
4171
+ var code = e.keyCode;
4172
+ cm.display.shift = code == 16 || e.shiftKey;
4173
+ var handled = handleKeyBinding(cm, e);
4174
+ if (presto) {
4175
+ lastStoppedKey = handled ? code : null;
4176
+ // Opera has no cut event... we try to at least catch the key combo
4177
+ if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey))
4178
+ cm.replaceSelection("", null, "cut");
4179
+ }
4180
+
4181
+ // Turn mouse into crosshair when Alt is held on Mac.
4182
+ if (code == 18 && !/\bCodeMirror-crosshair\b/.test(cm.display.lineDiv.className))
4183
+ showCrossHair(cm);
4184
+ }
4185
+
4186
+ function showCrossHair(cm) {
4187
+ var lineDiv = cm.display.lineDiv;
4188
+ addClass(lineDiv, "CodeMirror-crosshair");
4189
+
4190
+ function up(e) {
4191
+ if (e.keyCode == 18 || !e.altKey) {
4192
+ rmClass(lineDiv, "CodeMirror-crosshair");
4193
+ off(document, "keyup", up);
4194
+ off(document, "mouseover", up);
4195
+ }
4196
+ }
4197
+ on(document, "keyup", up);
4198
+ on(document, "mouseover", up);
4199
+ }
4200
+
4201
+ function onKeyUp(e) {
4202
+ if (e.keyCode == 16) this.doc.sel.shift = false;
4203
+ signalDOMEvent(this, e);
4204
+ }
4205
+
4206
+ function onKeyPress(e) {
4207
+ var cm = this;
4208
+ if (eventInWidget(cm.display, e) || signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) return;
4209
+ var keyCode = e.keyCode, charCode = e.charCode;
4210
+ if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;}
4211
+ if ((presto && (!e.which || e.which < 10)) && handleKeyBinding(cm, e)) return;
4212
+ var ch = String.fromCharCode(charCode == null ? keyCode : charCode);
4213
+ if (handleCharBinding(cm, e, ch)) return;
4214
+ cm.display.input.onKeyPress(e);
4215
+ }
4216
+
4217
+ // FOCUS/BLUR EVENTS
4218
+
4219
+ function delayBlurEvent(cm) {
4220
+ cm.state.delayingBlurEvent = true;
4221
+ setTimeout(function() {
4222
+ if (cm.state.delayingBlurEvent) {
4223
+ cm.state.delayingBlurEvent = false;
4224
+ onBlur(cm);
4225
+ }
4226
+ }, 100);
4227
+ }
4228
+
4229
+ function onFocus(cm) {
4230
+ if (cm.state.delayingBlurEvent) cm.state.delayingBlurEvent = false;
4231
+
4232
+ if (cm.options.readOnly == "nocursor") return;
4233
+ if (!cm.state.focused) {
4234
+ signal(cm, "focus", cm);
4235
+ cm.state.focused = true;
4236
+ addClass(cm.display.wrapper, "CodeMirror-focused");
4237
+ // This test prevents this from firing when a context
4238
+ // menu is closed (since the input reset would kill the
4239
+ // select-all detection hack)
4240
+ if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) {
4241
+ cm.display.input.reset();
4242
+ if (webkit) setTimeout(function() { cm.display.input.reset(true); }, 20); // Issue #1730
4243
+ }
4244
+ cm.display.input.receivedFocus();
4245
+ }
4246
+ restartBlink(cm);
4247
+ }
4248
+ function onBlur(cm) {
4249
+ if (cm.state.delayingBlurEvent) return;
4250
+
4251
+ if (cm.state.focused) {
4252
+ signal(cm, "blur", cm);
4253
+ cm.state.focused = false;
4254
+ rmClass(cm.display.wrapper, "CodeMirror-focused");
4255
+ }
4256
+ clearInterval(cm.display.blinker);
4257
+ setTimeout(function() {if (!cm.state.focused) cm.display.shift = false;}, 150);
4258
+ }
4259
+
4260
+ // CONTEXT MENU HANDLING
4261
+
4262
+ // To make the context menu work, we need to briefly unhide the
4263
+ // textarea (making it as unobtrusive as possible) to let the
4264
+ // right-click take effect on it.
4265
+ function onContextMenu(cm, e) {
4266
+ if (eventInWidget(cm.display, e) || contextMenuInGutter(cm, e)) return;
4267
+ if (signalDOMEvent(cm, e, "contextmenu")) return;
4268
+ cm.display.input.onContextMenu(e);
4269
+ }
4270
+
4271
+ function contextMenuInGutter(cm, e) {
4272
+ if (!hasHandler(cm, "gutterContextMenu")) return false;
4273
+ return gutterEvent(cm, e, "gutterContextMenu", false);
4274
+ }
4275
+
4276
+ // UPDATING
4277
+
4278
+ // Compute the position of the end of a change (its 'to' property
4279
+ // refers to the pre-change end).
4280
+ var changeEnd = CodeMirror.changeEnd = function(change) {
4281
+ if (!change.text) return change.to;
4282
+ return Pos(change.from.line + change.text.length - 1,
4283
+ lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0));
4284
+ };
4285
+
4286
+ // Adjust a position to refer to the post-change position of the
4287
+ // same text, or the end of the change if the change covers it.
4288
+ function adjustForChange(pos, change) {
4289
+ if (cmp(pos, change.from) < 0) return pos;
4290
+ if (cmp(pos, change.to) <= 0) return changeEnd(change);
4291
+
4292
+ var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch;
4293
+ if (pos.line == change.to.line) ch += changeEnd(change).ch - change.to.ch;
4294
+ return Pos(line, ch);
4295
+ }
4296
+
4297
+ function computeSelAfterChange(doc, change) {
4298
+ var out = [];
4299
+ for (var i = 0; i < doc.sel.ranges.length; i++) {
4300
+ var range = doc.sel.ranges[i];
4301
+ out.push(new Range(adjustForChange(range.anchor, change),
4302
+ adjustForChange(range.head, change)));
4303
+ }
4304
+ return normalizeSelection(out, doc.sel.primIndex);
4305
+ }
4306
+
4307
+ function offsetPos(pos, old, nw) {
4308
+ if (pos.line == old.line)
4309
+ return Pos(nw.line, pos.ch - old.ch + nw.ch);
4310
+ else
4311
+ return Pos(nw.line + (pos.line - old.line), pos.ch);
4312
+ }
4313
+
4314
+ // Used by replaceSelections to allow moving the selection to the
4315
+ // start or around the replaced test. Hint may be "start" or "around".
4316
+ function computeReplacedSel(doc, changes, hint) {
4317
+ var out = [];
4318
+ var oldPrev = Pos(doc.first, 0), newPrev = oldPrev;
4319
+ for (var i = 0; i < changes.length; i++) {
4320
+ var change = changes[i];
4321
+ var from = offsetPos(change.from, oldPrev, newPrev);
4322
+ var to = offsetPos(changeEnd(change), oldPrev, newPrev);
4323
+ oldPrev = change.to;
4324
+ newPrev = to;
4325
+ if (hint == "around") {
4326
+ var range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) < 0;
4327
+ out[i] = new Range(inv ? to : from, inv ? from : to);
4328
+ } else {
4329
+ out[i] = new Range(from, from);
4330
+ }
4331
+ }
4332
+ return new Selection(out, doc.sel.primIndex);
4333
+ }
4334
+
4335
+ // Allow "beforeChange" event handlers to influence a change
4336
+ function filterChange(doc, change, update) {
4337
+ var obj = {
4338
+ canceled: false,
4339
+ from: change.from,
4340
+ to: change.to,
4341
+ text: change.text,
4342
+ origin: change.origin,
4343
+ cancel: function() { this.canceled = true; }
4344
+ };
4345
+ if (update) obj.update = function(from, to, text, origin) {
4346
+ if (from) this.from = clipPos(doc, from);
4347
+ if (to) this.to = clipPos(doc, to);
4348
+ if (text) this.text = text;
4349
+ if (origin !== undefined) this.origin = origin;
4350
+ };
4351
+ signal(doc, "beforeChange", doc, obj);
4352
+ if (doc.cm) signal(doc.cm, "beforeChange", doc.cm, obj);
4353
+
4354
+ if (obj.canceled) return null;
4355
+ return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin};
4356
+ }
4357
+
4358
+ // Apply a change to a document, and add it to the document's
4359
+ // history, and propagating it to all linked documents.
4360
+ function makeChange(doc, change, ignoreReadOnly) {
4361
+ if (doc.cm) {
4362
+ if (!doc.cm.curOp) return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly);
4363
+ if (doc.cm.state.suppressEdits) return;
4364
+ }
4365
+
4366
+ if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) {
4367
+ change = filterChange(doc, change, true);
4368
+ if (!change) return;
4369
+ }
4370
+
4371
+ // Possibly split or suppress the update based on the presence
4372
+ // of read-only spans in its range.
4373
+ var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to);
4374
+ if (split) {
4375
+ for (var i = split.length - 1; i >= 0; --i)
4376
+ makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [""] : change.text});
4377
+ } else {
4378
+ makeChangeInner(doc, change);
4379
+ }
4380
+ }
4381
+
4382
+ function makeChangeInner(doc, change) {
4383
+ if (change.text.length == 1 && change.text[0] == "" && cmp(change.from, change.to) == 0) return;
4384
+ var selAfter = computeSelAfterChange(doc, change);
4385
+ addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN);
4386
+
4387
+ makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change));
4388
+ var rebased = [];
4389
+
4390
+ linkedDocs(doc, function(doc, sharedHist) {
4391
+ if (!sharedHist && indexOf(rebased, doc.history) == -1) {
4392
+ rebaseHist(doc.history, change);
4393
+ rebased.push(doc.history);
4394
+ }
4395
+ makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change));
4396
+ });
4397
+ }
4398
+
4399
+ // Revert a change stored in a document's history.
4400
+ function makeChangeFromHistory(doc, type, allowSelectionOnly) {
4401
+ if (doc.cm && doc.cm.state.suppressEdits) return;
4402
+
4403
+ var hist = doc.history, event, selAfter = doc.sel;
4404
+ var source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done;
4405
+
4406
+ // Verify that there is a useable event (so that ctrl-z won't
4407
+ // needlessly clear selection events)
4408
+ for (var i = 0; i < source.length; i++) {
4409
+ event = source[i];
4410
+ if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ranges)
4411
+ break;
4412
+ }
4413
+ if (i == source.length) return;
4414
+ hist.lastOrigin = hist.lastSelOrigin = null;
4415
+
4416
+ for (;;) {
4417
+ event = source.pop();
4418
+ if (event.ranges) {
4419
+ pushSelectionToHistory(event, dest);
4420
+ if (allowSelectionOnly && !event.equals(doc.sel)) {
4421
+ setSelection(doc, event, {clearRedo: false});
4422
+ return;
4423
+ }
4424
+ selAfter = event;
4425
+ }
4426
+ else break;
4427
+ }
4428
+
4429
+ // Build up a reverse change object to add to the opposite history
4430
+ // stack (redo when undoing, and vice versa).
4431
+ var antiChanges = [];
4432
+ pushSelectionToHistory(selAfter, dest);
4433
+ dest.push({changes: antiChanges, generation: hist.generation});
4434
+ hist.generation = event.generation || ++hist.maxGeneration;
4435
+
4436
+ var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange");
4437
+
4438
+ for (var i = event.changes.length - 1; i >= 0; --i) {
4439
+ var change = event.changes[i];
4440
+ change.origin = type;
4441
+ if (filter && !filterChange(doc, change, false)) {
4442
+ source.length = 0;
4443
+ return;
4444
+ }
4445
+
4446
+ antiChanges.push(historyChangeFromChange(doc, change));
4447
+
4448
+ var after = i ? computeSelAfterChange(doc, change) : lst(source);
4449
+ makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change));
4450
+ if (!i && doc.cm) doc.cm.scrollIntoView({from: change.from, to: changeEnd(change)});
4451
+ var rebased = [];
4452
+
4453
+ // Propagate to the linked documents
4454
+ linkedDocs(doc, function(doc, sharedHist) {
4455
+ if (!sharedHist && indexOf(rebased, doc.history) == -1) {
4456
+ rebaseHist(doc.history, change);
4457
+ rebased.push(doc.history);
4458
+ }
4459
+ makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change));
4460
+ });
4461
+ }
4462
+ }
4463
+
4464
+ // Sub-views need their line numbers shifted when text is added
4465
+ // above or below them in the parent document.
4466
+ function shiftDoc(doc, distance) {
4467
+ if (distance == 0) return;
4468
+ doc.first += distance;
4469
+ doc.sel = new Selection(map(doc.sel.ranges, function(range) {
4470
+ return new Range(Pos(range.anchor.line + distance, range.anchor.ch),
4471
+ Pos(range.head.line + distance, range.head.ch));
4472
+ }), doc.sel.primIndex);
4473
+ if (doc.cm) {
4474
+ regChange(doc.cm, doc.first, doc.first - distance, distance);
4475
+ for (var d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++)
4476
+ regLineChange(doc.cm, l, "gutter");
4477
+ }
4478
+ }
4479
+
4480
+ // More lower-level change function, handling only a single document
4481
+ // (not linked ones).
4482
+ function makeChangeSingleDoc(doc, change, selAfter, spans) {
4483
+ if (doc.cm && !doc.cm.curOp)
4484
+ return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans);
4485
+
4486
+ if (change.to.line < doc.first) {
4487
+ shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line));
4488
+ return;
4489
+ }
4490
+ if (change.from.line > doc.lastLine()) return;
4491
+
4492
+ // Clip the change to the size of this doc
4493
+ if (change.from.line < doc.first) {
4494
+ var shift = change.text.length - 1 - (doc.first - change.from.line);
4495
+ shiftDoc(doc, shift);
4496
+ change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch),
4497
+ text: [lst(change.text)], origin: change.origin};
4498
+ }
4499
+ var last = doc.lastLine();
4500
+ if (change.to.line > last) {
4501
+ change = {from: change.from, to: Pos(last, getLine(doc, last).text.length),
4502
+ text: [change.text[0]], origin: change.origin};
4503
+ }
4504
+
4505
+ change.removed = getBetween(doc, change.from, change.to);
4506
+
4507
+ if (!selAfter) selAfter = computeSelAfterChange(doc, change);
4508
+ if (doc.cm) makeChangeSingleDocInEditor(doc.cm, change, spans);
4509
+ else updateDoc(doc, change, spans);
4510
+ setSelectionNoUndo(doc, selAfter, sel_dontScroll);
4511
+ }
4512
+
4513
+ // Handle the interaction of a change to a document with the editor
4514
+ // that this document is part of.
4515
+ function makeChangeSingleDocInEditor(cm, change, spans) {
4516
+ var doc = cm.doc, display = cm.display, from = change.from, to = change.to;
4517
+
4518
+ var recomputeMaxLength = false, checkWidthStart = from.line;
4519
+ if (!cm.options.lineWrapping) {
4520
+ checkWidthStart = lineNo(visualLine(getLine(doc, from.line)));
4521
+ doc.iter(checkWidthStart, to.line + 1, function(line) {
4522
+ if (line == display.maxLine) {
4523
+ recomputeMaxLength = true;
4524
+ return true;
4525
+ }
4526
+ });
4527
+ }
4528
+
4529
+ if (doc.sel.contains(change.from, change.to) > -1)
4530
+ signalCursorActivity(cm);
4531
+
4532
+ updateDoc(doc, change, spans, estimateHeight(cm));
4533
+
4534
+ if (!cm.options.lineWrapping) {
4535
+ doc.iter(checkWidthStart, from.line + change.text.length, function(line) {
4536
+ var len = lineLength(line);
4537
+ if (len > display.maxLineLength) {
4538
+ display.maxLine = line;
4539
+ display.maxLineLength = len;
4540
+ display.maxLineChanged = true;
4541
+ recomputeMaxLength = false;
4542
+ }
4543
+ });
4544
+ if (recomputeMaxLength) cm.curOp.updateMaxLine = true;
4545
+ }
4546
+
4547
+ // Adjust frontier, schedule worker
4548
+ doc.frontier = Math.min(doc.frontier, from.line);
4549
+ startWorker(cm, 400);
4550
+
4551
+ var lendiff = change.text.length - (to.line - from.line) - 1;
4552
+ // Remember that these lines changed, for updating the display
4553
+ if (change.full)
4554
+ regChange(cm);
4555
+ else if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change))
4556
+ regLineChange(cm, from.line, "text");
4557
+ else
4558
+ regChange(cm, from.line, to.line + 1, lendiff);
4559
+
4560
+ var changesHandler = hasHandler(cm, "changes"), changeHandler = hasHandler(cm, "change");
4561
+ if (changeHandler || changesHandler) {
4562
+ var obj = {
4563
+ from: from, to: to,
4564
+ text: change.text,
4565
+ removed: change.removed,
4566
+ origin: change.origin
4567
+ };
4568
+ if (changeHandler) signalLater(cm, "change", cm, obj);
4569
+ if (changesHandler) (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj);
4570
+ }
4571
+ cm.display.selForContextMenu = null;
4572
+ }
4573
+
4574
+ function replaceRange(doc, code, from, to, origin) {
4575
+ if (!to) to = from;
4576
+ if (cmp(to, from) < 0) { var tmp = to; to = from; from = tmp; }
4577
+ if (typeof code == "string") code = doc.splitLines(code);
4578
+ makeChange(doc, {from: from, to: to, text: code, origin: origin});
4579
+ }
4580
+
4581
+ // SCROLLING THINGS INTO VIEW
4582
+
4583
+ // If an editor sits on the top or bottom of the window, partially
4584
+ // scrolled out of view, this ensures that the cursor is visible.
4585
+ function maybeScrollWindow(cm, coords) {
4586
+ if (signalDOMEvent(cm, "scrollCursorIntoView")) return;
4587
+
4588
+ var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null;
4589
+ if (coords.top + box.top < 0) doScroll = true;
4590
+ else if (coords.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false;
4591
+ if (doScroll != null && !phantom) {
4592
+ var scrollNode = elt("div", "\u200b", null, "position: absolute; top: " +
4593
+ (coords.top - display.viewOffset - paddingTop(cm.display)) + "px; height: " +
4594
+ (coords.bottom - coords.top + scrollGap(cm) + display.barHeight) + "px; left: " +
4595
+ coords.left + "px; width: 2px;");
4596
+ cm.display.lineSpace.appendChild(scrollNode);
4597
+ scrollNode.scrollIntoView(doScroll);
4598
+ cm.display.lineSpace.removeChild(scrollNode);
4599
+ }
4600
+ }
4601
+
4602
+ // Scroll a given position into view (immediately), verifying that
4603
+ // it actually became visible (as line heights are accurately
4604
+ // measured, the position of something may 'drift' during drawing).
4605
+ function scrollPosIntoView(cm, pos, end, margin) {
4606
+ if (margin == null) margin = 0;
4607
+ for (var limit = 0; limit < 5; limit++) {
4608
+ var changed = false, coords = cursorCoords(cm, pos);
4609
+ var endCoords = !end || end == pos ? coords : cursorCoords(cm, end);
4610
+ var scrollPos = calculateScrollPos(cm, Math.min(coords.left, endCoords.left),
4611
+ Math.min(coords.top, endCoords.top) - margin,
4612
+ Math.max(coords.left, endCoords.left),
4613
+ Math.max(coords.bottom, endCoords.bottom) + margin);
4614
+ var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft;
4615
+ if (scrollPos.scrollTop != null) {
4616
+ setScrollTop(cm, scrollPos.scrollTop);
4617
+ if (Math.abs(cm.doc.scrollTop - startTop) > 1) changed = true;
4618
+ }
4619
+ if (scrollPos.scrollLeft != null) {
4620
+ setScrollLeft(cm, scrollPos.scrollLeft);
4621
+ if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) changed = true;
4622
+ }
4623
+ if (!changed) break;
4624
+ }
4625
+ return coords;
4626
+ }
4627
+
4628
+ // Scroll a given set of coordinates into view (immediately).
4629
+ function scrollIntoView(cm, x1, y1, x2, y2) {
4630
+ var scrollPos = calculateScrollPos(cm, x1, y1, x2, y2);
4631
+ if (scrollPos.scrollTop != null) setScrollTop(cm, scrollPos.scrollTop);
4632
+ if (scrollPos.scrollLeft != null) setScrollLeft(cm, scrollPos.scrollLeft);
4633
+ }
4634
+
4635
+ // Calculate a new scroll position needed to scroll the given
4636
+ // rectangle into view. Returns an object with scrollTop and
4637
+ // scrollLeft properties. When these are undefined, the
4638
+ // vertical/horizontal position does not need to be adjusted.
4639
+ function calculateScrollPos(cm, x1, y1, x2, y2) {
4640
+ var display = cm.display, snapMargin = textHeight(cm.display);
4641
+ if (y1 < 0) y1 = 0;
4642
+ var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop;
4643
+ var screen = displayHeight(cm), result = {};
4644
+ if (y2 - y1 > screen) y2 = y1 + screen;
4645
+ var docBottom = cm.doc.height + paddingVert(display);
4646
+ var atTop = y1 < snapMargin, atBottom = y2 > docBottom - snapMargin;
4647
+ if (y1 < screentop) {
4648
+ result.scrollTop = atTop ? 0 : y1;
4649
+ } else if (y2 > screentop + screen) {
4650
+ var newTop = Math.min(y1, (atBottom ? docBottom : y2) - screen);
4651
+ if (newTop != screentop) result.scrollTop = newTop;
4652
+ }
4653
+
4654
+ var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft;
4655
+ var screenw = displayWidth(cm) - (cm.options.fixedGutter ? display.gutters.offsetWidth : 0);
4656
+ var tooWide = x2 - x1 > screenw;
4657
+ if (tooWide) x2 = x1 + screenw;
4658
+ if (x1 < 10)
4659
+ result.scrollLeft = 0;
4660
+ else if (x1 < screenleft)
4661
+ result.scrollLeft = Math.max(0, x1 - (tooWide ? 0 : 10));
4662
+ else if (x2 > screenw + screenleft - 3)
4663
+ result.scrollLeft = x2 + (tooWide ? 0 : 10) - screenw;
4664
+ return result;
4665
+ }
4666
+
4667
+ // Store a relative adjustment to the scroll position in the current
4668
+ // operation (to be applied when the operation finishes).
4669
+ function addToScrollPos(cm, left, top) {
4670
+ if (left != null || top != null) resolveScrollToPos(cm);
4671
+ if (left != null)
4672
+ cm.curOp.scrollLeft = (cm.curOp.scrollLeft == null ? cm.doc.scrollLeft : cm.curOp.scrollLeft) + left;
4673
+ if (top != null)
4674
+ cm.curOp.scrollTop = (cm.curOp.scrollTop == null ? cm.doc.scrollTop : cm.curOp.scrollTop) + top;
4675
+ }
4676
+
4677
+ // Make sure that at the end of the operation the current cursor is
4678
+ // shown.
4679
+ function ensureCursorVisible(cm) {
4680
+ resolveScrollToPos(cm);
4681
+ var cur = cm.getCursor(), from = cur, to = cur;
4682
+ if (!cm.options.lineWrapping) {
4683
+ from = cur.ch ? Pos(cur.line, cur.ch - 1) : cur;
4684
+ to = Pos(cur.line, cur.ch + 1);
4685
+ }
4686
+ cm.curOp.scrollToPos = {from: from, to: to, margin: cm.options.cursorScrollMargin, isCursor: true};
4687
+ }
4688
+
4689
+ // When an operation has its scrollToPos property set, and another
4690
+ // scroll action is applied before the end of the operation, this
4691
+ // 'simulates' scrolling that position into view in a cheap way, so
4692
+ // that the effect of intermediate scroll commands is not ignored.
4693
+ function resolveScrollToPos(cm) {
4694
+ var range = cm.curOp.scrollToPos;
4695
+ if (range) {
4696
+ cm.curOp.scrollToPos = null;
4697
+ var from = estimateCoords(cm, range.from), to = estimateCoords(cm, range.to);
4698
+ var sPos = calculateScrollPos(cm, Math.min(from.left, to.left),
4699
+ Math.min(from.top, to.top) - range.margin,
4700
+ Math.max(from.right, to.right),
4701
+ Math.max(from.bottom, to.bottom) + range.margin);
4702
+ cm.scrollTo(sPos.scrollLeft, sPos.scrollTop);
4703
+ }
4704
+ }
4705
+
4706
+ // API UTILITIES
4707
+
4708
+ // Indent the given line. The how parameter can be "smart",
4709
+ // "add"/null, "subtract", or "prev". When aggressive is false
4710
+ // (typically set to true for forced single-line indents), empty
4711
+ // lines are not indented, and places where the mode returns Pass
4712
+ // are left alone.
4713
+ function indentLine(cm, n, how, aggressive) {
4714
+ var doc = cm.doc, state;
4715
+ if (how == null) how = "add";
4716
+ if (how == "smart") {
4717
+ // Fall back to "prev" when the mode doesn't have an indentation
4718
+ // method.
4719
+ if (!doc.mode.indent) how = "prev";
4720
+ else state = getStateBefore(cm, n);
4721
+ }
4722
+
4723
+ var tabSize = cm.options.tabSize;
4724
+ var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize);
4725
+ if (line.stateAfter) line.stateAfter = null;
4726
+ var curSpaceString = line.text.match(/^\s*/)[0], indentation;
4727
+ if (!aggressive && !/\S/.test(line.text)) {
4728
+ indentation = 0;
4729
+ how = "not";
4730
+ } else if (how == "smart") {
4731
+ indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text);
4732
+ if (indentation == Pass || indentation > 150) {
4733
+ if (!aggressive) return;
4734
+ how = "prev";
4735
+ }
4736
+ }
4737
+ if (how == "prev") {
4738
+ if (n > doc.first) indentation = countColumn(getLine(doc, n-1).text, null, tabSize);
4739
+ else indentation = 0;
4740
+ } else if (how == "add") {
4741
+ indentation = curSpace + cm.options.indentUnit;
4742
+ } else if (how == "subtract") {
4743
+ indentation = curSpace - cm.options.indentUnit;
4744
+ } else if (typeof how == "number") {
4745
+ indentation = curSpace + how;
4746
+ }
4747
+ indentation = Math.max(0, indentation);
4748
+
4749
+ var indentString = "", pos = 0;
4750
+ if (cm.options.indentWithTabs)
4751
+ for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";}
4752
+ if (pos < indentation) indentString += spaceStr(indentation - pos);
4753
+
4754
+ if (indentString != curSpaceString) {
4755
+ replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input");
4756
+ line.stateAfter = null;
4757
+ return true;
4758
+ } else {
4759
+ // Ensure that, if the cursor was in the whitespace at the start
4760
+ // of the line, it is moved to the end of that space.
4761
+ for (var i = 0; i < doc.sel.ranges.length; i++) {
4762
+ var range = doc.sel.ranges[i];
4763
+ if (range.head.line == n && range.head.ch < curSpaceString.length) {
4764
+ var pos = Pos(n, curSpaceString.length);
4765
+ replaceOneSelection(doc, i, new Range(pos, pos));
4766
+ break;
4767
+ }
4768
+ }
4769
+ }
4770
+ }
4771
+
4772
+ // Utility for applying a change to a line by handle or number,
4773
+ // returning the number and optionally registering the line as
4774
+ // changed.
4775
+ function changeLine(doc, handle, changeType, op) {
4776
+ var no = handle, line = handle;
4777
+ if (typeof handle == "number") line = getLine(doc, clipLine(doc, handle));
4778
+ else no = lineNo(handle);
4779
+ if (no == null) return null;
4780
+ if (op(line, no) && doc.cm) regLineChange(doc.cm, no, changeType);
4781
+ return line;
4782
+ }
4783
+
4784
+ // Helper for deleting text near the selection(s), used to implement
4785
+ // backspace, delete, and similar functionality.
4786
+ function deleteNearSelection(cm, compute) {
4787
+ var ranges = cm.doc.sel.ranges, kill = [];
4788
+ // Build up a set of ranges to kill first, merging overlapping
4789
+ // ranges.
4790
+ for (var i = 0; i < ranges.length; i++) {
4791
+ var toKill = compute(ranges[i]);
4792
+ while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) {
4793
+ var replaced = kill.pop();
4794
+ if (cmp(replaced.from, toKill.from) < 0) {
4795
+ toKill.from = replaced.from;
4796
+ break;
4797
+ }
4798
+ }
4799
+ kill.push(toKill);
4800
+ }
4801
+ // Next, remove those actual ranges.
4802
+ runInOp(cm, function() {
4803
+ for (var i = kill.length - 1; i >= 0; i--)
4804
+ replaceRange(cm.doc, "", kill[i].from, kill[i].to, "+delete");
4805
+ ensureCursorVisible(cm);
4806
+ });
4807
+ }
4808
+
4809
+ // Used for horizontal relative motion. Dir is -1 or 1 (left or
4810
+ // right), unit can be "char", "column" (like char, but doesn't
4811
+ // cross line boundaries), "word" (across next word), or "group" (to
4812
+ // the start of next group of word or non-word-non-whitespace
4813
+ // chars). The visually param controls whether, in right-to-left
4814
+ // text, direction 1 means to move towards the next index in the
4815
+ // string, or towards the character to the right of the current
4816
+ // position. The resulting position will have a hitSide=true
4817
+ // property if it reached the end of the document.
4818
+ function findPosH(doc, pos, dir, unit, visually) {
4819
+ var line = pos.line, ch = pos.ch, origDir = dir;
4820
+ var lineObj = getLine(doc, line);
4821
+ function findNextLine() {
4822
+ var l = line + dir;
4823
+ if (l < doc.first || l >= doc.first + doc.size) return false
4824
+ line = l;
4825
+ return lineObj = getLine(doc, l);
4826
+ }
4827
+ function moveOnce(boundToLine) {
4828
+ var next = (visually ? moveVisually : moveLogically)(lineObj, ch, dir, true);
4829
+ if (next == null) {
4830
+ if (!boundToLine && findNextLine()) {
4831
+ if (visually) ch = (dir < 0 ? lineRight : lineLeft)(lineObj);
4832
+ else ch = dir < 0 ? lineObj.text.length : 0;
4833
+ } else return false
4834
+ } else ch = next;
4835
+ return true;
4836
+ }
4837
+
4838
+ if (unit == "char") {
4839
+ moveOnce()
4840
+ } else if (unit == "column") {
4841
+ moveOnce(true)
4842
+ } else if (unit == "word" || unit == "group") {
4843
+ var sawType = null, group = unit == "group";
4844
+ var helper = doc.cm && doc.cm.getHelper(pos, "wordChars");
4845
+ for (var first = true;; first = false) {
4846
+ if (dir < 0 && !moveOnce(!first)) break;
4847
+ var cur = lineObj.text.charAt(ch) || "\n";
4848
+ var type = isWordChar(cur, helper) ? "w"
4849
+ : group && cur == "\n" ? "n"
4850
+ : !group || /\s/.test(cur) ? null
4851
+ : "p";
4852
+ if (group && !first && !type) type = "s";
4853
+ if (sawType && sawType != type) {
4854
+ if (dir < 0) {dir = 1; moveOnce();}
4855
+ break;
4856
+ }
4857
+
4858
+ if (type) sawType = type;
4859
+ if (dir > 0 && !moveOnce(!first)) break;
4860
+ }
4861
+ }
4862
+ var result = skipAtomic(doc, Pos(line, ch), pos, origDir, true);
4863
+ if (!cmp(pos, result)) result.hitSide = true;
4864
+ return result;
4865
+ }
4866
+
4867
+ // For relative vertical movement. Dir may be -1 or 1. Unit can be
4868
+ // "page" or "line". The resulting position will have a hitSide=true
4869
+ // property if it reached the end of the document.
4870
+ function findPosV(cm, pos, dir, unit) {
4871
+ var doc = cm.doc, x = pos.left, y;
4872
+ if (unit == "page") {
4873
+ var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight);
4874
+ y = pos.top + dir * (pageSize - (dir < 0 ? 1.5 : .5) * textHeight(cm.display));
4875
+ } else if (unit == "line") {
4876
+ y = dir > 0 ? pos.bottom + 3 : pos.top - 3;
4877
+ }
4878
+ for (;;) {
4879
+ var target = coordsChar(cm, x, y);
4880
+ if (!target.outside) break;
4881
+ if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break; }
4882
+ y += dir * 5;
4883
+ }
4884
+ return target;
4885
+ }
4886
+
4887
+ // EDITOR METHODS
4888
+
4889
+ // The publicly visible API. Note that methodOp(f) means
4890
+ // 'wrap f in an operation, performed on its `this` parameter'.
4891
+
4892
+ // This is not the complete set of editor methods. Most of the
4893
+ // methods defined on the Doc type are also injected into
4894
+ // CodeMirror.prototype, for backwards compatibility and
4895
+ // convenience.
4896
+
4897
+ CodeMirror.prototype = {
4898
+ constructor: CodeMirror,
4899
+ focus: function(){window.focus(); this.display.input.focus();},
4900
+
4901
+ setOption: function(option, value) {
4902
+ var options = this.options, old = options[option];
4903
+ if (options[option] == value && option != "mode") return;
4904
+ options[option] = value;
4905
+ if (optionHandlers.hasOwnProperty(option))
4906
+ operation(this, optionHandlers[option])(this, value, old);
4907
+ },
4908
+
4909
+ getOption: function(option) {return this.options[option];},
4910
+ getDoc: function() {return this.doc;},
4911
+
4912
+ addKeyMap: function(map, bottom) {
4913
+ this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map));
4914
+ },
4915
+ removeKeyMap: function(map) {
4916
+ var maps = this.state.keyMaps;
4917
+ for (var i = 0; i < maps.length; ++i)
4918
+ if (maps[i] == map || maps[i].name == map) {
4919
+ maps.splice(i, 1);
4920
+ return true;
4921
+ }
4922
+ },
4923
+
4924
+ addOverlay: methodOp(function(spec, options) {
4925
+ var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec);
4926
+ if (mode.startState) throw new Error("Overlays may not be stateful.");
4927
+ this.state.overlays.push({mode: mode, modeSpec: spec, opaque: options && options.opaque});
4928
+ this.state.modeGen++;
4929
+ regChange(this);
4930
+ }),
4931
+ removeOverlay: methodOp(function(spec) {
4932
+ var overlays = this.state.overlays;
4933
+ for (var i = 0; i < overlays.length; ++i) {
4934
+ var cur = overlays[i].modeSpec;
4935
+ if (cur == spec || typeof spec == "string" && cur.name == spec) {
4936
+ overlays.splice(i, 1);
4937
+ this.state.modeGen++;
4938
+ regChange(this);
4939
+ return;
4940
+ }
4941
+ }
4942
+ }),
4943
+
4944
+ indentLine: methodOp(function(n, dir, aggressive) {
4945
+ if (typeof dir != "string" && typeof dir != "number") {
4946
+ if (dir == null) dir = this.options.smartIndent ? "smart" : "prev";
4947
+ else dir = dir ? "add" : "subtract";
4948
+ }
4949
+ if (isLine(this.doc, n)) indentLine(this, n, dir, aggressive);
4950
+ }),
4951
+ indentSelection: methodOp(function(how) {
4952
+ var ranges = this.doc.sel.ranges, end = -1;
4953
+ for (var i = 0; i < ranges.length; i++) {
4954
+ var range = ranges[i];
4955
+ if (!range.empty()) {
4956
+ var from = range.from(), to = range.to();
4957
+ var start = Math.max(end, from.line);
4958
+ end = Math.min(this.lastLine(), to.line - (to.ch ? 0 : 1)) + 1;
4959
+ for (var j = start; j < end; ++j)
4960
+ indentLine(this, j, how);
4961
+ var newRanges = this.doc.sel.ranges;
4962
+ if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].from().ch > 0)
4963
+ replaceOneSelection(this.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll);
4964
+ } else if (range.head.line > end) {
4965
+ indentLine(this, range.head.line, how, true);
4966
+ end = range.head.line;
4967
+ if (i == this.doc.sel.primIndex) ensureCursorVisible(this);
4968
+ }
4969
+ }
4970
+ }),
4971
+
4972
+ // Fetch the parser token for a given character. Useful for hacks
4973
+ // that want to inspect the mode state (say, for completion).
4974
+ getTokenAt: function(pos, precise) {
4975
+ return takeToken(this, pos, precise);
4976
+ },
4977
+
4978
+ getLineTokens: function(line, precise) {
4979
+ return takeToken(this, Pos(line), precise, true);
4980
+ },
4981
+
4982
+ getTokenTypeAt: function(pos) {
4983
+ pos = clipPos(this.doc, pos);
4984
+ var styles = getLineStyles(this, getLine(this.doc, pos.line));
4985
+ var before = 0, after = (styles.length - 1) / 2, ch = pos.ch;
4986
+ var type;
4987
+ if (ch == 0) type = styles[2];
4988
+ else for (;;) {
4989
+ var mid = (before + after) >> 1;
4990
+ if ((mid ? styles[mid * 2 - 1] : 0) >= ch) after = mid;
4991
+ else if (styles[mid * 2 + 1] < ch) before = mid + 1;
4992
+ else { type = styles[mid * 2 + 2]; break; }
4993
+ }
4994
+ var cut = type ? type.indexOf("cm-overlay ") : -1;
4995
+ return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1);
4996
+ },
4997
+
4998
+ getModeAt: function(pos) {
4999
+ var mode = this.doc.mode;
5000
+ if (!mode.innerMode) return mode;
5001
+ return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode;
5002
+ },
5003
+
5004
+ getHelper: function(pos, type) {
5005
+ return this.getHelpers(pos, type)[0];
5006
+ },
5007
+
5008
+ getHelpers: function(pos, type) {
5009
+ var found = [];
5010
+ if (!helpers.hasOwnProperty(type)) return found;
5011
+ var help = helpers[type], mode = this.getModeAt(pos);
5012
+ if (typeof mode[type] == "string") {
5013
+ if (help[mode[type]]) found.push(help[mode[type]]);
5014
+ } else if (mode[type]) {
5015
+ for (var i = 0; i < mode[type].length; i++) {
5016
+ var val = help[mode[type][i]];
5017
+ if (val) found.push(val);
5018
+ }
5019
+ } else if (mode.helperType && help[mode.helperType]) {
5020
+ found.push(help[mode.helperType]);
5021
+ } else if (help[mode.name]) {
5022
+ found.push(help[mode.name]);
5023
+ }
5024
+ for (var i = 0; i < help._global.length; i++) {
5025
+ var cur = help._global[i];
5026
+ if (cur.pred(mode, this) && indexOf(found, cur.val) == -1)
5027
+ found.push(cur.val);
5028
+ }
5029
+ return found;
5030
+ },
5031
+
5032
+ getStateAfter: function(line, precise) {
5033
+ var doc = this.doc;
5034
+ line = clipLine(doc, line == null ? doc.first + doc.size - 1: line);
5035
+ return getStateBefore(this, line + 1, precise);
5036
+ },
5037
+
5038
+ cursorCoords: function(start, mode) {
5039
+ var pos, range = this.doc.sel.primary();
5040
+ if (start == null) pos = range.head;
5041
+ else if (typeof start == "object") pos = clipPos(this.doc, start);
5042
+ else pos = start ? range.from() : range.to();
5043
+ return cursorCoords(this, pos, mode || "page");
5044
+ },
5045
+
5046
+ charCoords: function(pos, mode) {
5047
+ return charCoords(this, clipPos(this.doc, pos), mode || "page");
5048
+ },
5049
+
5050
+ coordsChar: function(coords, mode) {
5051
+ coords = fromCoordSystem(this, coords, mode || "page");
5052
+ return coordsChar(this, coords.left, coords.top);
5053
+ },
5054
+
5055
+ lineAtHeight: function(height, mode) {
5056
+ height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top;
5057
+ return lineAtHeight(this.doc, height + this.display.viewOffset);
5058
+ },
5059
+ heightAtLine: function(line, mode) {
5060
+ var end = false, lineObj;
5061
+ if (typeof line == "number") {
5062
+ var last = this.doc.first + this.doc.size - 1;
5063
+ if (line < this.doc.first) line = this.doc.first;
5064
+ else if (line > last) { line = last; end = true; }
5065
+ lineObj = getLine(this.doc, line);
5066
+ } else {
5067
+ lineObj = line;
5068
+ }
5069
+ return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || "page").top +
5070
+ (end ? this.doc.height - heightAtLine(lineObj) : 0);
5071
+ },
5072
+
5073
+ defaultTextHeight: function() { return textHeight(this.display); },
5074
+ defaultCharWidth: function() { return charWidth(this.display); },
5075
+
5076
+ setGutterMarker: methodOp(function(line, gutterID, value) {
5077
+ return changeLine(this.doc, line, "gutter", function(line) {
5078
+ var markers = line.gutterMarkers || (line.gutterMarkers = {});
5079
+ markers[gutterID] = value;
5080
+ if (!value && isEmpty(markers)) line.gutterMarkers = null;
5081
+ return true;
5082
+ });
5083
+ }),
5084
+
5085
+ clearGutter: methodOp(function(gutterID) {
5086
+ var cm = this, doc = cm.doc, i = doc.first;
5087
+ doc.iter(function(line) {
5088
+ if (line.gutterMarkers && line.gutterMarkers[gutterID]) {
5089
+ line.gutterMarkers[gutterID] = null;
5090
+ regLineChange(cm, i, "gutter");
5091
+ if (isEmpty(line.gutterMarkers)) line.gutterMarkers = null;
5092
+ }
5093
+ ++i;
5094
+ });
5095
+ }),
5096
+
5097
+ lineInfo: function(line) {
5098
+ if (typeof line == "number") {
5099
+ if (!isLine(this.doc, line)) return null;
5100
+ var n = line;
5101
+ line = getLine(this.doc, line);
5102
+ if (!line) return null;
5103
+ } else {
5104
+ var n = lineNo(line);
5105
+ if (n == null) return null;
5106
+ }
5107
+ return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers,
5108
+ textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass,
5109
+ widgets: line.widgets};
5110
+ },
5111
+
5112
+ getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo};},
5113
+
5114
+ addWidget: function(pos, node, scroll, vert, horiz) {
5115
+ var display = this.display;
5116
+ pos = cursorCoords(this, clipPos(this.doc, pos));
5117
+ var top = pos.bottom, left = pos.left;
5118
+ node.style.position = "absolute";
5119
+ node.setAttribute("cm-ignore-events", "true");
5120
+ this.display.input.setUneditable(node);
5121
+ display.sizer.appendChild(node);
5122
+ if (vert == "over") {
5123
+ top = pos.top;
5124
+ } else if (vert == "above" || vert == "near") {
5125
+ var vspace = Math.max(display.wrapper.clientHeight, this.doc.height),
5126
+ hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth);
5127
+ // Default to positioning above (if specified and possible); otherwise default to positioning below
5128
+ if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight)
5129
+ top = pos.top - node.offsetHeight;
5130
+ else if (pos.bottom + node.offsetHeight <= vspace)
5131
+ top = pos.bottom;
5132
+ if (left + node.offsetWidth > hspace)
5133
+ left = hspace - node.offsetWidth;
5134
+ }
5135
+ node.style.top = top + "px";
5136
+ node.style.left = node.style.right = "";
5137
+ if (horiz == "right") {
5138
+ left = display.sizer.clientWidth - node.offsetWidth;
5139
+ node.style.right = "0px";
5140
+ } else {
5141
+ if (horiz == "left") left = 0;
5142
+ else if (horiz == "middle") left = (display.sizer.clientWidth - node.offsetWidth) / 2;
5143
+ node.style.left = left + "px";
5144
+ }
5145
+ if (scroll)
5146
+ scrollIntoView(this, left, top, left + node.offsetWidth, top + node.offsetHeight);
5147
+ },
5148
+
5149
+ triggerOnKeyDown: methodOp(onKeyDown),
5150
+ triggerOnKeyPress: methodOp(onKeyPress),
5151
+ triggerOnKeyUp: onKeyUp,
5152
+
5153
+ execCommand: function(cmd) {
5154
+ if (commands.hasOwnProperty(cmd))
5155
+ return commands[cmd].call(null, this);
5156
+ },
5157
+
5158
+ triggerElectric: methodOp(function(text) { triggerElectric(this, text); }),
5159
+
5160
+ findPosH: function(from, amount, unit, visually) {
5161
+ var dir = 1;
5162
+ if (amount < 0) { dir = -1; amount = -amount; }
5163
+ for (var i = 0, cur = clipPos(this.doc, from); i < amount; ++i) {
5164
+ cur = findPosH(this.doc, cur, dir, unit, visually);
5165
+ if (cur.hitSide) break;
5166
+ }
5167
+ return cur;
5168
+ },
5169
+
5170
+ moveH: methodOp(function(dir, unit) {
5171
+ var cm = this;
5172
+ cm.extendSelectionsBy(function(range) {
5173
+ if (cm.display.shift || cm.doc.extend || range.empty())
5174
+ return findPosH(cm.doc, range.head, dir, unit, cm.options.rtlMoveVisually);
5175
+ else
5176
+ return dir < 0 ? range.from() : range.to();
5177
+ }, sel_move);
5178
+ }),
5179
+
5180
+ deleteH: methodOp(function(dir, unit) {
5181
+ var sel = this.doc.sel, doc = this.doc;
5182
+ if (sel.somethingSelected())
5183
+ doc.replaceSelection("", null, "+delete");
5184
+ else
5185
+ deleteNearSelection(this, function(range) {
5186
+ var other = findPosH(doc, range.head, dir, unit, false);
5187
+ return dir < 0 ? {from: other, to: range.head} : {from: range.head, to: other};
5188
+ });
5189
+ }),
5190
+
5191
+ findPosV: function(from, amount, unit, goalColumn) {
5192
+ var dir = 1, x = goalColumn;
5193
+ if (amount < 0) { dir = -1; amount = -amount; }
5194
+ for (var i = 0, cur = clipPos(this.doc, from); i < amount; ++i) {
5195
+ var coords = cursorCoords(this, cur, "div");
5196
+ if (x == null) x = coords.left;
5197
+ else coords.left = x;
5198
+ cur = findPosV(this, coords, dir, unit);
5199
+ if (cur.hitSide) break;
5200
+ }
5201
+ return cur;
5202
+ },
5203
+
5204
+ moveV: methodOp(function(dir, unit) {
5205
+ var cm = this, doc = this.doc, goals = [];
5206
+ var collapse = !cm.display.shift && !doc.extend && doc.sel.somethingSelected();
5207
+ doc.extendSelectionsBy(function(range) {
5208
+ if (collapse)
5209
+ return dir < 0 ? range.from() : range.to();
5210
+ var headPos = cursorCoords(cm, range.head, "div");
5211
+ if (range.goalColumn != null) headPos.left = range.goalColumn;
5212
+ goals.push(headPos.left);
5213
+ var pos = findPosV(cm, headPos, dir, unit);
5214
+ if (unit == "page" && range == doc.sel.primary())
5215
+ addToScrollPos(cm, null, charCoords(cm, pos, "div").top - headPos.top);
5216
+ return pos;
5217
+ }, sel_move);
5218
+ if (goals.length) for (var i = 0; i < doc.sel.ranges.length; i++)
5219
+ doc.sel.ranges[i].goalColumn = goals[i];
5220
+ }),
5221
+
5222
+ // Find the word at the given position (as returned by coordsChar).
5223
+ findWordAt: function(pos) {
5224
+ var doc = this.doc, line = getLine(doc, pos.line).text;
5225
+ var start = pos.ch, end = pos.ch;
5226
+ if (line) {
5227
+ var helper = this.getHelper(pos, "wordChars");
5228
+ if ((pos.xRel < 0 || end == line.length) && start) --start; else ++end;
5229
+ var startChar = line.charAt(start);
5230
+ var check = isWordChar(startChar, helper)
5231
+ ? function(ch) { return isWordChar(ch, helper); }
5232
+ : /\s/.test(startChar) ? function(ch) {return /\s/.test(ch);}
5233
+ : function(ch) {return !/\s/.test(ch) && !isWordChar(ch);};
5234
+ while (start > 0 && check(line.charAt(start - 1))) --start;
5235
+ while (end < line.length && check(line.charAt(end))) ++end;
5236
+ }
5237
+ return new Range(Pos(pos.line, start), Pos(pos.line, end));
5238
+ },
5239
+
5240
+ toggleOverwrite: function(value) {
5241
+ if (value != null && value == this.state.overwrite) return;
5242
+ if (this.state.overwrite = !this.state.overwrite)
5243
+ addClass(this.display.cursorDiv, "CodeMirror-overwrite");
5244
+ else
5245
+ rmClass(this.display.cursorDiv, "CodeMirror-overwrite");
5246
+
5247
+ signal(this, "overwriteToggle", this, this.state.overwrite);
5248
+ },
5249
+ hasFocus: function() { return this.display.input.getField() == activeElt(); },
5250
+ isReadOnly: function() { return !!(this.options.readOnly || this.doc.cantEdit); },
5251
+
5252
+ scrollTo: methodOp(function(x, y) {
5253
+ if (x != null || y != null) resolveScrollToPos(this);
5254
+ if (x != null) this.curOp.scrollLeft = x;
5255
+ if (y != null) this.curOp.scrollTop = y;
5256
+ }),
5257
+ getScrollInfo: function() {
5258
+ var scroller = this.display.scroller;
5259
+ return {left: scroller.scrollLeft, top: scroller.scrollTop,
5260
+ height: scroller.scrollHeight - scrollGap(this) - this.display.barHeight,
5261
+ width: scroller.scrollWidth - scrollGap(this) - this.display.barWidth,
5262
+ clientHeight: displayHeight(this), clientWidth: displayWidth(this)};
5263
+ },
5264
+
5265
+ scrollIntoView: methodOp(function(range, margin) {
5266
+ if (range == null) {
5267
+ range = {from: this.doc.sel.primary().head, to: null};
5268
+ if (margin == null) margin = this.options.cursorScrollMargin;
5269
+ } else if (typeof range == "number") {
5270
+ range = {from: Pos(range, 0), to: null};
5271
+ } else if (range.from == null) {
5272
+ range = {from: range, to: null};
5273
+ }
5274
+ if (!range.to) range.to = range.from;
5275
+ range.margin = margin || 0;
5276
+
5277
+ if (range.from.line != null) {
5278
+ resolveScrollToPos(this);
5279
+ this.curOp.scrollToPos = range;
5280
+ } else {
5281
+ var sPos = calculateScrollPos(this, Math.min(range.from.left, range.to.left),
5282
+ Math.min(range.from.top, range.to.top) - range.margin,
5283
+ Math.max(range.from.right, range.to.right),
5284
+ Math.max(range.from.bottom, range.to.bottom) + range.margin);
5285
+ this.scrollTo(sPos.scrollLeft, sPos.scrollTop);
5286
+ }
5287
+ }),
5288
+
5289
+ setSize: methodOp(function(width, height) {
5290
+ var cm = this;
5291
+ function interpret(val) {
5292
+ return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val;
5293
+ }
5294
+ if (width != null) cm.display.wrapper.style.width = interpret(width);
5295
+ if (height != null) cm.display.wrapper.style.height = interpret(height);
5296
+ if (cm.options.lineWrapping) clearLineMeasurementCache(this);
5297
+ var lineNo = cm.display.viewFrom;
5298
+ cm.doc.iter(lineNo, cm.display.viewTo, function(line) {
5299
+ if (line.widgets) for (var i = 0; i < line.widgets.length; i++)
5300
+ if (line.widgets[i].noHScroll) { regLineChange(cm, lineNo, "widget"); break; }
5301
+ ++lineNo;
5302
+ });
5303
+ cm.curOp.forceUpdate = true;
5304
+ signal(cm, "refresh", this);
5305
+ }),
5306
+
5307
+ operation: function(f){return runInOp(this, f);},
5308
+
5309
+ refresh: methodOp(function() {
5310
+ var oldHeight = this.display.cachedTextHeight;
5311
+ regChange(this);
5312
+ this.curOp.forceUpdate = true;
5313
+ clearCaches(this);
5314
+ this.scrollTo(this.doc.scrollLeft, this.doc.scrollTop);
5315
+ updateGutterSpace(this);
5316
+ if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5)
5317
+ estimateLineHeights(this);
5318
+ signal(this, "refresh", this);
5319
+ }),
5320
+
5321
+ swapDoc: methodOp(function(doc) {
5322
+ var old = this.doc;
5323
+ old.cm = null;
5324
+ attachDoc(this, doc);
5325
+ clearCaches(this);
5326
+ this.display.input.reset();
5327
+ this.scrollTo(doc.scrollLeft, doc.scrollTop);
5328
+ this.curOp.forceScroll = true;
5329
+ signalLater(this, "swapDoc", this, old);
5330
+ return old;
5331
+ }),
5332
+
5333
+ getInputField: function(){return this.display.input.getField();},
5334
+ getWrapperElement: function(){return this.display.wrapper;},
5335
+ getScrollerElement: function(){return this.display.scroller;},
5336
+ getGutterElement: function(){return this.display.gutters;}
5337
+ };
5338
+ eventMixin(CodeMirror);
5339
+
5340
+ // OPTION DEFAULTS
5341
+
5342
+ // The default configuration options.
5343
+ var defaults = CodeMirror.defaults = {};
5344
+ // Functions to run when options are changed.
5345
+ var optionHandlers = CodeMirror.optionHandlers = {};
5346
+
5347
+ function option(name, deflt, handle, notOnInit) {
5348
+ CodeMirror.defaults[name] = deflt;
5349
+ if (handle) optionHandlers[name] =
5350
+ notOnInit ? function(cm, val, old) {if (old != Init) handle(cm, val, old);} : handle;
5351
+ }
5352
+
5353
+ // Passed to option handlers when there is no old value.
5354
+ var Init = CodeMirror.Init = {toString: function(){return "CodeMirror.Init";}};
5355
+
5356
+ // These two are, on init, called from the constructor because they
5357
+ // have to be initialized before the editor can start at all.
5358
+ option("value", "", function(cm, val) {
5359
+ cm.setValue(val);
5360
+ }, true);
5361
+ option("mode", null, function(cm, val) {
5362
+ cm.doc.modeOption = val;
5363
+ loadMode(cm);
5364
+ }, true);
5365
+
5366
+ option("indentUnit", 2, loadMode, true);
5367
+ option("indentWithTabs", false);
5368
+ option("smartIndent", true);
5369
+ option("tabSize", 4, function(cm) {
5370
+ resetModeState(cm);
5371
+ clearCaches(cm);
5372
+ regChange(cm);
5373
+ }, true);
5374
+ option("lineSeparator", null, function(cm, val) {
5375
+ cm.doc.lineSep = val;
5376
+ if (!val) return;
5377
+ var newBreaks = [], lineNo = cm.doc.first;
5378
+ cm.doc.iter(function(line) {
5379
+ for (var pos = 0;;) {
5380
+ var found = line.text.indexOf(val, pos);
5381
+ if (found == -1) break;
5382
+ pos = found + val.length;
5383
+ newBreaks.push(Pos(lineNo, found));
5384
+ }
5385
+ lineNo++;
5386
+ });
5387
+ for (var i = newBreaks.length - 1; i >= 0; i--)
5388
+ replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i].ch + val.length))
5389
+ });
5390
+ option("specialChars", /[\t\u0000-\u0019\u00ad\u200b-\u200f\u2028\u2029\ufeff]/g, function(cm, val, old) {
5391
+ cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g");
5392
+ if (old != CodeMirror.Init) cm.refresh();
5393
+ });
5394
+ option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function(cm) {cm.refresh();}, true);
5395
+ option("electricChars", true);
5396
+ option("inputStyle", mobile ? "contenteditable" : "textarea", function() {
5397
+ throw new Error("inputStyle can not (yet) be changed in a running editor"); // FIXME
5398
+ }, true);
5399
+ option("rtlMoveVisually", !windows);
5400
+ option("wholeLineUpdateBefore", true);
5401
+
5402
+ option("theme", "default", function(cm) {
5403
+ themeChanged(cm);
5404
+ guttersChanged(cm);
5405
+ }, true);
5406
+ option("keyMap", "default", function(cm, val, old) {
5407
+ var next = getKeyMap(val);
5408
+ var prev = old != CodeMirror.Init && getKeyMap(old);
5409
+ if (prev && prev.detach) prev.detach(cm, next);
5410
+ if (next.attach) next.attach(cm, prev || null);
5411
+ });
5412
+ option("extraKeys", null);
5413
+
5414
+ option("lineWrapping", false, wrappingChanged, true);
5415
+ option("gutters", [], function(cm) {
5416
+ setGuttersForLineNumbers(cm.options);
5417
+ guttersChanged(cm);
5418
+ }, true);
5419
+ option("fixedGutter", true, function(cm, val) {
5420
+ cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0";
5421
+ cm.refresh();
5422
+ }, true);
5423
+ option("coverGutterNextToScrollbar", false, function(cm) {updateScrollbars(cm);}, true);
5424
+ option("scrollbarStyle", "native", function(cm) {
5425
+ initScrollbars(cm);
5426
+ updateScrollbars(cm);
5427
+ cm.display.scrollbars.setScrollTop(cm.doc.scrollTop);
5428
+ cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft);
5429
+ }, true);
5430
+ option("lineNumbers", false, function(cm) {
5431
+ setGuttersForLineNumbers(cm.options);
5432
+ guttersChanged(cm);
5433
+ }, true);
5434
+ option("firstLineNumber", 1, guttersChanged, true);
5435
+ option("lineNumberFormatter", function(integer) {return integer;}, guttersChanged, true);
5436
+ option("showCursorWhenSelecting", false, updateSelection, true);
5437
+
5438
+ option("resetSelectionOnContextMenu", true);
5439
+ option("lineWiseCopyCut", true);
5440
+
5441
+ option("readOnly", false, function(cm, val) {
5442
+ if (val == "nocursor") {
5443
+ onBlur(cm);
5444
+ cm.display.input.blur();
5445
+ cm.display.disabled = true;
5446
+ } else {
5447
+ cm.display.disabled = false;
5448
+ }
5449
+ cm.display.input.readOnlyChanged(val)
5450
+ });
5451
+ option("disableInput", false, function(cm, val) {if (!val) cm.display.input.reset();}, true);
5452
+ option("dragDrop", true, dragDropChanged);
5453
+ option("allowDropFileTypes", null);
5454
+
5455
+ option("cursorBlinkRate", 530);
5456
+ option("cursorScrollMargin", 0);
5457
+ option("cursorHeight", 1, updateSelection, true);
5458
+ option("singleCursorHeightPerLine", true, updateSelection, true);
5459
+ option("workTime", 100);
5460
+ option("workDelay", 100);
5461
+ option("flattenSpans", true, resetModeState, true);
5462
+ option("addModeClass", false, resetModeState, true);
5463
+ option("pollInterval", 100);
5464
+ option("undoDepth", 200, function(cm, val){cm.doc.history.undoDepth = val;});
5465
+ option("historyEventDelay", 1250);
5466
+ option("viewportMargin", 10, function(cm){cm.refresh();}, true);
5467
+ option("maxHighlightLength", 10000, resetModeState, true);
5468
+ option("moveInputWithCursor", true, function(cm, val) {
5469
+ if (!val) cm.display.input.resetPosition();
5470
+ });
5471
+
5472
+ option("tabindex", null, function(cm, val) {
5473
+ cm.display.input.getField().tabIndex = val || "";
5474
+ });
5475
+ option("autofocus", null);
5476
+
5477
+ // MODE DEFINITION AND QUERYING
5478
+
5479
+ // Known modes, by name and by MIME
5480
+ var modes = CodeMirror.modes = {}, mimeModes = CodeMirror.mimeModes = {};
5481
+
5482
+ // Extra arguments are stored as the mode's dependencies, which is
5483
+ // used by (legacy) mechanisms like loadmode.js to automatically
5484
+ // load a mode. (Preferred mechanism is the require/define calls.)
5485
+ CodeMirror.defineMode = function(name, mode) {
5486
+ if (!CodeMirror.defaults.mode && name != "null") CodeMirror.defaults.mode = name;
5487
+ if (arguments.length > 2)
5488
+ mode.dependencies = Array.prototype.slice.call(arguments, 2);
5489
+ modes[name] = mode;
5490
+ };
5491
+
5492
+ CodeMirror.defineMIME = function(mime, spec) {
5493
+ mimeModes[mime] = spec;
5494
+ };
5495
+
5496
+ // Given a MIME type, a {name, ...options} config object, or a name
5497
+ // string, return a mode config object.
5498
+ CodeMirror.resolveMode = function(spec) {
5499
+ if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) {
5500
+ spec = mimeModes[spec];
5501
+ } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) {
5502
+ var found = mimeModes[spec.name];
5503
+ if (typeof found == "string") found = {name: found};
5504
+ spec = createObj(found, spec);
5505
+ spec.name = found.name;
5506
+ } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) {
5507
+ return CodeMirror.resolveMode("application/xml");
5508
+ }
5509
+ if (typeof spec == "string") return {name: spec};
5510
+ else return spec || {name: "null"};
5511
+ };
5512
+
5513
+ // Given a mode spec (anything that resolveMode accepts), find and
5514
+ // initialize an actual mode object.
5515
+ CodeMirror.getMode = function(options, spec) {
5516
+ var spec = CodeMirror.resolveMode(spec);
5517
+ var mfactory = modes[spec.name];
5518
+ if (!mfactory) return CodeMirror.getMode(options, "text/plain");
5519
+ var modeObj = mfactory(options, spec);
5520
+ if (modeExtensions.hasOwnProperty(spec.name)) {
5521
+ var exts = modeExtensions[spec.name];
5522
+ for (var prop in exts) {
5523
+ if (!exts.hasOwnProperty(prop)) continue;
5524
+ if (modeObj.hasOwnProperty(prop)) modeObj["_" + prop] = modeObj[prop];
5525
+ modeObj[prop] = exts[prop];
5526
+ }
5527
+ }
5528
+ modeObj.name = spec.name;
5529
+ if (spec.helperType) modeObj.helperType = spec.helperType;
5530
+ if (spec.modeProps) for (var prop in spec.modeProps)
5531
+ modeObj[prop] = spec.modeProps[prop];
5532
+
5533
+ return modeObj;
5534
+ };
5535
+
5536
+ // Minimal default mode.
5537
+ CodeMirror.defineMode("null", function() {
5538
+ return {token: function(stream) {stream.skipToEnd();}};
5539
+ });
5540
+ CodeMirror.defineMIME("text/plain", "null");
5541
+
5542
+ // This can be used to attach properties to mode objects from
5543
+ // outside the actual mode definition.
5544
+ var modeExtensions = CodeMirror.modeExtensions = {};
5545
+ CodeMirror.extendMode = function(mode, properties) {
5546
+ var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});
5547
+ copyObj(properties, exts);
5548
+ };
5549
+
5550
+ // EXTENSIONS
5551
+
5552
+ CodeMirror.defineExtension = function(name, func) {
5553
+ CodeMirror.prototype[name] = func;
5554
+ };
5555
+ CodeMirror.defineDocExtension = function(name, func) {
5556
+ Doc.prototype[name] = func;
5557
+ };
5558
+ CodeMirror.defineOption = option;
5559
+
5560
+ var initHooks = [];
5561
+ CodeMirror.defineInitHook = function(f) {initHooks.push(f);};
5562
+
5563
+ var helpers = CodeMirror.helpers = {};
5564
+ CodeMirror.registerHelper = function(type, name, value) {
5565
+ if (!helpers.hasOwnProperty(type)) helpers[type] = CodeMirror[type] = {_global: []};
5566
+ helpers[type][name] = value;
5567
+ };
5568
+ CodeMirror.registerGlobalHelper = function(type, name, predicate, value) {
5569
+ CodeMirror.registerHelper(type, name, value);
5570
+ helpers[type]._global.push({pred: predicate, val: value});
5571
+ };
5572
+
5573
+ // MODE STATE HANDLING
5574
+
5575
+ // Utility functions for working with state. Exported because nested
5576
+ // modes need to do this for their inner modes.
5577
+
5578
+ var copyState = CodeMirror.copyState = function(mode, state) {
5579
+ if (state === true) return state;
5580
+ if (mode.copyState) return mode.copyState(state);
5581
+ var nstate = {};
5582
+ for (var n in state) {
5583
+ var val = state[n];
5584
+ if (val instanceof Array) val = val.concat([]);
5585
+ nstate[n] = val;
5586
+ }
5587
+ return nstate;
5588
+ };
5589
+
5590
+ var startState = CodeMirror.startState = function(mode, a1, a2) {
5591
+ return mode.startState ? mode.startState(a1, a2) : true;
5592
+ };
5593
+
5594
+ // Given a mode and a state (for that mode), find the inner mode and
5595
+ // state at the position that the state refers to.
5596
+ CodeMirror.innerMode = function(mode, state) {
5597
+ while (mode.innerMode) {
5598
+ var info = mode.innerMode(state);
5599
+ if (!info || info.mode == mode) break;
5600
+ state = info.state;
5601
+ mode = info.mode;
5602
+ }
5603
+ return info || {mode: mode, state: state};
5604
+ };
5605
+
5606
+ // STANDARD COMMANDS
5607
+
5608
+ // Commands are parameter-less actions that can be performed on an
5609
+ // editor, mostly used for keybindings.
5610
+ var commands = CodeMirror.commands = {
5611
+ selectAll: function(cm) {cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll);},
5612
+ singleSelection: function(cm) {
5613
+ cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head"), sel_dontScroll);
5614
+ },
5615
+ killLine: function(cm) {
5616
+ deleteNearSelection(cm, function(range) {
5617
+ if (range.empty()) {
5618
+ var len = getLine(cm.doc, range.head.line).text.length;
5619
+ if (range.head.ch == len && range.head.line < cm.lastLine())
5620
+ return {from: range.head, to: Pos(range.head.line + 1, 0)};
5621
+ else
5622
+ return {from: range.head, to: Pos(range.head.line, len)};
5623
+ } else {
5624
+ return {from: range.from(), to: range.to()};
5625
+ }
5626
+ });
5627
+ },
5628
+ deleteLine: function(cm) {
5629
+ deleteNearSelection(cm, function(range) {
5630
+ return {from: Pos(range.from().line, 0),
5631
+ to: clipPos(cm.doc, Pos(range.to().line + 1, 0))};
5632
+ });
5633
+ },
5634
+ delLineLeft: function(cm) {
5635
+ deleteNearSelection(cm, function(range) {
5636
+ return {from: Pos(range.from().line, 0), to: range.from()};
5637
+ });
5638
+ },
5639
+ delWrappedLineLeft: function(cm) {
5640
+ deleteNearSelection(cm, function(range) {
5641
+ var top = cm.charCoords(range.head, "div").top + 5;
5642
+ var leftPos = cm.coordsChar({left: 0, top: top}, "div");
5643
+ return {from: leftPos, to: range.from()};
5644
+ });
5645
+ },
5646
+ delWrappedLineRight: function(cm) {
5647
+ deleteNearSelection(cm, function(range) {
5648
+ var top = cm.charCoords(range.head, "div").top + 5;
5649
+ var rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div");
5650
+ return {from: range.from(), to: rightPos };
5651
+ });
5652
+ },
5653
+ undo: function(cm) {cm.undo();},
5654
+ redo: function(cm) {cm.redo();},
5655
+ undoSelection: function(cm) {cm.undoSelection();},
5656
+ redoSelection: function(cm) {cm.redoSelection();},
5657
+ goDocStart: function(cm) {cm.extendSelection(Pos(cm.firstLine(), 0));},
5658
+ goDocEnd: function(cm) {cm.extendSelection(Pos(cm.lastLine()));},
5659
+ goLineStart: function(cm) {
5660
+ cm.extendSelectionsBy(function(range) { return lineStart(cm, range.head.line); },
5661
+ {origin: "+move", bias: 1});
5662
+ },
5663
+ goLineStartSmart: function(cm) {
5664
+ cm.extendSelectionsBy(function(range) {
5665
+ return lineStartSmart(cm, range.head);
5666
+ }, {origin: "+move", bias: 1});
5667
+ },
5668
+ goLineEnd: function(cm) {
5669
+ cm.extendSelectionsBy(function(range) { return lineEnd(cm, range.head.line); },
5670
+ {origin: "+move", bias: -1});
5671
+ },
5672
+ goLineRight: function(cm) {
5673
+ cm.extendSelectionsBy(function(range) {
5674
+ var top = cm.charCoords(range.head, "div").top + 5;
5675
+ return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div");
5676
+ }, sel_move);
5677
+ },
5678
+ goLineLeft: function(cm) {
5679
+ cm.extendSelectionsBy(function(range) {
5680
+ var top = cm.charCoords(range.head, "div").top + 5;
5681
+ return cm.coordsChar({left: 0, top: top}, "div");
5682
+ }, sel_move);
5683
+ },
5684
+ goLineLeftSmart: function(cm) {
5685
+ cm.extendSelectionsBy(function(range) {
5686
+ var top = cm.charCoords(range.head, "div").top + 5;
5687
+ var pos = cm.coordsChar({left: 0, top: top}, "div");
5688
+ if (pos.ch < cm.getLine(pos.line).search(/\S/)) return lineStartSmart(cm, range.head);
5689
+ return pos;
5690
+ }, sel_move);
5691
+ },
5692
+ goLineUp: function(cm) {cm.moveV(-1, "line");},
5693
+ goLineDown: function(cm) {cm.moveV(1, "line");},
5694
+ goPageUp: function(cm) {cm.moveV(-1, "page");},
5695
+ goPageDown: function(cm) {cm.moveV(1, "page");},
5696
+ goCharLeft: function(cm) {cm.moveH(-1, "char");},
5697
+ goCharRight: function(cm) {cm.moveH(1, "char");},
5698
+ goColumnLeft: function(cm) {cm.moveH(-1, "column");},
5699
+ goColumnRight: function(cm) {cm.moveH(1, "column");},
5700
+ goWordLeft: function(cm) {cm.moveH(-1, "word");},
5701
+ goGroupRight: function(cm) {cm.moveH(1, "group");},
5702
+ goGroupLeft: function(cm) {cm.moveH(-1, "group");},
5703
+ goWordRight: function(cm) {cm.moveH(1, "word");},
5704
+ delCharBefore: function(cm) {cm.deleteH(-1, "char");},
5705
+ delCharAfter: function(cm) {cm.deleteH(1, "char");},
5706
+ delWordBefore: function(cm) {cm.deleteH(-1, "word");},
5707
+ delWordAfter: function(cm) {cm.deleteH(1, "word");},
5708
+ delGroupBefore: function(cm) {cm.deleteH(-1, "group");},
5709
+ delGroupAfter: function(cm) {cm.deleteH(1, "group");},
5710
+ indentAuto: function(cm) {cm.indentSelection("smart");},
5711
+ indentMore: function(cm) {cm.indentSelection("add");},
5712
+ indentLess: function(cm) {cm.indentSelection("subtract");},
5713
+ insertTab: function(cm) {cm.replaceSelection("\t");},
5714
+ insertSoftTab: function(cm) {
5715
+ var spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize;
5716
+ for (var i = 0; i < ranges.length; i++) {
5717
+ var pos = ranges[i].from();
5718
+ var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize);
5719
+ spaces.push(new Array(tabSize - col % tabSize + 1).join(" "));
5720
+ }
5721
+ cm.replaceSelections(spaces);
5722
+ },
5723
+ defaultTab: function(cm) {
5724
+ if (cm.somethingSelected()) cm.indentSelection("add");
5725
+ else cm.execCommand("insertTab");
5726
+ },
5727
+ transposeChars: function(cm) {
5728
+ runInOp(cm, function() {
5729
+ var ranges = cm.listSelections(), newSel = [];
5730
+ for (var i = 0; i < ranges.length; i++) {
5731
+ var cur = ranges[i].head, line = getLine(cm.doc, cur.line).text;
5732
+ if (line) {
5733
+ if (cur.ch == line.length) cur = new Pos(cur.line, cur.ch - 1);
5734
+ if (cur.ch > 0) {
5735
+ cur = new Pos(cur.line, cur.ch + 1);
5736
+ cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2),
5737
+ Pos(cur.line, cur.ch - 2), cur, "+transpose");
5738
+ } else if (cur.line > cm.doc.first) {
5739
+ var prev = getLine(cm.doc, cur.line - 1).text;
5740
+ if (prev)
5741
+ cm.replaceRange(line.charAt(0) + cm.doc.lineSeparator() +
5742
+ prev.charAt(prev.length - 1),
5743
+ Pos(cur.line - 1, prev.length - 1), Pos(cur.line, 1), "+transpose");
5744
+ }
5745
+ }
5746
+ newSel.push(new Range(cur, cur));
5747
+ }
5748
+ cm.setSelections(newSel);
5749
+ });
5750
+ },
5751
+ newlineAndIndent: function(cm) {
5752
+ runInOp(cm, function() {
5753
+ var len = cm.listSelections().length;
5754
+ for (var i = 0; i < len; i++) {
5755
+ var range = cm.listSelections()[i];
5756
+ cm.replaceRange(cm.doc.lineSeparator(), range.anchor, range.head, "+input");
5757
+ cm.indentLine(range.from().line + 1, null, true);
5758
+ }
5759
+ ensureCursorVisible(cm);
5760
+ });
5761
+ },
5762
+ toggleOverwrite: function(cm) {cm.toggleOverwrite();}
5763
+ };
5764
+
5765
+
5766
+ // STANDARD KEYMAPS
5767
+
5768
+ var keyMap = CodeMirror.keyMap = {};
5769
+
5770
+ keyMap.basic = {
5771
+ "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
5772
+ "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
5773
+ "Delete": "delCharAfter", "Backspace": "delCharBefore", "Shift-Backspace": "delCharBefore",
5774
+ "Tab": "defaultTab", "Shift-Tab": "indentAuto",
5775
+ "Enter": "newlineAndIndent", "Insert": "toggleOverwrite",
5776
+ "Esc": "singleSelection"
5777
+ };
5778
+ // Note that the save and find-related commands aren't defined by
5779
+ // default. User code or addons can define them. Unknown commands
5780
+ // are simply ignored.
5781
+ keyMap.pcDefault = {
5782
+ "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
5783
+ "Ctrl-Home": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Up": "goLineUp", "Ctrl-Down": "goLineDown",
5784
+ "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
5785
+ "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find",
5786
+ "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
5787
+ "Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
5788
+ "Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSelection",
5789
+ fallthrough: "basic"
5790
+ };
5791
+ // Very basic readline/emacs-style bindings, which are standard on Mac.
5792
+ keyMap.emacsy = {
5793
+ "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
5794
+ "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
5795
+ "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore",
5796
+ "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars"
5797
+ };
5798
+ keyMap.macDefault = {
5799
+ "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
5800
+ "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft",
5801
+ "Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineRight", "Alt-Backspace": "delGroupBefore",
5802
+ "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find",
5803
+ "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
5804
+ "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLineLeft", "Cmd-Delete": "delWrappedLineRight",
5805
+ "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocStart", "Ctrl-Down": "goDocEnd",
5806
+ fallthrough: ["basic", "emacsy"]
5807
+ };
5808
+ keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault;
5809
+
5810
+ // KEYMAP DISPATCH
5811
+
5812
+ function normalizeKeyName(name) {
5813
+ var parts = name.split(/-(?!$)/), name = parts[parts.length - 1];
5814
+ var alt, ctrl, shift, cmd;
5815
+ for (var i = 0; i < parts.length - 1; i++) {
5816
+ var mod = parts[i];
5817
+ if (/^(cmd|meta|m)$/i.test(mod)) cmd = true;
5818
+ else if (/^a(lt)?$/i.test(mod)) alt = true;
5819
+ else if (/^(c|ctrl|control)$/i.test(mod)) ctrl = true;
5820
+ else if (/^s(hift)$/i.test(mod)) shift = true;
5821
+ else throw new Error("Unrecognized modifier name: " + mod);
5822
+ }
5823
+ if (alt) name = "Alt-" + name;
5824
+ if (ctrl) name = "Ctrl-" + name;
5825
+ if (cmd) name = "Cmd-" + name;
5826
+ if (shift) name = "Shift-" + name;
5827
+ return name;
5828
+ }
5829
+
5830
+ // This is a kludge to keep keymaps mostly working as raw objects
5831
+ // (backwards compatibility) while at the same time support features
5832
+ // like normalization and multi-stroke key bindings. It compiles a
5833
+ // new normalized keymap, and then updates the old object to reflect
5834
+ // this.
5835
+ CodeMirror.normalizeKeyMap = function(keymap) {
5836
+ var copy = {};
5837
+ for (var keyname in keymap) if (keymap.hasOwnProperty(keyname)) {
5838
+ var value = keymap[keyname];
5839
+ if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) continue;
5840
+ if (value == "...") { delete keymap[keyname]; continue; }
5841
+
5842
+ var keys = map(keyname.split(" "), normalizeKeyName);
5843
+ for (var i = 0; i < keys.length; i++) {
5844
+ var val, name;
5845
+ if (i == keys.length - 1) {
5846
+ name = keys.join(" ");
5847
+ val = value;
5848
+ } else {
5849
+ name = keys.slice(0, i + 1).join(" ");
5850
+ val = "...";
5851
+ }
5852
+ var prev = copy[name];
5853
+ if (!prev) copy[name] = val;
5854
+ else if (prev != val) throw new Error("Inconsistent bindings for " + name);
5855
+ }
5856
+ delete keymap[keyname];
5857
+ }
5858
+ for (var prop in copy) keymap[prop] = copy[prop];
5859
+ return keymap;
5860
+ };
5861
+
5862
+ var lookupKey = CodeMirror.lookupKey = function(key, map, handle, context) {
5863
+ map = getKeyMap(map);
5864
+ var found = map.call ? map.call(key, context) : map[key];
5865
+ if (found === false) return "nothing";
5866
+ if (found === "...") return "multi";
5867
+ if (found != null && handle(found)) return "handled";
5868
+
5869
+ if (map.fallthrough) {
5870
+ if (Object.prototype.toString.call(map.fallthrough) != "[object Array]")
5871
+ return lookupKey(key, map.fallthrough, handle, context);
5872
+ for (var i = 0; i < map.fallthrough.length; i++) {
5873
+ var result = lookupKey(key, map.fallthrough[i], handle, context);
5874
+ if (result) return result;
5875
+ }
5876
+ }
5877
+ };
5878
+
5879
+ // Modifier key presses don't count as 'real' key presses for the
5880
+ // purpose of keymap fallthrough.
5881
+ var isModifierKey = CodeMirror.isModifierKey = function(value) {
5882
+ var name = typeof value == "string" ? value : keyNames[value.keyCode];
5883
+ return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod";
5884
+ };
5885
+
5886
+ // Look up the name of a key as indicated by an event object.
5887
+ var keyName = CodeMirror.keyName = function(event, noShift) {
5888
+ if (presto && event.keyCode == 34 && event["char"]) return false;
5889
+ var base = keyNames[event.keyCode], name = base;
5890
+ if (name == null || event.altGraphKey) return false;
5891
+ if (event.altKey && base != "Alt") name = "Alt-" + name;
5892
+ if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != "Ctrl") name = "Ctrl-" + name;
5893
+ if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != "Cmd") name = "Cmd-" + name;
5894
+ if (!noShift && event.shiftKey && base != "Shift") name = "Shift-" + name;
5895
+ return name;
5896
+ };
5897
+
5898
+ function getKeyMap(val) {
5899
+ return typeof val == "string" ? keyMap[val] : val;
5900
+ }
5901
+
5902
+ // FROMTEXTAREA
5903
+
5904
+ CodeMirror.fromTextArea = function(textarea, options) {
5905
+ options = options ? copyObj(options) : {};
5906
+ options.value = textarea.value;
5907
+ if (!options.tabindex && textarea.tabIndex)
5908
+ options.tabindex = textarea.tabIndex;
5909
+ if (!options.placeholder && textarea.placeholder)
5910
+ options.placeholder = textarea.placeholder;
5911
+ // Set autofocus to true if this textarea is focused, or if it has
5912
+ // autofocus and no other element is focused.
5913
+ if (options.autofocus == null) {
5914
+ var hasFocus = activeElt();
5915
+ options.autofocus = hasFocus == textarea ||
5916
+ textarea.getAttribute("autofocus") != null && hasFocus == document.body;
5917
+ }
5918
+
5919
+ function save() {textarea.value = cm.getValue();}
5920
+ if (textarea.form) {
5921
+ on(textarea.form, "submit", save);
5922
+ // Deplorable hack to make the submit method do the right thing.
5923
+ if (!options.leaveSubmitMethodAlone) {
5924
+ var form = textarea.form, realSubmit = form.submit;
5925
+ try {
5926
+ var wrappedSubmit = form.submit = function() {
5927
+ save();
5928
+ form.submit = realSubmit;
5929
+ form.submit();
5930
+ form.submit = wrappedSubmit;
5931
+ };
5932
+ } catch(e) {}
5933
+ }
5934
+ }
5935
+
5936
+ options.finishInit = function(cm) {
5937
+ cm.save = save;
5938
+ cm.getTextArea = function() { return textarea; };
5939
+ cm.toTextArea = function() {
5940
+ cm.toTextArea = isNaN; // Prevent this from being ran twice
5941
+ save();
5942
+ textarea.parentNode.removeChild(cm.getWrapperElement());
5943
+ textarea.style.display = "";
5944
+ if (textarea.form) {
5945
+ off(textarea.form, "submit", save);
5946
+ if (typeof textarea.form.submit == "function")
5947
+ textarea.form.submit = realSubmit;
5948
+ }
5949
+ };
5950
+ };
5951
+
5952
+ textarea.style.display = "none";
5953
+ var cm = CodeMirror(function(node) {
5954
+ textarea.parentNode.insertBefore(node, textarea.nextSibling);
5955
+ }, options);
5956
+ return cm;
5957
+ };
5958
+
5959
+ // STRING STREAM
5960
+
5961
+ // Fed to the mode parsers, provides helper functions to make
5962
+ // parsers more succinct.
5963
+
5964
+ var StringStream = CodeMirror.StringStream = function(string, tabSize) {
5965
+ this.pos = this.start = 0;
5966
+ this.string = string;
5967
+ this.tabSize = tabSize || 8;
5968
+ this.lastColumnPos = this.lastColumnValue = 0;
5969
+ this.lineStart = 0;
5970
+ };
5971
+
5972
+ StringStream.prototype = {
5973
+ eol: function() {return this.pos >= this.string.length;},
5974
+ sol: function() {return this.pos == this.lineStart;},
5975
+ peek: function() {return this.string.charAt(this.pos) || undefined;},
5976
+ next: function() {
5977
+ if (this.pos < this.string.length)
5978
+ return this.string.charAt(this.pos++);
5979
+ },
5980
+ eat: function(match) {
5981
+ var ch = this.string.charAt(this.pos);
5982
+ if (typeof match == "string") var ok = ch == match;
5983
+ else var ok = ch && (match.test ? match.test(ch) : match(ch));
5984
+ if (ok) {++this.pos; return ch;}
5985
+ },
5986
+ eatWhile: function(match) {
5987
+ var start = this.pos;
5988
+ while (this.eat(match)){}
5989
+ return this.pos > start;
5990
+ },
5991
+ eatSpace: function() {
5992
+ var start = this.pos;
5993
+ while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) ++this.pos;
5994
+ return this.pos > start;
5995
+ },
5996
+ skipToEnd: function() {this.pos = this.string.length;},
5997
+ skipTo: function(ch) {
5998
+ var found = this.string.indexOf(ch, this.pos);
5999
+ if (found > -1) {this.pos = found; return true;}
6000
+ },
6001
+ backUp: function(n) {this.pos -= n;},
6002
+ column: function() {
6003
+ if (this.lastColumnPos < this.start) {
6004
+ this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue);
6005
+ this.lastColumnPos = this.start;
6006
+ }
6007
+ return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0);
6008
+ },
6009
+ indentation: function() {
6010
+ return countColumn(this.string, null, this.tabSize) -
6011
+ (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0);
6012
+ },
6013
+ match: function(pattern, consume, caseInsensitive) {
6014
+ if (typeof pattern == "string") {
6015
+ var cased = function(str) {return caseInsensitive ? str.toLowerCase() : str;};
6016
+ var substr = this.string.substr(this.pos, pattern.length);
6017
+ if (cased(substr) == cased(pattern)) {
6018
+ if (consume !== false) this.pos += pattern.length;
6019
+ return true;
6020
+ }
6021
+ } else {
6022
+ var match = this.string.slice(this.pos).match(pattern);
6023
+ if (match && match.index > 0) return null;
6024
+ if (match && consume !== false) this.pos += match[0].length;
6025
+ return match;
6026
+ }
6027
+ },
6028
+ current: function(){return this.string.slice(this.start, this.pos);},
6029
+ hideFirstChars: function(n, inner) {
6030
+ this.lineStart += n;
6031
+ try { return inner(); }
6032
+ finally { this.lineStart -= n; }
6033
+ }
6034
+ };
6035
+
6036
+ // TEXTMARKERS
6037
+
6038
+ // Created with markText and setBookmark methods. A TextMarker is a
6039
+ // handle that can be used to clear or find a marked position in the
6040
+ // document. Line objects hold arrays (markedSpans) containing
6041
+ // {from, to, marker} object pointing to such marker objects, and
6042
+ // indicating that such a marker is present on that line. Multiple
6043
+ // lines may point to the same marker when it spans across lines.
6044
+ // The spans will have null for their from/to properties when the
6045
+ // marker continues beyond the start/end of the line. Markers have
6046
+ // links back to the lines they currently touch.
6047
+
6048
+ var nextMarkerId = 0;
6049
+
6050
+ var TextMarker = CodeMirror.TextMarker = function(doc, type) {
6051
+ this.lines = [];
6052
+ this.type = type;
6053
+ this.doc = doc;
6054
+ this.id = ++nextMarkerId;
6055
+ };
6056
+ eventMixin(TextMarker);
6057
+
6058
+ // Clear the marker.
6059
+ TextMarker.prototype.clear = function() {
6060
+ if (this.explicitlyCleared) return;
6061
+ var cm = this.doc.cm, withOp = cm && !cm.curOp;
6062
+ if (withOp) startOperation(cm);
6063
+ if (hasHandler(this, "clear")) {
6064
+ var found = this.find();
6065
+ if (found) signalLater(this, "clear", found.from, found.to);
6066
+ }
6067
+ var min = null, max = null;
6068
+ for (var i = 0; i < this.lines.length; ++i) {
6069
+ var line = this.lines[i];
6070
+ var span = getMarkedSpanFor(line.markedSpans, this);
6071
+ if (cm && !this.collapsed) regLineChange(cm, lineNo(line), "text");
6072
+ else if (cm) {
6073
+ if (span.to != null) max = lineNo(line);
6074
+ if (span.from != null) min = lineNo(line);
6075
+ }
6076
+ line.markedSpans = removeMarkedSpan(line.markedSpans, span);
6077
+ if (span.from == null && this.collapsed && !lineIsHidden(this.doc, line) && cm)
6078
+ updateLineHeight(line, textHeight(cm.display));
6079
+ }
6080
+ if (cm && this.collapsed && !cm.options.lineWrapping) for (var i = 0; i < this.lines.length; ++i) {
6081
+ var visual = visualLine(this.lines[i]), len = lineLength(visual);
6082
+ if (len > cm.display.maxLineLength) {
6083
+ cm.display.maxLine = visual;
6084
+ cm.display.maxLineLength = len;
6085
+ cm.display.maxLineChanged = true;
6086
+ }
6087
+ }
6088
+
6089
+ if (min != null && cm && this.collapsed) regChange(cm, min, max + 1);
6090
+ this.lines.length = 0;
6091
+ this.explicitlyCleared = true;
6092
+ if (this.atomic && this.doc.cantEdit) {
6093
+ this.doc.cantEdit = false;
6094
+ if (cm) reCheckSelection(cm.doc);
6095
+ }
6096
+ if (cm) signalLater(cm, "markerCleared", cm, this);
6097
+ if (withOp) endOperation(cm);
6098
+ if (this.parent) this.parent.clear();
6099
+ };
6100
+
6101
+ // Find the position of the marker in the document. Returns a {from,
6102
+ // to} object by default. Side can be passed to get a specific side
6103
+ // -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the
6104
+ // Pos objects returned contain a line object, rather than a line
6105
+ // number (used to prevent looking up the same line twice).
6106
+ TextMarker.prototype.find = function(side, lineObj) {
6107
+ if (side == null && this.type == "bookmark") side = 1;
6108
+ var from, to;
6109
+ for (var i = 0; i < this.lines.length; ++i) {
6110
+ var line = this.lines[i];
6111
+ var span = getMarkedSpanFor(line.markedSpans, this);
6112
+ if (span.from != null) {
6113
+ from = Pos(lineObj ? line : lineNo(line), span.from);
6114
+ if (side == -1) return from;
6115
+ }
6116
+ if (span.to != null) {
6117
+ to = Pos(lineObj ? line : lineNo(line), span.to);
6118
+ if (side == 1) return to;
6119
+ }
6120
+ }
6121
+ return from && {from: from, to: to};
6122
+ };
6123
+
6124
+ // Signals that the marker's widget changed, and surrounding layout
6125
+ // should be recomputed.
6126
+ TextMarker.prototype.changed = function() {
6127
+ var pos = this.find(-1, true), widget = this, cm = this.doc.cm;
6128
+ if (!pos || !cm) return;
6129
+ runInOp(cm, function() {
6130
+ var line = pos.line, lineN = lineNo(pos.line);
6131
+ var view = findViewForLine(cm, lineN);
6132
+ if (view) {
6133
+ clearLineMeasurementCacheFor(view);
6134
+ cm.curOp.selectionChanged = cm.curOp.forceUpdate = true;
6135
+ }
6136
+ cm.curOp.updateMaxLine = true;
6137
+ if (!lineIsHidden(widget.doc, line) && widget.height != null) {
6138
+ var oldHeight = widget.height;
6139
+ widget.height = null;
6140
+ var dHeight = widgetHeight(widget) - oldHeight;
6141
+ if (dHeight)
6142
+ updateLineHeight(line, line.height + dHeight);
6143
+ }
6144
+ });
6145
+ };
6146
+
6147
+ TextMarker.prototype.attachLine = function(line) {
6148
+ if (!this.lines.length && this.doc.cm) {
6149
+ var op = this.doc.cm.curOp;
6150
+ if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1)
6151
+ (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this);
6152
+ }
6153
+ this.lines.push(line);
6154
+ };
6155
+ TextMarker.prototype.detachLine = function(line) {
6156
+ this.lines.splice(indexOf(this.lines, line), 1);
6157
+ if (!this.lines.length && this.doc.cm) {
6158
+ var op = this.doc.cm.curOp;
6159
+ (op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this);
6160
+ }
6161
+ };
6162
+
6163
+ // Collapsed markers have unique ids, in order to be able to order
6164
+ // them, which is needed for uniquely determining an outer marker
6165
+ // when they overlap (they may nest, but not partially overlap).
6166
+ var nextMarkerId = 0;
6167
+
6168
+ // Create a marker, wire it up to the right lines, and
6169
+ function markText(doc, from, to, options, type) {
6170
+ // Shared markers (across linked documents) are handled separately
6171
+ // (markTextShared will call out to this again, once per
6172
+ // document).
6173
+ if (options && options.shared) return markTextShared(doc, from, to, options, type);
6174
+ // Ensure we are in an operation.
6175
+ if (doc.cm && !doc.cm.curOp) return operation(doc.cm, markText)(doc, from, to, options, type);
6176
+
6177
+ var marker = new TextMarker(doc, type), diff = cmp(from, to);
6178
+ if (options) copyObj(options, marker, false);
6179
+ // Don't connect empty markers unless clearWhenEmpty is false
6180
+ if (diff > 0 || diff == 0 && marker.clearWhenEmpty !== false)
6181
+ return marker;
6182
+ if (marker.replacedWith) {
6183
+ // Showing up as a widget implies collapsed (widget replaces text)
6184
+ marker.collapsed = true;
6185
+ marker.widgetNode = elt("span", [marker.replacedWith], "CodeMirror-widget");
6186
+ if (!options.handleMouseEvents) marker.widgetNode.setAttribute("cm-ignore-events", "true");
6187
+ if (options.insertLeft) marker.widgetNode.insertLeft = true;
6188
+ }
6189
+ if (marker.collapsed) {
6190
+ if (conflictingCollapsedRange(doc, from.line, from, to, marker) ||
6191
+ from.line != to.line && conflictingCollapsedRange(doc, to.line, from, to, marker))
6192
+ throw new Error("Inserting collapsed marker partially overlapping an existing one");
6193
+ sawCollapsedSpans = true;
6194
+ }
6195
+
6196
+ if (marker.addToHistory)
6197
+ addChangeToHistory(doc, {from: from, to: to, origin: "markText"}, doc.sel, NaN);
6198
+
6199
+ var curLine = from.line, cm = doc.cm, updateMaxLine;
6200
+ doc.iter(curLine, to.line + 1, function(line) {
6201
+ if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) == cm.display.maxLine)
6202
+ updateMaxLine = true;
6203
+ if (marker.collapsed && curLine != from.line) updateLineHeight(line, 0);
6204
+ addMarkedSpan(line, new MarkedSpan(marker,
6205
+ curLine == from.line ? from.ch : null,
6206
+ curLine == to.line ? to.ch : null));
6207
+ ++curLine;
6208
+ });
6209
+ // lineIsHidden depends on the presence of the spans, so needs a second pass
6210
+ if (marker.collapsed) doc.iter(from.line, to.line + 1, function(line) {
6211
+ if (lineIsHidden(doc, line)) updateLineHeight(line, 0);
6212
+ });
6213
+
6214
+ if (marker.clearOnEnter) on(marker, "beforeCursorEnter", function() { marker.clear(); });
6215
+
6216
+ if (marker.readOnly) {
6217
+ sawReadOnlySpans = true;
6218
+ if (doc.history.done.length || doc.history.undone.length)
6219
+ doc.clearHistory();
6220
+ }
6221
+ if (marker.collapsed) {
6222
+ marker.id = ++nextMarkerId;
6223
+ marker.atomic = true;
6224
+ }
6225
+ if (cm) {
6226
+ // Sync editor state
6227
+ if (updateMaxLine) cm.curOp.updateMaxLine = true;
6228
+ if (marker.collapsed)
6229
+ regChange(cm, from.line, to.line + 1);
6230
+ else if (marker.className || marker.title || marker.startStyle || marker.endStyle || marker.css)
6231
+ for (var i = from.line; i <= to.line; i++) regLineChange(cm, i, "text");
6232
+ if (marker.atomic) reCheckSelection(cm.doc);
6233
+ signalLater(cm, "markerAdded", cm, marker);
6234
+ }
6235
+ return marker;
6236
+ }
6237
+
6238
+ // SHARED TEXTMARKERS
6239
+
6240
+ // A shared marker spans multiple linked documents. It is
6241
+ // implemented as a meta-marker-object controlling multiple normal
6242
+ // markers.
6243
+ var SharedTextMarker = CodeMirror.SharedTextMarker = function(markers, primary) {
6244
+ this.markers = markers;
6245
+ this.primary = primary;
6246
+ for (var i = 0; i < markers.length; ++i)
6247
+ markers[i].parent = this;
6248
+ };
6249
+ eventMixin(SharedTextMarker);
6250
+
6251
+ SharedTextMarker.prototype.clear = function() {
6252
+ if (this.explicitlyCleared) return;
6253
+ this.explicitlyCleared = true;
6254
+ for (var i = 0; i < this.markers.length; ++i)
6255
+ this.markers[i].clear();
6256
+ signalLater(this, "clear");
6257
+ };
6258
+ SharedTextMarker.prototype.find = function(side, lineObj) {
6259
+ return this.primary.find(side, lineObj);
6260
+ };
6261
+
6262
+ function markTextShared(doc, from, to, options, type) {
6263
+ options = copyObj(options);
6264
+ options.shared = false;
6265
+ var markers = [markText(doc, from, to, options, type)], primary = markers[0];
6266
+ var widget = options.widgetNode;
6267
+ linkedDocs(doc, function(doc) {
6268
+ if (widget) options.widgetNode = widget.cloneNode(true);
6269
+ markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type));
6270
+ for (var i = 0; i < doc.linked.length; ++i)
6271
+ if (doc.linked[i].isParent) return;
6272
+ primary = lst(markers);
6273
+ });
6274
+ return new SharedTextMarker(markers, primary);
6275
+ }
6276
+
6277
+ function findSharedMarkers(doc) {
6278
+ return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())),
6279
+ function(m) { return m.parent; });
6280
+ }
6281
+
6282
+ function copySharedMarkers(doc, markers) {
6283
+ for (var i = 0; i < markers.length; i++) {
6284
+ var marker = markers[i], pos = marker.find();
6285
+ var mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to);
6286
+ if (cmp(mFrom, mTo)) {
6287
+ var subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type);
6288
+ marker.markers.push(subMark);
6289
+ subMark.parent = marker;
6290
+ }
6291
+ }
6292
+ }
6293
+
6294
+ function detachSharedMarkers(markers) {
6295
+ for (var i = 0; i < markers.length; i++) {
6296
+ var marker = markers[i], linked = [marker.primary.doc];;
6297
+ linkedDocs(marker.primary.doc, function(d) { linked.push(d); });
6298
+ for (var j = 0; j < marker.markers.length; j++) {
6299
+ var subMarker = marker.markers[j];
6300
+ if (indexOf(linked, subMarker.doc) == -1) {
6301
+ subMarker.parent = null;
6302
+ marker.markers.splice(j--, 1);
6303
+ }
6304
+ }
6305
+ }
6306
+ }
6307
+
6308
+ // TEXTMARKER SPANS
6309
+
6310
+ function MarkedSpan(marker, from, to) {
6311
+ this.marker = marker;
6312
+ this.from = from; this.to = to;
6313
+ }
6314
+
6315
+ // Search an array of spans for a span matching the given marker.
6316
+ function getMarkedSpanFor(spans, marker) {
6317
+ if (spans) for (var i = 0; i < spans.length; ++i) {
6318
+ var span = spans[i];
6319
+ if (span.marker == marker) return span;
6320
+ }
6321
+ }
6322
+ // Remove a span from an array, returning undefined if no spans are
6323
+ // left (we don't store arrays for lines without spans).
6324
+ function removeMarkedSpan(spans, span) {
6325
+ for (var r, i = 0; i < spans.length; ++i)
6326
+ if (spans[i] != span) (r || (r = [])).push(spans[i]);
6327
+ return r;
6328
+ }
6329
+ // Add a span to a line.
6330
+ function addMarkedSpan(line, span) {
6331
+ line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span];
6332
+ span.marker.attachLine(line);
6333
+ }
6334
+
6335
+ // Used for the algorithm that adjusts markers for a change in the
6336
+ // document. These functions cut an array of spans at a given
6337
+ // character position, returning an array of remaining chunks (or
6338
+ // undefined if nothing remains).
6339
+ function markedSpansBefore(old, startCh, isInsert) {
6340
+ if (old) for (var i = 0, nw; i < old.length; ++i) {
6341
+ var span = old[i], marker = span.marker;
6342
+ var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh);
6343
+ if (startsBefore || span.from == startCh && marker.type == "bookmark" && (!isInsert || !span.marker.insertLeft)) {
6344
+ var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh);
6345
+ (nw || (nw = [])).push(new MarkedSpan(marker, span.from, endsAfter ? null : span.to));
6346
+ }
6347
+ }
6348
+ return nw;
6349
+ }
6350
+ function markedSpansAfter(old, endCh, isInsert) {
6351
+ if (old) for (var i = 0, nw; i < old.length; ++i) {
6352
+ var span = old[i], marker = span.marker;
6353
+ var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh);
6354
+ if (endsAfter || span.from == endCh && marker.type == "bookmark" && (!isInsert || span.marker.insertLeft)) {
6355
+ var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh);
6356
+ (nw || (nw = [])).push(new MarkedSpan(marker, startsBefore ? null : span.from - endCh,
6357
+ span.to == null ? null : span.to - endCh));
6358
+ }
6359
+ }
6360
+ return nw;
6361
+ }
6362
+
6363
+ // Given a change object, compute the new set of marker spans that
6364
+ // cover the line in which the change took place. Removes spans
6365
+ // entirely within the change, reconnects spans belonging to the
6366
+ // same marker that appear on both sides of the change, and cuts off
6367
+ // spans partially within the change. Returns an array of span
6368
+ // arrays with one element for each line in (after) the change.
6369
+ function stretchSpansOverChange(doc, change) {
6370
+ if (change.full) return null;
6371
+ var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans;
6372
+ var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans;
6373
+ if (!oldFirst && !oldLast) return null;
6374
+
6375
+ var startCh = change.from.ch, endCh = change.to.ch, isInsert = cmp(change.from, change.to) == 0;
6376
+ // Get the spans that 'stick out' on both sides
6377
+ var first = markedSpansBefore(oldFirst, startCh, isInsert);
6378
+ var last = markedSpansAfter(oldLast, endCh, isInsert);
6379
+
6380
+ // Next, merge those two ends
6381
+ var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0);
6382
+ if (first) {
6383
+ // Fix up .to properties of first
6384
+ for (var i = 0; i < first.length; ++i) {
6385
+ var span = first[i];
6386
+ if (span.to == null) {
6387
+ var found = getMarkedSpanFor(last, span.marker);
6388
+ if (!found) span.to = startCh;
6389
+ else if (sameLine) span.to = found.to == null ? null : found.to + offset;
6390
+ }
6391
+ }
6392
+ }
6393
+ if (last) {
6394
+ // Fix up .from in last (or move them into first in case of sameLine)
6395
+ for (var i = 0; i < last.length; ++i) {
6396
+ var span = last[i];
6397
+ if (span.to != null) span.to += offset;
6398
+ if (span.from == null) {
6399
+ var found = getMarkedSpanFor(first, span.marker);
6400
+ if (!found) {
6401
+ span.from = offset;
6402
+ if (sameLine) (first || (first = [])).push(span);
6403
+ }
6404
+ } else {
6405
+ span.from += offset;
6406
+ if (sameLine) (first || (first = [])).push(span);
6407
+ }
6408
+ }
6409
+ }
6410
+ // Make sure we didn't create any zero-length spans
6411
+ if (first) first = clearEmptySpans(first);
6412
+ if (last && last != first) last = clearEmptySpans(last);
6413
+
6414
+ var newMarkers = [first];
6415
+ if (!sameLine) {
6416
+ // Fill gap with whole-line-spans
6417
+ var gap = change.text.length - 2, gapMarkers;
6418
+ if (gap > 0 && first)
6419
+ for (var i = 0; i < first.length; ++i)
6420
+ if (first[i].to == null)
6421
+ (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i].marker, null, null));
6422
+ for (var i = 0; i < gap; ++i)
6423
+ newMarkers.push(gapMarkers);
6424
+ newMarkers.push(last);
6425
+ }
6426
+ return newMarkers;
6427
+ }
6428
+
6429
+ // Remove spans that are empty and don't have a clearWhenEmpty
6430
+ // option of false.
6431
+ function clearEmptySpans(spans) {
6432
+ for (var i = 0; i < spans.length; ++i) {
6433
+ var span = spans[i];
6434
+ if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false)
6435
+ spans.splice(i--, 1);
6436
+ }
6437
+ if (!spans.length) return null;
6438
+ return spans;
6439
+ }
6440
+
6441
+ // Used for un/re-doing changes from the history. Combines the
6442
+ // result of computing the existing spans with the set of spans that
6443
+ // existed in the history (so that deleting around a span and then
6444
+ // undoing brings back the span).
6445
+ function mergeOldSpans(doc, change) {
6446
+ var old = getOldSpans(doc, change);
6447
+ var stretched = stretchSpansOverChange(doc, change);
6448
+ if (!old) return stretched;
6449
+ if (!stretched) return old;
6450
+
6451
+ for (var i = 0; i < old.length; ++i) {
6452
+ var oldCur = old[i], stretchCur = stretched[i];
6453
+ if (oldCur && stretchCur) {
6454
+ spans: for (var j = 0; j < stretchCur.length; ++j) {
6455
+ var span = stretchCur[j];
6456
+ for (var k = 0; k < oldCur.length; ++k)
6457
+ if (oldCur[k].marker == span.marker) continue spans;
6458
+ oldCur.push(span);
6459
+ }
6460
+ } else if (stretchCur) {
6461
+ old[i] = stretchCur;
6462
+ }
6463
+ }
6464
+ return old;
6465
+ }
6466
+
6467
+ // Used to 'clip' out readOnly ranges when making a change.
6468
+ function removeReadOnlyRanges(doc, from, to) {
6469
+ var markers = null;
6470
+ doc.iter(from.line, to.line + 1, function(line) {
6471
+ if (line.markedSpans) for (var i = 0; i < line.markedSpans.length; ++i) {
6472
+ var mark = line.markedSpans[i].marker;
6473
+ if (mark.readOnly && (!markers || indexOf(markers, mark) == -1))
6474
+ (markers || (markers = [])).push(mark);
6475
+ }
6476
+ });
6477
+ if (!markers) return null;
6478
+ var parts = [{from: from, to: to}];
6479
+ for (var i = 0; i < markers.length; ++i) {
6480
+ var mk = markers[i], m = mk.find(0);
6481
+ for (var j = 0; j < parts.length; ++j) {
6482
+ var p = parts[j];
6483
+ if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) continue;
6484
+ var newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to);
6485
+ if (dfrom < 0 || !mk.inclusiveLeft && !dfrom)
6486
+ newParts.push({from: p.from, to: m.from});
6487
+ if (dto > 0 || !mk.inclusiveRight && !dto)
6488
+ newParts.push({from: m.to, to: p.to});
6489
+ parts.splice.apply(parts, newParts);
6490
+ j += newParts.length - 1;
6491
+ }
6492
+ }
6493
+ return parts;
6494
+ }
6495
+
6496
+ // Connect or disconnect spans from a line.
6497
+ function detachMarkedSpans(line) {
6498
+ var spans = line.markedSpans;
6499
+ if (!spans) return;
6500
+ for (var i = 0; i < spans.length; ++i)
6501
+ spans[i].marker.detachLine(line);
6502
+ line.markedSpans = null;
6503
+ }
6504
+ function attachMarkedSpans(line, spans) {
6505
+ if (!spans) return;
6506
+ for (var i = 0; i < spans.length; ++i)
6507
+ spans[i].marker.attachLine(line);
6508
+ line.markedSpans = spans;
6509
+ }
6510
+
6511
+ // Helpers used when computing which overlapping collapsed span
6512
+ // counts as the larger one.
6513
+ function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0; }
6514
+ function extraRight(marker) { return marker.inclusiveRight ? 1 : 0; }
6515
+
6516
+ // Returns a number indicating which of two overlapping collapsed
6517
+ // spans is larger (and thus includes the other). Falls back to
6518
+ // comparing ids when the spans cover exactly the same range.
6519
+ function compareCollapsedMarkers(a, b) {
6520
+ var lenDiff = a.lines.length - b.lines.length;
6521
+ if (lenDiff != 0) return lenDiff;
6522
+ var aPos = a.find(), bPos = b.find();
6523
+ var fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b);
6524
+ if (fromCmp) return -fromCmp;
6525
+ var toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b);
6526
+ if (toCmp) return toCmp;
6527
+ return b.id - a.id;
6528
+ }
6529
+
6530
+ // Find out whether a line ends or starts in a collapsed span. If
6531
+ // so, return the marker for that span.
6532
+ function collapsedSpanAtSide(line, start) {
6533
+ var sps = sawCollapsedSpans && line.markedSpans, found;
6534
+ if (sps) for (var sp, i = 0; i < sps.length; ++i) {
6535
+ sp = sps[i];
6536
+ if (sp.marker.collapsed && (start ? sp.from : sp.to) == null &&
6537
+ (!found || compareCollapsedMarkers(found, sp.marker) < 0))
6538
+ found = sp.marker;
6539
+ }
6540
+ return found;
6541
+ }
6542
+ function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true); }
6543
+ function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false); }
6544
+
6545
+ // Test whether there exists a collapsed span that partially
6546
+ // overlaps (covers the start or end, but not both) of a new span.
6547
+ // Such overlap is not allowed.
6548
+ function conflictingCollapsedRange(doc, lineNo, from, to, marker) {
6549
+ var line = getLine(doc, lineNo);
6550
+ var sps = sawCollapsedSpans && line.markedSpans;
6551
+ if (sps) for (var i = 0; i < sps.length; ++i) {
6552
+ var sp = sps[i];
6553
+ if (!sp.marker.collapsed) continue;
6554
+ var found = sp.marker.find(0);
6555
+ var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker);
6556
+ var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker);
6557
+ if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) continue;
6558
+ if (fromCmp <= 0 && (cmp(found.to, from) > 0 || (sp.marker.inclusiveRight && marker.inclusiveLeft)) ||
6559
+ fromCmp >= 0 && (cmp(found.from, to) < 0 || (sp.marker.inclusiveLeft && marker.inclusiveRight)))
6560
+ return true;
6561
+ }
6562
+ }
6563
+
6564
+ // A visual line is a line as drawn on the screen. Folding, for
6565
+ // example, can cause multiple logical lines to appear on the same
6566
+ // visual line. This finds the start of the visual line that the
6567
+ // given line is part of (usually that is the line itself).
6568
+ function visualLine(line) {
6569
+ var merged;
6570
+ while (merged = collapsedSpanAtStart(line))
6571
+ line = merged.find(-1, true).line;
6572
+ return line;
6573
+ }
6574
+
6575
+ // Returns an array of logical lines that continue the visual line
6576
+ // started by the argument, or undefined if there are no such lines.
6577
+ function visualLineContinued(line) {
6578
+ var merged, lines;
6579
+ while (merged = collapsedSpanAtEnd(line)) {
6580
+ line = merged.find(1, true).line;
6581
+ (lines || (lines = [])).push(line);
6582
+ }
6583
+ return lines;
6584
+ }
6585
+
6586
+ // Get the line number of the start of the visual line that the
6587
+ // given line number is part of.
6588
+ function visualLineNo(doc, lineN) {
6589
+ var line = getLine(doc, lineN), vis = visualLine(line);
6590
+ if (line == vis) return lineN;
6591
+ return lineNo(vis);
6592
+ }
6593
+ // Get the line number of the start of the next visual line after
6594
+ // the given line.
6595
+ function visualLineEndNo(doc, lineN) {
6596
+ if (lineN > doc.lastLine()) return lineN;
6597
+ var line = getLine(doc, lineN), merged;
6598
+ if (!lineIsHidden(doc, line)) return lineN;
6599
+ while (merged = collapsedSpanAtEnd(line))
6600
+ line = merged.find(1, true).line;
6601
+ return lineNo(line) + 1;
6602
+ }
6603
+
6604
+ // Compute whether a line is hidden. Lines count as hidden when they
6605
+ // are part of a visual line that starts with another line, or when
6606
+ // they are entirely covered by collapsed, non-widget span.
6607
+ function lineIsHidden(doc, line) {
6608
+ var sps = sawCollapsedSpans && line.markedSpans;
6609
+ if (sps) for (var sp, i = 0; i < sps.length; ++i) {
6610
+ sp = sps[i];
6611
+ if (!sp.marker.collapsed) continue;
6612
+ if (sp.from == null) return true;
6613
+ if (sp.marker.widgetNode) continue;
6614
+ if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp))
6615
+ return true;
6616
+ }
6617
+ }
6618
+ function lineIsHiddenInner(doc, line, span) {
6619
+ if (span.to == null) {
6620
+ var end = span.marker.find(1, true);
6621
+ return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpans, span.marker));
6622
+ }
6623
+ if (span.marker.inclusiveRight && span.to == line.text.length)
6624
+ return true;
6625
+ for (var sp, i = 0; i < line.markedSpans.length; ++i) {
6626
+ sp = line.markedSpans[i];
6627
+ if (sp.marker.collapsed && !sp.marker.widgetNode && sp.from == span.to &&
6628
+ (sp.to == null || sp.to != span.from) &&
6629
+ (sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
6630
+ lineIsHiddenInner(doc, line, sp)) return true;
6631
+ }
6632
+ }
6633
+
6634
+ // LINE WIDGETS
6635
+
6636
+ // Line widgets are block elements displayed above or below a line.
6637
+
6638
+ var LineWidget = CodeMirror.LineWidget = function(doc, node, options) {
6639
+ if (options) for (var opt in options) if (options.hasOwnProperty(opt))
6640
+ this[opt] = options[opt];
6641
+ this.doc = doc;
6642
+ this.node = node;
6643
+ };
6644
+ eventMixin(LineWidget);
6645
+
6646
+ function adjustScrollWhenAboveVisible(cm, line, diff) {
6647
+ if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop))
6648
+ addToScrollPos(cm, null, diff);
6649
+ }
6650
+
6651
+ LineWidget.prototype.clear = function() {
6652
+ var cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line);
6653
+ if (no == null || !ws) return;
6654
+ for (var i = 0; i < ws.length; ++i) if (ws[i] == this) ws.splice(i--, 1);
6655
+ if (!ws.length) line.widgets = null;
6656
+ var height = widgetHeight(this);
6657
+ updateLineHeight(line, Math.max(0, line.height - height));
6658
+ if (cm) runInOp(cm, function() {
6659
+ adjustScrollWhenAboveVisible(cm, line, -height);
6660
+ regLineChange(cm, no, "widget");
6661
+ });
6662
+ };
6663
+ LineWidget.prototype.changed = function() {
6664
+ var oldH = this.height, cm = this.doc.cm, line = this.line;
6665
+ this.height = null;
6666
+ var diff = widgetHeight(this) - oldH;
6667
+ if (!diff) return;
6668
+ updateLineHeight(line, line.height + diff);
6669
+ if (cm) runInOp(cm, function() {
6670
+ cm.curOp.forceUpdate = true;
6671
+ adjustScrollWhenAboveVisible(cm, line, diff);
6672
+ });
6673
+ };
6674
+
6675
+ function widgetHeight(widget) {
6676
+ if (widget.height != null) return widget.height;
6677
+ var cm = widget.doc.cm;
6678
+ if (!cm) return 0;
6679
+ if (!contains(document.body, widget.node)) {
6680
+ var parentStyle = "position: relative;";
6681
+ if (widget.coverGutter)
6682
+ parentStyle += "margin-left: -" + cm.display.gutters.offsetWidth + "px;";
6683
+ if (widget.noHScroll)
6684
+ parentStyle += "width: " + cm.display.wrapper.clientWidth + "px;";
6685
+ removeChildrenAndAdd(cm.display.measure, elt("div", [widget.node], null, parentStyle));
6686
+ }
6687
+ return widget.height = widget.node.parentNode.offsetHeight;
6688
+ }
6689
+
6690
+ function addLineWidget(doc, handle, node, options) {
6691
+ var widget = new LineWidget(doc, node, options);
6692
+ var cm = doc.cm;
6693
+ if (cm && widget.noHScroll) cm.display.alignWidgets = true;
6694
+ changeLine(doc, handle, "widget", function(line) {
6695
+ var widgets = line.widgets || (line.widgets = []);
6696
+ if (widget.insertAt == null) widgets.push(widget);
6697
+ else widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget);
6698
+ widget.line = line;
6699
+ if (cm && !lineIsHidden(doc, line)) {
6700
+ var aboveVisible = heightAtLine(line) < doc.scrollTop;
6701
+ updateLineHeight(line, line.height + widgetHeight(widget));
6702
+ if (aboveVisible) addToScrollPos(cm, null, widget.height);
6703
+ cm.curOp.forceUpdate = true;
6704
+ }
6705
+ return true;
6706
+ });
6707
+ return widget;
6708
+ }
6709
+
6710
+ // LINE DATA STRUCTURE
6711
+
6712
+ // Line objects. These hold state related to a line, including
6713
+ // highlighting info (the styles array).
6714
+ var Line = CodeMirror.Line = function(text, markedSpans, estimateHeight) {
6715
+ this.text = text;
6716
+ attachMarkedSpans(this, markedSpans);
6717
+ this.height = estimateHeight ? estimateHeight(this) : 1;
6718
+ };
6719
+ eventMixin(Line);
6720
+ Line.prototype.lineNo = function() { return lineNo(this); };
6721
+
6722
+ // Change the content (text, markers) of a line. Automatically
6723
+ // invalidates cached information and tries to re-estimate the
6724
+ // line's height.
6725
+ function updateLine(line, text, markedSpans, estimateHeight) {
6726
+ line.text = text;
6727
+ if (line.stateAfter) line.stateAfter = null;
6728
+ if (line.styles) line.styles = null;
6729
+ if (line.order != null) line.order = null;
6730
+ detachMarkedSpans(line);
6731
+ attachMarkedSpans(line, markedSpans);
6732
+ var estHeight = estimateHeight ? estimateHeight(line) : 1;
6733
+ if (estHeight != line.height) updateLineHeight(line, estHeight);
6734
+ }
6735
+
6736
+ // Detach a line from the document tree and its markers.
6737
+ function cleanUpLine(line) {
6738
+ line.parent = null;
6739
+ detachMarkedSpans(line);
6740
+ }
6741
+
6742
+ function extractLineClasses(type, output) {
6743
+ if (type) for (;;) {
6744
+ var lineClass = type.match(/(?:^|\s+)line-(background-)?(\S+)/);
6745
+ if (!lineClass) break;
6746
+ type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length);
6747
+ var prop = lineClass[1] ? "bgClass" : "textClass";
6748
+ if (output[prop] == null)
6749
+ output[prop] = lineClass[2];
6750
+ else if (!(new RegExp("(?:^|\s)" + lineClass[2] + "(?:$|\s)")).test(output[prop]))
6751
+ output[prop] += " " + lineClass[2];
6752
+ }
6753
+ return type;
6754
+ }
6755
+
6756
+ function callBlankLine(mode, state) {
6757
+ if (mode.blankLine) return mode.blankLine(state);
6758
+ if (!mode.innerMode) return;
6759
+ var inner = CodeMirror.innerMode(mode, state);
6760
+ if (inner.mode.blankLine) return inner.mode.blankLine(inner.state);
6761
+ }
6762
+
6763
+ function readToken(mode, stream, state, inner) {
6764
+ for (var i = 0; i < 10; i++) {
6765
+ if (inner) inner[0] = CodeMirror.innerMode(mode, state).mode;
6766
+ var style = mode.token(stream, state);
6767
+ if (stream.pos > stream.start) return style;
6768
+ }
6769
+ throw new Error("Mode " + mode.name + " failed to advance stream.");
6770
+ }
6771
+
6772
+ // Utility for getTokenAt and getLineTokens
6773
+ function takeToken(cm, pos, precise, asArray) {
6774
+ function getObj(copy) {
6775
+ return {start: stream.start, end: stream.pos,
6776
+ string: stream.current(),
6777
+ type: style || null,
6778
+ state: copy ? copyState(doc.mode, state) : state};
6779
+ }
6780
+
6781
+ var doc = cm.doc, mode = doc.mode, style;
6782
+ pos = clipPos(doc, pos);
6783
+ var line = getLine(doc, pos.line), state = getStateBefore(cm, pos.line, precise);
6784
+ var stream = new StringStream(line.text, cm.options.tabSize), tokens;
6785
+ if (asArray) tokens = [];
6786
+ while ((asArray || stream.pos < pos.ch) && !stream.eol()) {
6787
+ stream.start = stream.pos;
6788
+ style = readToken(mode, stream, state);
6789
+ if (asArray) tokens.push(getObj(true));
6790
+ }
6791
+ return asArray ? tokens : getObj();
6792
+ }
6793
+
6794
+ // Run the given mode's parser over a line, calling f for each token.
6795
+ function runMode(cm, text, mode, state, f, lineClasses, forceToEnd) {
6796
+ var flattenSpans = mode.flattenSpans;
6797
+ if (flattenSpans == null) flattenSpans = cm.options.flattenSpans;
6798
+ var curStart = 0, curStyle = null;
6799
+ var stream = new StringStream(text, cm.options.tabSize), style;
6800
+ var inner = cm.options.addModeClass && [null];
6801
+ if (text == "") extractLineClasses(callBlankLine(mode, state), lineClasses);
6802
+ while (!stream.eol()) {
6803
+ if (stream.pos > cm.options.maxHighlightLength) {
6804
+ flattenSpans = false;
6805
+ if (forceToEnd) processLine(cm, text, state, stream.pos);
6806
+ stream.pos = text.length;
6807
+ style = null;
6808
+ } else {
6809
+ style = extractLineClasses(readToken(mode, stream, state, inner), lineClasses);
6810
+ }
6811
+ if (inner) {
6812
+ var mName = inner[0].name;
6813
+ if (mName) style = "m-" + (style ? mName + " " + style : mName);
6814
+ }
6815
+ if (!flattenSpans || curStyle != style) {
6816
+ while (curStart < stream.start) {
6817
+ curStart = Math.min(stream.start, curStart + 50000);
6818
+ f(curStart, curStyle);
6819
+ }
6820
+ curStyle = style;
6821
+ }
6822
+ stream.start = stream.pos;
6823
+ }
6824
+ while (curStart < stream.pos) {
6825
+ // Webkit seems to refuse to render text nodes longer than 57444 characters
6826
+ var pos = Math.min(stream.pos, curStart + 50000);
6827
+ f(pos, curStyle);
6828
+ curStart = pos;
6829
+ }
6830
+ }
6831
+
6832
+ // Compute a style array (an array starting with a mode generation
6833
+ // -- for invalidation -- followed by pairs of end positions and
6834
+ // style strings), which is used to highlight the tokens on the
6835
+ // line.
6836
+ function highlightLine(cm, line, state, forceToEnd) {
6837
+ // A styles array always starts with a number identifying the
6838
+ // mode/overlays that it is based on (for easy invalidation).
6839
+ var st = [cm.state.modeGen], lineClasses = {};
6840
+ // Compute the base array of styles
6841
+ runMode(cm, line.text, cm.doc.mode, state, function(end, style) {
6842
+ st.push(end, style);
6843
+ }, lineClasses, forceToEnd);
6844
+
6845
+ // Run overlays, adjust style array.
6846
+ for (var o = 0; o < cm.state.overlays.length; ++o) {
6847
+ var overlay = cm.state.overlays[o], i = 1, at = 0;
6848
+ runMode(cm, line.text, overlay.mode, true, function(end, style) {
6849
+ var start = i;
6850
+ // Ensure there's a token end at the current position, and that i points at it
6851
+ while (at < end) {
6852
+ var i_end = st[i];
6853
+ if (i_end > end)
6854
+ st.splice(i, 1, end, st[i+1], i_end);
6855
+ i += 2;
6856
+ at = Math.min(end, i_end);
6857
+ }
6858
+ if (!style) return;
6859
+ if (overlay.opaque) {
6860
+ st.splice(start, i - start, end, "cm-overlay " + style);
6861
+ i = start + 2;
6862
+ } else {
6863
+ for (; start < i; start += 2) {
6864
+ var cur = st[start+1];
6865
+ st[start+1] = (cur ? cur + " " : "") + "cm-overlay " + style;
6866
+ }
6867
+ }
6868
+ }, lineClasses);
6869
+ }
6870
+
6871
+ return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null};
6872
+ }
6873
+
6874
+ function getLineStyles(cm, line, updateFrontier) {
6875
+ if (!line.styles || line.styles[0] != cm.state.modeGen) {
6876
+ var state = getStateBefore(cm, lineNo(line));
6877
+ var result = highlightLine(cm, line, line.text.length > cm.options.maxHighlightLength ? copyState(cm.doc.mode, state) : state);
6878
+ line.stateAfter = state;
6879
+ line.styles = result.styles;
6880
+ if (result.classes) line.styleClasses = result.classes;
6881
+ else if (line.styleClasses) line.styleClasses = null;
6882
+ if (updateFrontier === cm.doc.frontier) cm.doc.frontier++;
6883
+ }
6884
+ return line.styles;
6885
+ }
6886
+
6887
+ // Lightweight form of highlight -- proceed over this line and
6888
+ // update state, but don't save a style array. Used for lines that
6889
+ // aren't currently visible.
6890
+ function processLine(cm, text, state, startAt) {
6891
+ var mode = cm.doc.mode;
6892
+ var stream = new StringStream(text, cm.options.tabSize);
6893
+ stream.start = stream.pos = startAt || 0;
6894
+ if (text == "") callBlankLine(mode, state);
6895
+ while (!stream.eol()) {
6896
+ readToken(mode, stream, state);
6897
+ stream.start = stream.pos;
6898
+ }
6899
+ }
6900
+
6901
+ // Convert a style as returned by a mode (either null, or a string
6902
+ // containing one or more styles) to a CSS style. This is cached,
6903
+ // and also looks for line-wide styles.
6904
+ var styleToClassCache = {}, styleToClassCacheWithMode = {};
6905
+ function interpretTokenStyle(style, options) {
6906
+ if (!style || /^\s*$/.test(style)) return null;
6907
+ var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache;
6908
+ return cache[style] ||
6909
+ (cache[style] = style.replace(/\S+/g, "cm-$&"));
6910
+ }
6911
+
6912
+ // Render the DOM representation of the text of a line. Also builds
6913
+ // up a 'line map', which points at the DOM nodes that represent
6914
+ // specific stretches of text, and is used by the measuring code.
6915
+ // The returned object contains the DOM node, this map, and
6916
+ // information about line-wide styles that were set by the mode.
6917
+ function buildLineContent(cm, lineView) {
6918
+ // The padding-right forces the element to have a 'border', which
6919
+ // is needed on Webkit to be able to get line-level bounding
6920
+ // rectangles for it (in measureChar).
6921
+ var content = elt("span", null, null, webkit ? "padding-right: .1px" : null);
6922
+ var builder = {pre: elt("pre", [content], "CodeMirror-line"), content: content,
6923
+ col: 0, pos: 0, cm: cm,
6924
+ splitSpaces: (ie || webkit) && cm.getOption("lineWrapping")};
6925
+ lineView.measure = {};
6926
+
6927
+ // Iterate over the logical lines that make up this visual line.
6928
+ for (var i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) {
6929
+ var line = i ? lineView.rest[i - 1] : lineView.line, order;
6930
+ builder.pos = 0;
6931
+ builder.addToken = buildToken;
6932
+ // Optionally wire in some hacks into the token-rendering
6933
+ // algorithm, to deal with browser quirks.
6934
+ if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line)))
6935
+ builder.addToken = buildTokenBadBidi(builder.addToken, order);
6936
+ builder.map = [];
6937
+ var allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line);
6938
+ insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate));
6939
+ if (line.styleClasses) {
6940
+ if (line.styleClasses.bgClass)
6941
+ builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || "");
6942
+ if (line.styleClasses.textClass)
6943
+ builder.textClass = joinClasses(line.styleClasses.textClass, builder.textClass || "");
6944
+ }
6945
+
6946
+ // Ensure at least a single node is present, for measuring.
6947
+ if (builder.map.length == 0)
6948
+ builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.display.measure)));
6949
+
6950
+ // Store the map and a cache object for the current logical line
6951
+ if (i == 0) {
6952
+ lineView.measure.map = builder.map;
6953
+ lineView.measure.cache = {};
6954
+ } else {
6955
+ (lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map);
6956
+ (lineView.measure.caches || (lineView.measure.caches = [])).push({});
6957
+ }
6958
+ }
6959
+
6960
+ // See issue #2901
6961
+ if (webkit && /\bcm-tab\b/.test(builder.content.lastChild.className))
6962
+ builder.content.className = "cm-tab-wrap-hack";
6963
+
6964
+ signal(cm, "renderLine", cm, lineView.line, builder.pre);
6965
+ if (builder.pre.className)
6966
+ builder.textClass = joinClasses(builder.pre.className, builder.textClass || "");
6967
+
6968
+ return builder;
6969
+ }
6970
+
6971
+ function defaultSpecialCharPlaceholder(ch) {
6972
+ var token = elt("span", "\u2022", "cm-invalidchar");
6973
+ token.title = "\\u" + ch.charCodeAt(0).toString(16);
6974
+ token.setAttribute("aria-label", token.title);
6975
+ return token;
6976
+ }
6977
+
6978
+ // Build up the DOM representation for a single token, and add it to
6979
+ // the line map. Takes care to render special characters separately.
6980
+ function buildToken(builder, text, style, startStyle, endStyle, title, css) {
6981
+ if (!text) return;
6982
+ var displayText = builder.splitSpaces ? text.replace(/ {3,}/g, splitSpaces) : text;
6983
+ var special = builder.cm.state.specialChars, mustWrap = false;
6984
+ if (!special.test(text)) {
6985
+ builder.col += text.length;
6986
+ var content = document.createTextNode(displayText);
6987
+ builder.map.push(builder.pos, builder.pos + text.length, content);
6988
+ if (ie && ie_version < 9) mustWrap = true;
6989
+ builder.pos += text.length;
6990
+ } else {
6991
+ var content = document.createDocumentFragment(), pos = 0;
6992
+ while (true) {
6993
+ special.lastIndex = pos;
6994
+ var m = special.exec(text);
6995
+ var skipped = m ? m.index - pos : text.length - pos;
6996
+ if (skipped) {
6997
+ var txt = document.createTextNode(displayText.slice(pos, pos + skipped));
6998
+ if (ie && ie_version < 9) content.appendChild(elt("span", [txt]));
6999
+ else content.appendChild(txt);
7000
+ builder.map.push(builder.pos, builder.pos + skipped, txt);
7001
+ builder.col += skipped;
7002
+ builder.pos += skipped;
7003
+ }
7004
+ if (!m) break;
7005
+ pos += skipped + 1;
7006
+ if (m[0] == "\t") {
7007
+ var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize;
7008
+ var txt = content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab"));
7009
+ txt.setAttribute("role", "presentation");
7010
+ txt.setAttribute("cm-text", "\t");
7011
+ builder.col += tabWidth;
7012
+ } else if (m[0] == "\r" || m[0] == "\n") {
7013
+ var txt = content.appendChild(elt("span", m[0] == "\r" ? "\u240d" : "\u2424", "cm-invalidchar"));
7014
+ txt.setAttribute("cm-text", m[0]);
7015
+ builder.col += 1;
7016
+ } else {
7017
+ var txt = builder.cm.options.specialCharPlaceholder(m[0]);
7018
+ txt.setAttribute("cm-text", m[0]);
7019
+ if (ie && ie_version < 9) content.appendChild(elt("span", [txt]));
7020
+ else content.appendChild(txt);
7021
+ builder.col += 1;
7022
+ }
7023
+ builder.map.push(builder.pos, builder.pos + 1, txt);
7024
+ builder.pos++;
7025
+ }
7026
+ }
7027
+ if (style || startStyle || endStyle || mustWrap || css) {
7028
+ var fullStyle = style || "";
7029
+ if (startStyle) fullStyle += startStyle;
7030
+ if (endStyle) fullStyle += endStyle;
7031
+ var token = elt("span", [content], fullStyle, css);
7032
+ if (title) token.title = title;
7033
+ return builder.content.appendChild(token);
7034
+ }
7035
+ builder.content.appendChild(content);
7036
+ }
7037
+
7038
+ function splitSpaces(old) {
7039
+ var out = " ";
7040
+ for (var i = 0; i < old.length - 2; ++i) out += i % 2 ? " " : "\u00a0";
7041
+ out += " ";
7042
+ return out;
7043
+ }
7044
+
7045
+ // Work around nonsense dimensions being reported for stretches of
7046
+ // right-to-left text.
7047
+ function buildTokenBadBidi(inner, order) {
7048
+ return function(builder, text, style, startStyle, endStyle, title, css) {
7049
+ style = style ? style + " cm-force-border" : "cm-force-border";
7050
+ var start = builder.pos, end = start + text.length;
7051
+ for (;;) {
7052
+ // Find the part that overlaps with the start of this text
7053
+ for (var i = 0; i < order.length; i++) {
7054
+ var part = order[i];
7055
+ if (part.to > start && part.from <= start) break;
7056
+ }
7057
+ if (part.to >= end) return inner(builder, text, style, startStyle, endStyle, title, css);
7058
+ inner(builder, text.slice(0, part.to - start), style, startStyle, null, title, css);
7059
+ startStyle = null;
7060
+ text = text.slice(part.to - start);
7061
+ start = part.to;
7062
+ }
7063
+ };
7064
+ }
7065
+
7066
+ function buildCollapsedSpan(builder, size, marker, ignoreWidget) {
7067
+ var widget = !ignoreWidget && marker.widgetNode;
7068
+ if (widget) builder.map.push(builder.pos, builder.pos + size, widget);
7069
+ if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) {
7070
+ if (!widget)
7071
+ widget = builder.content.appendChild(document.createElement("span"));
7072
+ widget.setAttribute("cm-marker", marker.id);
7073
+ }
7074
+ if (widget) {
7075
+ builder.cm.display.input.setUneditable(widget);
7076
+ builder.content.appendChild(widget);
7077
+ }
7078
+ builder.pos += size;
7079
+ }
7080
+
7081
+ // Outputs a number of spans to make up a line, taking highlighting
7082
+ // and marked text into account.
7083
+ function insertLineContent(line, builder, styles) {
7084
+ var spans = line.markedSpans, allText = line.text, at = 0;
7085
+ if (!spans) {
7086
+ for (var i = 1; i < styles.length; i+=2)
7087
+ builder.addToken(builder, allText.slice(at, at = styles[i]), interpretTokenStyle(styles[i+1], builder.cm.options));
7088
+ return;
7089
+ }
7090
+
7091
+ var len = allText.length, pos = 0, i = 1, text = "", style, css;
7092
+ var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, title, collapsed;
7093
+ for (;;) {
7094
+ if (nextChange == pos) { // Update current marker set
7095
+ spanStyle = spanEndStyle = spanStartStyle = title = css = "";
7096
+ collapsed = null; nextChange = Infinity;
7097
+ var foundBookmarks = [], endStyles
7098
+ for (var j = 0; j < spans.length; ++j) {
7099
+ var sp = spans[j], m = sp.marker;
7100
+ if (m.type == "bookmark" && sp.from == pos && m.widgetNode) {
7101
+ foundBookmarks.push(m);
7102
+ } else if (sp.from <= pos && (sp.to == null || sp.to > pos || m.collapsed && sp.to == pos && sp.from == pos)) {
7103
+ if (sp.to != null && sp.to != pos && nextChange > sp.to) {
7104
+ nextChange = sp.to;
7105
+ spanEndStyle = "";
7106
+ }
7107
+ if (m.className) spanStyle += " " + m.className;
7108
+ if (m.css) css = (css ? css + ";" : "") + m.css;
7109
+ if (m.startStyle && sp.from == pos) spanStartStyle += " " + m.startStyle;
7110
+ if (m.endStyle && sp.to == nextChange) (endStyles || (endStyles = [])).push(m.endStyle, sp.to)
7111
+ if (m.title && !title) title = m.title;
7112
+ if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0))
7113
+ collapsed = sp;
7114
+ } else if (sp.from > pos && nextChange > sp.from) {
7115
+ nextChange = sp.from;
7116
+ }
7117
+ }
7118
+ if (endStyles) for (var j = 0; j < endStyles.length; j += 2)
7119
+ if (endStyles[j + 1] == nextChange) spanEndStyle += " " + endStyles[j]
7120
+
7121
+ if (!collapsed || collapsed.from == pos) for (var j = 0; j < foundBookmarks.length; ++j)
7122
+ buildCollapsedSpan(builder, 0, foundBookmarks[j]);
7123
+ if (collapsed && (collapsed.from || 0) == pos) {
7124
+ buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos,
7125
+ collapsed.marker, collapsed.from == null);
7126
+ if (collapsed.to == null) return;
7127
+ if (collapsed.to == pos) collapsed = false;
7128
+ }
7129
+ }
7130
+ if (pos >= len) break;
7131
+
7132
+ var upto = Math.min(len, nextChange);
7133
+ while (true) {
7134
+ if (text) {
7135
+ var end = pos + text.length;
7136
+ if (!collapsed) {
7137
+ var tokenText = end > upto ? text.slice(0, upto - pos) : text;
7138
+ builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle,
7139
+ spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", title, css);
7140
+ }
7141
+ if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;}
7142
+ pos = end;
7143
+ spanStartStyle = "";
7144
+ }
7145
+ text = allText.slice(at, at = styles[i++]);
7146
+ style = interpretTokenStyle(styles[i++], builder.cm.options);
7147
+ }
7148
+ }
7149
+ }
7150
+
7151
+ // DOCUMENT DATA STRUCTURE
7152
+
7153
+ // By default, updates that start and end at the beginning of a line
7154
+ // are treated specially, in order to make the association of line
7155
+ // widgets and marker elements with the text behave more intuitive.
7156
+ function isWholeLineUpdate(doc, change) {
7157
+ return change.from.ch == 0 && change.to.ch == 0 && lst(change.text) == "" &&
7158
+ (!doc.cm || doc.cm.options.wholeLineUpdateBefore);
7159
+ }
7160
+
7161
+ // Perform a change on the document data structure.
7162
+ function updateDoc(doc, change, markedSpans, estimateHeight) {
7163
+ function spansFor(n) {return markedSpans ? markedSpans[n] : null;}
7164
+ function update(line, text, spans) {
7165
+ updateLine(line, text, spans, estimateHeight);
7166
+ signalLater(line, "change", line, change);
7167
+ }
7168
+ function linesFor(start, end) {
7169
+ for (var i = start, result = []; i < end; ++i)
7170
+ result.push(new Line(text[i], spansFor(i), estimateHeight));
7171
+ return result;
7172
+ }
7173
+
7174
+ var from = change.from, to = change.to, text = change.text;
7175
+ var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line);
7176
+ var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line;
7177
+
7178
+ // Adjust the line structure
7179
+ if (change.full) {
7180
+ doc.insert(0, linesFor(0, text.length));
7181
+ doc.remove(text.length, doc.size - text.length);
7182
+ } else if (isWholeLineUpdate(doc, change)) {
7183
+ // This is a whole-line replace. Treated specially to make
7184
+ // sure line objects move the way they are supposed to.
7185
+ var added = linesFor(0, text.length - 1);
7186
+ update(lastLine, lastLine.text, lastSpans);
7187
+ if (nlines) doc.remove(from.line, nlines);
7188
+ if (added.length) doc.insert(from.line, added);
7189
+ } else if (firstLine == lastLine) {
7190
+ if (text.length == 1) {
7191
+ update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans);
7192
+ } else {
7193
+ var added = linesFor(1, text.length - 1);
7194
+ added.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight));
7195
+ update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
7196
+ doc.insert(from.line + 1, added);
7197
+ }
7198
+ } else if (text.length == 1) {
7199
+ update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0));
7200
+ doc.remove(from.line + 1, nlines);
7201
+ } else {
7202
+ update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
7203
+ update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans);
7204
+ var added = linesFor(1, text.length - 1);
7205
+ if (nlines > 1) doc.remove(from.line + 1, nlines - 1);
7206
+ doc.insert(from.line + 1, added);
7207
+ }
7208
+
7209
+ signalLater(doc, "change", doc, change);
7210
+ }
7211
+
7212
+ // The document is represented as a BTree consisting of leaves, with
7213
+ // chunk of lines in them, and branches, with up to ten leaves or
7214
+ // other branch nodes below them. The top node is always a branch
7215
+ // node, and is the document object itself (meaning it has
7216
+ // additional methods and properties).
7217
+ //
7218
+ // All nodes have parent links. The tree is used both to go from
7219
+ // line numbers to line objects, and to go from objects to numbers.
7220
+ // It also indexes by height, and is used to convert between height
7221
+ // and line object, and to find the total height of the document.
7222
+ //
7223
+ // See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html
7224
+
7225
+ function LeafChunk(lines) {
7226
+ this.lines = lines;
7227
+ this.parent = null;
7228
+ for (var i = 0, height = 0; i < lines.length; ++i) {
7229
+ lines[i].parent = this;
7230
+ height += lines[i].height;
7231
+ }
7232
+ this.height = height;
7233
+ }
7234
+
7235
+ LeafChunk.prototype = {
7236
+ chunkSize: function() { return this.lines.length; },
7237
+ // Remove the n lines at offset 'at'.
7238
+ removeInner: function(at, n) {
7239
+ for (var i = at, e = at + n; i < e; ++i) {
7240
+ var line = this.lines[i];
7241
+ this.height -= line.height;
7242
+ cleanUpLine(line);
7243
+ signalLater(line, "delete");
7244
+ }
7245
+ this.lines.splice(at, n);
7246
+ },
7247
+ // Helper used to collapse a small branch into a single leaf.
7248
+ collapse: function(lines) {
7249
+ lines.push.apply(lines, this.lines);
7250
+ },
7251
+ // Insert the given array of lines at offset 'at', count them as
7252
+ // having the given height.
7253
+ insertInner: function(at, lines, height) {
7254
+ this.height += height;
7255
+ this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at));
7256
+ for (var i = 0; i < lines.length; ++i) lines[i].parent = this;
7257
+ },
7258
+ // Used to iterate over a part of the tree.
7259
+ iterN: function(at, n, op) {
7260
+ for (var e = at + n; at < e; ++at)
7261
+ if (op(this.lines[at])) return true;
7262
+ }
7263
+ };
7264
+
7265
+ function BranchChunk(children) {
7266
+ this.children = children;
7267
+ var size = 0, height = 0;
7268
+ for (var i = 0; i < children.length; ++i) {
7269
+ var ch = children[i];
7270
+ size += ch.chunkSize(); height += ch.height;
7271
+ ch.parent = this;
7272
+ }
7273
+ this.size = size;
7274
+ this.height = height;
7275
+ this.parent = null;
7276
+ }
7277
+
7278
+ BranchChunk.prototype = {
7279
+ chunkSize: function() { return this.size; },
7280
+ removeInner: function(at, n) {
7281
+ this.size -= n;
7282
+ for (var i = 0; i < this.children.length; ++i) {
7283
+ var child = this.children[i], sz = child.chunkSize();
7284
+ if (at < sz) {
7285
+ var rm = Math.min(n, sz - at), oldHeight = child.height;
7286
+ child.removeInner(at, rm);
7287
+ this.height -= oldHeight - child.height;
7288
+ if (sz == rm) { this.children.splice(i--, 1); child.parent = null; }
7289
+ if ((n -= rm) == 0) break;
7290
+ at = 0;
7291
+ } else at -= sz;
7292
+ }
7293
+ // If the result is smaller than 25 lines, ensure that it is a
7294
+ // single leaf node.
7295
+ if (this.size - n < 25 &&
7296
+ (this.children.length > 1 || !(this.children[0] instanceof LeafChunk))) {
7297
+ var lines = [];
7298
+ this.collapse(lines);
7299
+ this.children = [new LeafChunk(lines)];
7300
+ this.children[0].parent = this;
7301
+ }
7302
+ },
7303
+ collapse: function(lines) {
7304
+ for (var i = 0; i < this.children.length; ++i) this.children[i].collapse(lines);
7305
+ },
7306
+ insertInner: function(at, lines, height) {
7307
+ this.size += lines.length;
7308
+ this.height += height;
7309
+ for (var i = 0; i < this.children.length; ++i) {
7310
+ var child = this.children[i], sz = child.chunkSize();
7311
+ if (at <= sz) {
7312
+ child.insertInner(at, lines, height);
7313
+ if (child.lines && child.lines.length > 50) {
7314
+ while (child.lines.length > 50) {
7315
+ var spilled = child.lines.splice(child.lines.length - 25, 25);
7316
+ var newleaf = new LeafChunk(spilled);
7317
+ child.height -= newleaf.height;
7318
+ this.children.splice(i + 1, 0, newleaf);
7319
+ newleaf.parent = this;
7320
+ }
7321
+ this.maybeSpill();
7322
+ }
7323
+ break;
7324
+ }
7325
+ at -= sz;
7326
+ }
7327
+ },
7328
+ // When a node has grown, check whether it should be split.
7329
+ maybeSpill: function() {
7330
+ if (this.children.length <= 10) return;
7331
+ var me = this;
7332
+ do {
7333
+ var spilled = me.children.splice(me.children.length - 5, 5);
7334
+ var sibling = new BranchChunk(spilled);
7335
+ if (!me.parent) { // Become the parent node
7336
+ var copy = new BranchChunk(me.children);
7337
+ copy.parent = me;
7338
+ me.children = [copy, sibling];
7339
+ me = copy;
7340
+ } else {
7341
+ me.size -= sibling.size;
7342
+ me.height -= sibling.height;
7343
+ var myIndex = indexOf(me.parent.children, me);
7344
+ me.parent.children.splice(myIndex + 1, 0, sibling);
7345
+ }
7346
+ sibling.parent = me.parent;
7347
+ } while (me.children.length > 10);
7348
+ me.parent.maybeSpill();
7349
+ },
7350
+ iterN: function(at, n, op) {
7351
+ for (var i = 0; i < this.children.length; ++i) {
7352
+ var child = this.children[i], sz = child.chunkSize();
7353
+ if (at < sz) {
7354
+ var used = Math.min(n, sz - at);
7355
+ if (child.iterN(at, used, op)) return true;
7356
+ if ((n -= used) == 0) break;
7357
+ at = 0;
7358
+ } else at -= sz;
7359
+ }
7360
+ }
7361
+ };
7362
+
7363
+ var nextDocId = 0;
7364
+ var Doc = CodeMirror.Doc = function(text, mode, firstLine, lineSep) {
7365
+ if (!(this instanceof Doc)) return new Doc(text, mode, firstLine, lineSep);
7366
+ if (firstLine == null) firstLine = 0;
7367
+
7368
+ BranchChunk.call(this, [new LeafChunk([new Line("", null)])]);
7369
+ this.first = firstLine;
7370
+ this.scrollTop = this.scrollLeft = 0;
7371
+ this.cantEdit = false;
7372
+ this.cleanGeneration = 1;
7373
+ this.frontier = firstLine;
7374
+ var start = Pos(firstLine, 0);
7375
+ this.sel = simpleSelection(start);
7376
+ this.history = new History(null);
7377
+ this.id = ++nextDocId;
7378
+ this.modeOption = mode;
7379
+ this.lineSep = lineSep;
7380
+ this.extend = false;
7381
+
7382
+ if (typeof text == "string") text = this.splitLines(text);
7383
+ updateDoc(this, {from: start, to: start, text: text});
7384
+ setSelection(this, simpleSelection(start), sel_dontScroll);
7385
+ };
7386
+
7387
+ Doc.prototype = createObj(BranchChunk.prototype, {
7388
+ constructor: Doc,
7389
+ // Iterate over the document. Supports two forms -- with only one
7390
+ // argument, it calls that for each line in the document. With
7391
+ // three, it iterates over the range given by the first two (with
7392
+ // the second being non-inclusive).
7393
+ iter: function(from, to, op) {
7394
+ if (op) this.iterN(from - this.first, to - from, op);
7395
+ else this.iterN(this.first, this.first + this.size, from);
7396
+ },
7397
+
7398
+ // Non-public interface for adding and removing lines.
7399
+ insert: function(at, lines) {
7400
+ var height = 0;
7401
+ for (var i = 0; i < lines.length; ++i) height += lines[i].height;
7402
+ this.insertInner(at - this.first, lines, height);
7403
+ },
7404
+ remove: function(at, n) { this.removeInner(at - this.first, n); },
7405
+
7406
+ // From here, the methods are part of the public interface. Most
7407
+ // are also available from CodeMirror (editor) instances.
7408
+
7409
+ getValue: function(lineSep) {
7410
+ var lines = getLines(this, this.first, this.first + this.size);
7411
+ if (lineSep === false) return lines;
7412
+ return lines.join(lineSep || this.lineSeparator());
7413
+ },
7414
+ setValue: docMethodOp(function(code) {
7415
+ var top = Pos(this.first, 0), last = this.first + this.size - 1;
7416
+ makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length),
7417
+ text: this.splitLines(code), origin: "setValue", full: true}, true);
7418
+ setSelection(this, simpleSelection(top));
7419
+ }),
7420
+ replaceRange: function(code, from, to, origin) {
7421
+ from = clipPos(this, from);
7422
+ to = to ? clipPos(this, to) : from;
7423
+ replaceRange(this, code, from, to, origin);
7424
+ },
7425
+ getRange: function(from, to, lineSep) {
7426
+ var lines = getBetween(this, clipPos(this, from), clipPos(this, to));
7427
+ if (lineSep === false) return lines;
7428
+ return lines.join(lineSep || this.lineSeparator());
7429
+ },
7430
+
7431
+ getLine: function(line) {var l = this.getLineHandle(line); return l && l.text;},
7432
+
7433
+ getLineHandle: function(line) {if (isLine(this, line)) return getLine(this, line);},
7434
+ getLineNumber: function(line) {return lineNo(line);},
7435
+
7436
+ getLineHandleVisualStart: function(line) {
7437
+ if (typeof line == "number") line = getLine(this, line);
7438
+ return visualLine(line);
7439
+ },
7440
+
7441
+ lineCount: function() {return this.size;},
7442
+ firstLine: function() {return this.first;},
7443
+ lastLine: function() {return this.first + this.size - 1;},
7444
+
7445
+ clipPos: function(pos) {return clipPos(this, pos);},
7446
+
7447
+ getCursor: function(start) {
7448
+ var range = this.sel.primary(), pos;
7449
+ if (start == null || start == "head") pos = range.head;
7450
+ else if (start == "anchor") pos = range.anchor;
7451
+ else if (start == "end" || start == "to" || start === false) pos = range.to();
7452
+ else pos = range.from();
7453
+ return pos;
7454
+ },
7455
+ listSelections: function() { return this.sel.ranges; },
7456
+ somethingSelected: function() {return this.sel.somethingSelected();},
7457
+
7458
+ setCursor: docMethodOp(function(line, ch, options) {
7459
+ setSimpleSelection(this, clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line), null, options);
7460
+ }),
7461
+ setSelection: docMethodOp(function(anchor, head, options) {
7462
+ setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options);
7463
+ }),
7464
+ extendSelection: docMethodOp(function(head, other, options) {
7465
+ extendSelection(this, clipPos(this, head), other && clipPos(this, other), options);
7466
+ }),
7467
+ extendSelections: docMethodOp(function(heads, options) {
7468
+ extendSelections(this, clipPosArray(this, heads), options);
7469
+ }),
7470
+ extendSelectionsBy: docMethodOp(function(f, options) {
7471
+ var heads = map(this.sel.ranges, f);
7472
+ extendSelections(this, clipPosArray(this, heads), options);
7473
+ }),
7474
+ setSelections: docMethodOp(function(ranges, primary, options) {
7475
+ if (!ranges.length) return;
7476
+ for (var i = 0, out = []; i < ranges.length; i++)
7477
+ out[i] = new Range(clipPos(this, ranges[i].anchor),
7478
+ clipPos(this, ranges[i].head));
7479
+ if (primary == null) primary = Math.min(ranges.length - 1, this.sel.primIndex);
7480
+ setSelection(this, normalizeSelection(out, primary), options);
7481
+ }),
7482
+ addSelection: docMethodOp(function(anchor, head, options) {
7483
+ var ranges = this.sel.ranges.slice(0);
7484
+ ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor)));
7485
+ setSelection(this, normalizeSelection(ranges, ranges.length - 1), options);
7486
+ }),
7487
+
7488
+ getSelection: function(lineSep) {
7489
+ var ranges = this.sel.ranges, lines;
7490
+ for (var i = 0; i < ranges.length; i++) {
7491
+ var sel = getBetween(this, ranges[i].from(), ranges[i].to());
7492
+ lines = lines ? lines.concat(sel) : sel;
7493
+ }
7494
+ if (lineSep === false) return lines;
7495
+ else return lines.join(lineSep || this.lineSeparator());
7496
+ },
7497
+ getSelections: function(lineSep) {
7498
+ var parts = [], ranges = this.sel.ranges;
7499
+ for (var i = 0; i < ranges.length; i++) {
7500
+ var sel = getBetween(this, ranges[i].from(), ranges[i].to());
7501
+ if (lineSep !== false) sel = sel.join(lineSep || this.lineSeparator());
7502
+ parts[i] = sel;
7503
+ }
7504
+ return parts;
7505
+ },
7506
+ replaceSelection: function(code, collapse, origin) {
7507
+ var dup = [];
7508
+ for (var i = 0; i < this.sel.ranges.length; i++)
7509
+ dup[i] = code;
7510
+ this.replaceSelections(dup, collapse, origin || "+input");
7511
+ },
7512
+ replaceSelections: docMethodOp(function(code, collapse, origin) {
7513
+ var changes = [], sel = this.sel;
7514
+ for (var i = 0; i < sel.ranges.length; i++) {
7515
+ var range = sel.ranges[i];
7516
+ changes[i] = {from: range.from(), to: range.to(), text: this.splitLines(code[i]), origin: origin};
7517
+ }
7518
+ var newSel = collapse && collapse != "end" && computeReplacedSel(this, changes, collapse);
7519
+ for (var i = changes.length - 1; i >= 0; i--)
7520
+ makeChange(this, changes[i]);
7521
+ if (newSel) setSelectionReplaceHistory(this, newSel);
7522
+ else if (this.cm) ensureCursorVisible(this.cm);
7523
+ }),
7524
+ undo: docMethodOp(function() {makeChangeFromHistory(this, "undo");}),
7525
+ redo: docMethodOp(function() {makeChangeFromHistory(this, "redo");}),
7526
+ undoSelection: docMethodOp(function() {makeChangeFromHistory(this, "undo", true);}),
7527
+ redoSelection: docMethodOp(function() {makeChangeFromHistory(this, "redo", true);}),
7528
+
7529
+ setExtending: function(val) {this.extend = val;},
7530
+ getExtending: function() {return this.extend;},
7531
+
7532
+ historySize: function() {
7533
+ var hist = this.history, done = 0, undone = 0;
7534
+ for (var i = 0; i < hist.done.length; i++) if (!hist.done[i].ranges) ++done;
7535
+ for (var i = 0; i < hist.undone.length; i++) if (!hist.undone[i].ranges) ++undone;
7536
+ return {undo: done, redo: undone};
7537
+ },
7538
+ clearHistory: function() {this.history = new History(this.history.maxGeneration);},
7539
+
7540
+ markClean: function() {
7541
+ this.cleanGeneration = this.changeGeneration(true);
7542
+ },
7543
+ changeGeneration: function(forceSplit) {
7544
+ if (forceSplit)
7545
+ this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null;
7546
+ return this.history.generation;
7547
+ },
7548
+ isClean: function (gen) {
7549
+ return this.history.generation == (gen || this.cleanGeneration);
7550
+ },
7551
+
7552
+ getHistory: function() {
7553
+ return {done: copyHistoryArray(this.history.done),
7554
+ undone: copyHistoryArray(this.history.undone)};
7555
+ },
7556
+ setHistory: function(histData) {
7557
+ var hist = this.history = new History(this.history.maxGeneration);
7558
+ hist.done = copyHistoryArray(histData.done.slice(0), null, true);
7559
+ hist.undone = copyHistoryArray(histData.undone.slice(0), null, true);
7560
+ },
7561
+
7562
+ addLineClass: docMethodOp(function(handle, where, cls) {
7563
+ return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function(line) {
7564
+ var prop = where == "text" ? "textClass"
7565
+ : where == "background" ? "bgClass"
7566
+ : where == "gutter" ? "gutterClass" : "wrapClass";
7567
+ if (!line[prop]) line[prop] = cls;
7568
+ else if (classTest(cls).test(line[prop])) return false;
7569
+ else line[prop] += " " + cls;
7570
+ return true;
7571
+ });
7572
+ }),
7573
+ removeLineClass: docMethodOp(function(handle, where, cls) {
7574
+ return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function(line) {
7575
+ var prop = where == "text" ? "textClass"
7576
+ : where == "background" ? "bgClass"
7577
+ : where == "gutter" ? "gutterClass" : "wrapClass";
7578
+ var cur = line[prop];
7579
+ if (!cur) return false;
7580
+ else if (cls == null) line[prop] = null;
7581
+ else {
7582
+ var found = cur.match(classTest(cls));
7583
+ if (!found) return false;
7584
+ var end = found.index + found[0].length;
7585
+ line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null;
7586
+ }
7587
+ return true;
7588
+ });
7589
+ }),
7590
+
7591
+ addLineWidget: docMethodOp(function(handle, node, options) {
7592
+ return addLineWidget(this, handle, node, options);
7593
+ }),
7594
+ removeLineWidget: function(widget) { widget.clear(); },
7595
+
7596
+ markText: function(from, to, options) {
7597
+ return markText(this, clipPos(this, from), clipPos(this, to), options, options && options.type || "range");
7598
+ },
7599
+ setBookmark: function(pos, options) {
7600
+ var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options),
7601
+ insertLeft: options && options.insertLeft,
7602
+ clearWhenEmpty: false, shared: options && options.shared,
7603
+ handleMouseEvents: options && options.handleMouseEvents};
7604
+ pos = clipPos(this, pos);
7605
+ return markText(this, pos, pos, realOpts, "bookmark");
7606
+ },
7607
+ findMarksAt: function(pos) {
7608
+ pos = clipPos(this, pos);
7609
+ var markers = [], spans = getLine(this, pos.line).markedSpans;
7610
+ if (spans) for (var i = 0; i < spans.length; ++i) {
7611
+ var span = spans[i];
7612
+ if ((span.from == null || span.from <= pos.ch) &&
7613
+ (span.to == null || span.to >= pos.ch))
7614
+ markers.push(span.marker.parent || span.marker);
7615
+ }
7616
+ return markers;
7617
+ },
7618
+ findMarks: function(from, to, filter) {
7619
+ from = clipPos(this, from); to = clipPos(this, to);
7620
+ var found = [], lineNo = from.line;
7621
+ this.iter(from.line, to.line + 1, function(line) {
7622
+ var spans = line.markedSpans;
7623
+ if (spans) for (var i = 0; i < spans.length; i++) {
7624
+ var span = spans[i];
7625
+ if (!(lineNo == from.line && from.ch > span.to ||
7626
+ span.from == null && lineNo != from.line||
7627
+ lineNo == to.line && span.from > to.ch) &&
7628
+ (!filter || filter(span.marker)))
7629
+ found.push(span.marker.parent || span.marker);
7630
+ }
7631
+ ++lineNo;
7632
+ });
7633
+ return found;
7634
+ },
7635
+ getAllMarks: function() {
7636
+ var markers = [];
7637
+ this.iter(function(line) {
7638
+ var sps = line.markedSpans;
7639
+ if (sps) for (var i = 0; i < sps.length; ++i)
7640
+ if (sps[i].from != null) markers.push(sps[i].marker);
7641
+ });
7642
+ return markers;
7643
+ },
7644
+
7645
+ posFromIndex: function(off) {
7646
+ var ch, lineNo = this.first;
7647
+ this.iter(function(line) {
7648
+ var sz = line.text.length + 1;
7649
+ if (sz > off) { ch = off; return true; }
7650
+ off -= sz;
7651
+ ++lineNo;
7652
+ });
7653
+ return clipPos(this, Pos(lineNo, ch));
7654
+ },
7655
+ indexFromPos: function (coords) {
7656
+ coords = clipPos(this, coords);
7657
+ var index = coords.ch;
7658
+ if (coords.line < this.first || coords.ch < 0) return 0;
7659
+ this.iter(this.first, coords.line, function (line) {
7660
+ index += line.text.length + 1;
7661
+ });
7662
+ return index;
7663
+ },
7664
+
7665
+ copy: function(copyHistory) {
7666
+ var doc = new Doc(getLines(this, this.first, this.first + this.size),
7667
+ this.modeOption, this.first, this.lineSep);
7668
+ doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft;
7669
+ doc.sel = this.sel;
7670
+ doc.extend = false;
7671
+ if (copyHistory) {
7672
+ doc.history.undoDepth = this.history.undoDepth;
7673
+ doc.setHistory(this.getHistory());
7674
+ }
7675
+ return doc;
7676
+ },
7677
+
7678
+ linkedDoc: function(options) {
7679
+ if (!options) options = {};
7680
+ var from = this.first, to = this.first + this.size;
7681
+ if (options.from != null && options.from > from) from = options.from;
7682
+ if (options.to != null && options.to < to) to = options.to;
7683
+ var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from, this.lineSep);
7684
+ if (options.sharedHist) copy.history = this.history;
7685
+ (this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist});
7686
+ copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}];
7687
+ copySharedMarkers(copy, findSharedMarkers(this));
7688
+ return copy;
7689
+ },
7690
+ unlinkDoc: function(other) {
7691
+ if (other instanceof CodeMirror) other = other.doc;
7692
+ if (this.linked) for (var i = 0; i < this.linked.length; ++i) {
7693
+ var link = this.linked[i];
7694
+ if (link.doc != other) continue;
7695
+ this.linked.splice(i, 1);
7696
+ other.unlinkDoc(this);
7697
+ detachSharedMarkers(findSharedMarkers(this));
7698
+ break;
7699
+ }
7700
+ // If the histories were shared, split them again
7701
+ if (other.history == this.history) {
7702
+ var splitIds = [other.id];
7703
+ linkedDocs(other, function(doc) {splitIds.push(doc.id);}, true);
7704
+ other.history = new History(null);
7705
+ other.history.done = copyHistoryArray(this.history.done, splitIds);
7706
+ other.history.undone = copyHistoryArray(this.history.undone, splitIds);
7707
+ }
7708
+ },
7709
+ iterLinkedDocs: function(f) {linkedDocs(this, f);},
7710
+
7711
+ getMode: function() {return this.mode;},
7712
+ getEditor: function() {return this.cm;},
7713
+
7714
+ splitLines: function(str) {
7715
+ if (this.lineSep) return str.split(this.lineSep);
7716
+ return splitLinesAuto(str);
7717
+ },
7718
+ lineSeparator: function() { return this.lineSep || "\n"; }
7719
+ });
7720
+
7721
+ // Public alias.
7722
+ Doc.prototype.eachLine = Doc.prototype.iter;
7723
+
7724
+ // Set up methods on CodeMirror's prototype to redirect to the editor's document.
7725
+ var dontDelegate = "iter insert remove copy getEditor constructor".split(" ");
7726
+ for (var prop in Doc.prototype) if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0)
7727
+ CodeMirror.prototype[prop] = (function(method) {
7728
+ return function() {return method.apply(this.doc, arguments);};
7729
+ })(Doc.prototype[prop]);
7730
+
7731
+ eventMixin(Doc);
7732
+
7733
+ // Call f for all linked documents.
7734
+ function linkedDocs(doc, f, sharedHistOnly) {
7735
+ function propagate(doc, skip, sharedHist) {
7736
+ if (doc.linked) for (var i = 0; i < doc.linked.length; ++i) {
7737
+ var rel = doc.linked[i];
7738
+ if (rel.doc == skip) continue;
7739
+ var shared = sharedHist && rel.sharedHist;
7740
+ if (sharedHistOnly && !shared) continue;
7741
+ f(rel.doc, shared);
7742
+ propagate(rel.doc, doc, shared);
7743
+ }
7744
+ }
7745
+ propagate(doc, null, true);
7746
+ }
7747
+
7748
+ // Attach a document to an editor.
7749
+ function attachDoc(cm, doc) {
7750
+ if (doc.cm) throw new Error("This document is already in use.");
7751
+ cm.doc = doc;
7752
+ doc.cm = cm;
7753
+ estimateLineHeights(cm);
7754
+ loadMode(cm);
7755
+ if (!cm.options.lineWrapping) findMaxLine(cm);
7756
+ cm.options.mode = doc.modeOption;
7757
+ regChange(cm);
7758
+ }
7759
+
7760
+ // LINE UTILITIES
7761
+
7762
+ // Find the line object corresponding to the given line number.
7763
+ function getLine(doc, n) {
7764
+ n -= doc.first;
7765
+ if (n < 0 || n >= doc.size) throw new Error("There is no line " + (n + doc.first) + " in the document.");
7766
+ for (var chunk = doc; !chunk.lines;) {
7767
+ for (var i = 0;; ++i) {
7768
+ var child = chunk.children[i], sz = child.chunkSize();
7769
+ if (n < sz) { chunk = child; break; }
7770
+ n -= sz;
7771
+ }
7772
+ }
7773
+ return chunk.lines[n];
7774
+ }
7775
+
7776
+ // Get the part of a document between two positions, as an array of
7777
+ // strings.
7778
+ function getBetween(doc, start, end) {
7779
+ var out = [], n = start.line;
7780
+ doc.iter(start.line, end.line + 1, function(line) {
7781
+ var text = line.text;
7782
+ if (n == end.line) text = text.slice(0, end.ch);
7783
+ if (n == start.line) text = text.slice(start.ch);
7784
+ out.push(text);
7785
+ ++n;
7786
+ });
7787
+ return out;
7788
+ }
7789
+ // Get the lines between from and to, as array of strings.
7790
+ function getLines(doc, from, to) {
7791
+ var out = [];
7792
+ doc.iter(from, to, function(line) { out.push(line.text); });
7793
+ return out;
7794
+ }
7795
+
7796
+ // Update the height of a line, propagating the height change
7797
+ // upwards to parent nodes.
7798
+ function updateLineHeight(line, height) {
7799
+ var diff = height - line.height;
7800
+ if (diff) for (var n = line; n; n = n.parent) n.height += diff;
7801
+ }
7802
+
7803
+ // Given a line object, find its line number by walking up through
7804
+ // its parent links.
7805
+ function lineNo(line) {
7806
+ if (line.parent == null) return null;
7807
+ var cur = line.parent, no = indexOf(cur.lines, line);
7808
+ for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {
7809
+ for (var i = 0;; ++i) {
7810
+ if (chunk.children[i] == cur) break;
7811
+ no += chunk.children[i].chunkSize();
7812
+ }
7813
+ }
7814
+ return no + cur.first;
7815
+ }
7816
+
7817
+ // Find the line at the given vertical position, using the height
7818
+ // information in the document tree.
7819
+ function lineAtHeight(chunk, h) {
7820
+ var n = chunk.first;
7821
+ outer: do {
7822
+ for (var i = 0; i < chunk.children.length; ++i) {
7823
+ var child = chunk.children[i], ch = child.height;
7824
+ if (h < ch) { chunk = child; continue outer; }
7825
+ h -= ch;
7826
+ n += child.chunkSize();
7827
+ }
7828
+ return n;
7829
+ } while (!chunk.lines);
7830
+ for (var i = 0; i < chunk.lines.length; ++i) {
7831
+ var line = chunk.lines[i], lh = line.height;
7832
+ if (h < lh) break;
7833
+ h -= lh;
7834
+ }
7835
+ return n + i;
7836
+ }
7837
+
7838
+
7839
+ // Find the height above the given line.
7840
+ function heightAtLine(lineObj) {
7841
+ lineObj = visualLine(lineObj);
7842
+
7843
+ var h = 0, chunk = lineObj.parent;
7844
+ for (var i = 0; i < chunk.lines.length; ++i) {
7845
+ var line = chunk.lines[i];
7846
+ if (line == lineObj) break;
7847
+ else h += line.height;
7848
+ }
7849
+ for (var p = chunk.parent; p; chunk = p, p = chunk.parent) {
7850
+ for (var i = 0; i < p.children.length; ++i) {
7851
+ var cur = p.children[i];
7852
+ if (cur == chunk) break;
7853
+ else h += cur.height;
7854
+ }
7855
+ }
7856
+ return h;
7857
+ }
7858
+
7859
+ // Get the bidi ordering for the given line (and cache it). Returns
7860
+ // false for lines that are fully left-to-right, and an array of
7861
+ // BidiSpan objects otherwise.
7862
+ function getOrder(line) {
7863
+ var order = line.order;
7864
+ if (order == null) order = line.order = bidiOrdering(line.text);
7865
+ return order;
7866
+ }
7867
+
7868
+ // HISTORY
7869
+
7870
+ function History(startGen) {
7871
+ // Arrays of change events and selections. Doing something adds an
7872
+ // event to done and clears undo. Undoing moves events from done
7873
+ // to undone, redoing moves them in the other direction.
7874
+ this.done = []; this.undone = [];
7875
+ this.undoDepth = Infinity;
7876
+ // Used to track when changes can be merged into a single undo
7877
+ // event
7878
+ this.lastModTime = this.lastSelTime = 0;
7879
+ this.lastOp = this.lastSelOp = null;
7880
+ this.lastOrigin = this.lastSelOrigin = null;
7881
+ // Used by the isClean() method
7882
+ this.generation = this.maxGeneration = startGen || 1;
7883
+ }
7884
+
7885
+ // Create a history change event from an updateDoc-style change
7886
+ // object.
7887
+ function historyChangeFromChange(doc, change) {
7888
+ var histChange = {from: copyPos(change.from), to: changeEnd(change), text: getBetween(doc, change.from, change.to)};
7889
+ attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);
7890
+ linkedDocs(doc, function(doc) {attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);}, true);
7891
+ return histChange;
7892
+ }
7893
+
7894
+ // Pop all selection events off the end of a history array. Stop at
7895
+ // a change event.
7896
+ function clearSelectionEvents(array) {
7897
+ while (array.length) {
7898
+ var last = lst(array);
7899
+ if (last.ranges) array.pop();
7900
+ else break;
7901
+ }
7902
+ }
7903
+
7904
+ // Find the top change event in the history. Pop off selection
7905
+ // events that are in the way.
7906
+ function lastChangeEvent(hist, force) {
7907
+ if (force) {
7908
+ clearSelectionEvents(hist.done);
7909
+ return lst(hist.done);
7910
+ } else if (hist.done.length && !lst(hist.done).ranges) {
7911
+ return lst(hist.done);
7912
+ } else if (hist.done.length > 1 && !hist.done[hist.done.length - 2].ranges) {
7913
+ hist.done.pop();
7914
+ return lst(hist.done);
7915
+ }
7916
+ }
7917
+
7918
+ // Register a change in the history. Merges changes that are within
7919
+ // a single operation, ore are close together with an origin that
7920
+ // allows merging (starting with "+") into a single event.
7921
+ function addChangeToHistory(doc, change, selAfter, opId) {
7922
+ var hist = doc.history;
7923
+ hist.undone.length = 0;
7924
+ var time = +new Date, cur;
7925
+
7926
+ if ((hist.lastOp == opId ||
7927
+ hist.lastOrigin == change.origin && change.origin &&
7928
+ ((change.origin.charAt(0) == "+" && doc.cm && hist.lastModTime > time - doc.cm.options.historyEventDelay) ||
7929
+ change.origin.charAt(0) == "*")) &&
7930
+ (cur = lastChangeEvent(hist, hist.lastOp == opId))) {
7931
+ // Merge this change into the last event
7932
+ var last = lst(cur.changes);
7933
+ if (cmp(change.from, change.to) == 0 && cmp(change.from, last.to) == 0) {
7934
+ // Optimized case for simple insertion -- don't want to add
7935
+ // new changesets for every character typed
7936
+ last.to = changeEnd(change);
7937
+ } else {
7938
+ // Add new sub-event
7939
+ cur.changes.push(historyChangeFromChange(doc, change));
7940
+ }
7941
+ } else {
7942
+ // Can not be merged, start a new event.
7943
+ var before = lst(hist.done);
7944
+ if (!before || !before.ranges)
7945
+ pushSelectionToHistory(doc.sel, hist.done);
7946
+ cur = {changes: [historyChangeFromChange(doc, change)],
7947
+ generation: hist.generation};
7948
+ hist.done.push(cur);
7949
+ while (hist.done.length > hist.undoDepth) {
7950
+ hist.done.shift();
7951
+ if (!hist.done[0].ranges) hist.done.shift();
7952
+ }
7953
+ }
7954
+ hist.done.push(selAfter);
7955
+ hist.generation = ++hist.maxGeneration;
7956
+ hist.lastModTime = hist.lastSelTime = time;
7957
+ hist.lastOp = hist.lastSelOp = opId;
7958
+ hist.lastOrigin = hist.lastSelOrigin = change.origin;
7959
+
7960
+ if (!last) signal(doc, "historyAdded");
7961
+ }
7962
+
7963
+ function selectionEventCanBeMerged(doc, origin, prev, sel) {
7964
+ var ch = origin.charAt(0);
7965
+ return ch == "*" ||
7966
+ ch == "+" &&
7967
+ prev.ranges.length == sel.ranges.length &&
7968
+ prev.somethingSelected() == sel.somethingSelected() &&
7969
+ new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEventDelay : 500);
7970
+ }
7971
+
7972
+ // Called whenever the selection changes, sets the new selection as
7973
+ // the pending selection in the history, and pushes the old pending
7974
+ // selection into the 'done' array when it was significantly
7975
+ // different (in number of selected ranges, emptiness, or time).
7976
+ function addSelectionToHistory(doc, sel, opId, options) {
7977
+ var hist = doc.history, origin = options && options.origin;
7978
+
7979
+ // A new event is started when the previous origin does not match
7980
+ // the current, or the origins don't allow matching. Origins
7981
+ // starting with * are always merged, those starting with + are
7982
+ // merged when similar and close together in time.
7983
+ if (opId == hist.lastSelOp ||
7984
+ (origin && hist.lastSelOrigin == origin &&
7985
+ (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin ||
7986
+ selectionEventCanBeMerged(doc, origin, lst(hist.done), sel))))
7987
+ hist.done[hist.done.length - 1] = sel;
7988
+ else
7989
+ pushSelectionToHistory(sel, hist.done);
7990
+
7991
+ hist.lastSelTime = +new Date;
7992
+ hist.lastSelOrigin = origin;
7993
+ hist.lastSelOp = opId;
7994
+ if (options && options.clearRedo !== false)
7995
+ clearSelectionEvents(hist.undone);
7996
+ }
7997
+
7998
+ function pushSelectionToHistory(sel, dest) {
7999
+ var top = lst(dest);
8000
+ if (!(top && top.ranges && top.equals(sel)))
8001
+ dest.push(sel);
8002
+ }
8003
+
8004
+ // Used to store marked span information in the history.
8005
+ function attachLocalSpans(doc, change, from, to) {
8006
+ var existing = change["spans_" + doc.id], n = 0;
8007
+ doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function(line) {
8008
+ if (line.markedSpans)
8009
+ (existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans;
8010
+ ++n;
8011
+ });
8012
+ }
8013
+
8014
+ // When un/re-doing restores text containing marked spans, those
8015
+ // that have been explicitly cleared should not be restored.
8016
+ function removeClearedSpans(spans) {
8017
+ if (!spans) return null;
8018
+ for (var i = 0, out; i < spans.length; ++i) {
8019
+ if (spans[i].marker.explicitlyCleared) { if (!out) out = spans.slice(0, i); }
8020
+ else if (out) out.push(spans[i]);
8021
+ }
8022
+ return !out ? spans : out.length ? out : null;
8023
+ }
8024
+
8025
+ // Retrieve and filter the old marked spans stored in a change event.
8026
+ function getOldSpans(doc, change) {
8027
+ var found = change["spans_" + doc.id];
8028
+ if (!found) return null;
8029
+ for (var i = 0, nw = []; i < change.text.length; ++i)
8030
+ nw.push(removeClearedSpans(found[i]));
8031
+ return nw;
8032
+ }
8033
+
8034
+ // Used both to provide a JSON-safe object in .getHistory, and, when
8035
+ // detaching a document, to split the history in two
8036
+ function copyHistoryArray(events, newGroup, instantiateSel) {
8037
+ for (var i = 0, copy = []; i < events.length; ++i) {
8038
+ var event = events[i];
8039
+ if (event.ranges) {
8040
+ copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event);
8041
+ continue;
8042
+ }
8043
+ var changes = event.changes, newChanges = [];
8044
+ copy.push({changes: newChanges});
8045
+ for (var j = 0; j < changes.length; ++j) {
8046
+ var change = changes[j], m;
8047
+ newChanges.push({from: change.from, to: change.to, text: change.text});
8048
+ if (newGroup) for (var prop in change) if (m = prop.match(/^spans_(\d+)$/)) {
8049
+ if (indexOf(newGroup, Number(m[1])) > -1) {
8050
+ lst(newChanges)[prop] = change[prop];
8051
+ delete change[prop];
8052
+ }
8053
+ }
8054
+ }
8055
+ }
8056
+ return copy;
8057
+ }
8058
+
8059
+ // Rebasing/resetting history to deal with externally-sourced changes
8060
+
8061
+ function rebaseHistSelSingle(pos, from, to, diff) {
8062
+ if (to < pos.line) {
8063
+ pos.line += diff;
8064
+ } else if (from < pos.line) {
8065
+ pos.line = from;
8066
+ pos.ch = 0;
8067
+ }
8068
+ }
8069
+
8070
+ // Tries to rebase an array of history events given a change in the
8071
+ // document. If the change touches the same lines as the event, the
8072
+ // event, and everything 'behind' it, is discarded. If the change is
8073
+ // before the event, the event's positions are updated. Uses a
8074
+ // copy-on-write scheme for the positions, to avoid having to
8075
+ // reallocate them all on every rebase, but also avoid problems with
8076
+ // shared position objects being unsafely updated.
8077
+ function rebaseHistArray(array, from, to, diff) {
8078
+ for (var i = 0; i < array.length; ++i) {
8079
+ var sub = array[i], ok = true;
8080
+ if (sub.ranges) {
8081
+ if (!sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied = true; }
8082
+ for (var j = 0; j < sub.ranges.length; j++) {
8083
+ rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff);
8084
+ rebaseHistSelSingle(sub.ranges[j].head, from, to, diff);
8085
+ }
8086
+ continue;
8087
+ }
8088
+ for (var j = 0; j < sub.changes.length; ++j) {
8089
+ var cur = sub.changes[j];
8090
+ if (to < cur.from.line) {
8091
+ cur.from = Pos(cur.from.line + diff, cur.from.ch);
8092
+ cur.to = Pos(cur.to.line + diff, cur.to.ch);
8093
+ } else if (from <= cur.to.line) {
8094
+ ok = false;
8095
+ break;
8096
+ }
8097
+ }
8098
+ if (!ok) {
8099
+ array.splice(0, i + 1);
8100
+ i = 0;
8101
+ }
8102
+ }
8103
+ }
8104
+
8105
+ function rebaseHist(hist, change) {
8106
+ var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1;
8107
+ rebaseHistArray(hist.done, from, to, diff);
8108
+ rebaseHistArray(hist.undone, from, to, diff);
8109
+ }
8110
+
8111
+ // EVENT UTILITIES
8112
+
8113
+ // Due to the fact that we still support jurassic IE versions, some
8114
+ // compatibility wrappers are needed.
8115
+
8116
+ var e_preventDefault = CodeMirror.e_preventDefault = function(e) {
8117
+ if (e.preventDefault) e.preventDefault();
8118
+ else e.returnValue = false;
8119
+ };
8120
+ var e_stopPropagation = CodeMirror.e_stopPropagation = function(e) {
8121
+ if (e.stopPropagation) e.stopPropagation();
8122
+ else e.cancelBubble = true;
8123
+ };
8124
+ function e_defaultPrevented(e) {
8125
+ return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false;
8126
+ }
8127
+ var e_stop = CodeMirror.e_stop = function(e) {e_preventDefault(e); e_stopPropagation(e);};
8128
+
8129
+ function e_target(e) {return e.target || e.srcElement;}
8130
+ function e_button(e) {
8131
+ var b = e.which;
8132
+ if (b == null) {
8133
+ if (e.button & 1) b = 1;
8134
+ else if (e.button & 2) b = 3;
8135
+ else if (e.button & 4) b = 2;
8136
+ }
8137
+ if (mac && e.ctrlKey && b == 1) b = 3;
8138
+ return b;
8139
+ }
8140
+
8141
+ // EVENT HANDLING
8142
+
8143
+ // Lightweight event framework. on/off also work on DOM nodes,
8144
+ // registering native DOM handlers.
8145
+
8146
+ var on = CodeMirror.on = function(emitter, type, f) {
8147
+ if (emitter.addEventListener)
8148
+ emitter.addEventListener(type, f, false);
8149
+ else if (emitter.attachEvent)
8150
+ emitter.attachEvent("on" + type, f);
8151
+ else {
8152
+ var map = emitter._handlers || (emitter._handlers = {});
8153
+ var arr = map[type] || (map[type] = []);
8154
+ arr.push(f);
8155
+ }
8156
+ };
8157
+
8158
+ var noHandlers = []
8159
+ function getHandlers(emitter, type, copy) {
8160
+ var arr = emitter._handlers && emitter._handlers[type]
8161
+ if (copy) return arr && arr.length > 0 ? arr.slice() : noHandlers
8162
+ else return arr || noHandlers
8163
+ }
8164
+
8165
+ var off = CodeMirror.off = function(emitter, type, f) {
8166
+ if (emitter.removeEventListener)
8167
+ emitter.removeEventListener(type, f, false);
8168
+ else if (emitter.detachEvent)
8169
+ emitter.detachEvent("on" + type, f);
8170
+ else {
8171
+ var handlers = getHandlers(emitter, type, false)
8172
+ for (var i = 0; i < handlers.length; ++i)
8173
+ if (handlers[i] == f) { handlers.splice(i, 1); break; }
8174
+ }
8175
+ };
8176
+
8177
+ var signal = CodeMirror.signal = function(emitter, type /*, values...*/) {
8178
+ var handlers = getHandlers(emitter, type, true)
8179
+ if (!handlers.length) return;
8180
+ var args = Array.prototype.slice.call(arguments, 2);
8181
+ for (var i = 0; i < handlers.length; ++i) handlers[i].apply(null, args);
8182
+ };
8183
+
8184
+ var orphanDelayedCallbacks = null;
8185
+
8186
+ // Often, we want to signal events at a point where we are in the
8187
+ // middle of some work, but don't want the handler to start calling
8188
+ // other methods on the editor, which might be in an inconsistent
8189
+ // state or simply not expect any other events to happen.
8190
+ // signalLater looks whether there are any handlers, and schedules
8191
+ // them to be executed when the last operation ends, or, if no
8192
+ // operation is active, when a timeout fires.
8193
+ function signalLater(emitter, type /*, values...*/) {
8194
+ var arr = getHandlers(emitter, type, false)
8195
+ if (!arr.length) return;
8196
+ var args = Array.prototype.slice.call(arguments, 2), list;
8197
+ if (operationGroup) {
8198
+ list = operationGroup.delayedCallbacks;
8199
+ } else if (orphanDelayedCallbacks) {
8200
+ list = orphanDelayedCallbacks;
8201
+ } else {
8202
+ list = orphanDelayedCallbacks = [];
8203
+ setTimeout(fireOrphanDelayed, 0);
8204
+ }
8205
+ function bnd(f) {return function(){f.apply(null, args);};};
8206
+ for (var i = 0; i < arr.length; ++i)
8207
+ list.push(bnd(arr[i]));
8208
+ }
8209
+
8210
+ function fireOrphanDelayed() {
8211
+ var delayed = orphanDelayedCallbacks;
8212
+ orphanDelayedCallbacks = null;
8213
+ for (var i = 0; i < delayed.length; ++i) delayed[i]();
8214
+ }
8215
+
8216
+ // The DOM events that CodeMirror handles can be overridden by
8217
+ // registering a (non-DOM) handler on the editor for the event name,
8218
+ // and preventDefault-ing the event in that handler.
8219
+ function signalDOMEvent(cm, e, override) {
8220
+ if (typeof e == "string")
8221
+ e = {type: e, preventDefault: function() { this.defaultPrevented = true; }};
8222
+ signal(cm, override || e.type, cm, e);
8223
+ return e_defaultPrevented(e) || e.codemirrorIgnore;
8224
+ }
8225
+
8226
+ function signalCursorActivity(cm) {
8227
+ var arr = cm._handlers && cm._handlers.cursorActivity;
8228
+ if (!arr) return;
8229
+ var set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = []);
8230
+ for (var i = 0; i < arr.length; ++i) if (indexOf(set, arr[i]) == -1)
8231
+ set.push(arr[i]);
8232
+ }
8233
+
8234
+ function hasHandler(emitter, type) {
8235
+ return getHandlers(emitter, type).length > 0
8236
+ }
8237
+
8238
+ // Add on and off methods to a constructor's prototype, to make
8239
+ // registering events on such objects more convenient.
8240
+ function eventMixin(ctor) {
8241
+ ctor.prototype.on = function(type, f) {on(this, type, f);};
8242
+ ctor.prototype.off = function(type, f) {off(this, type, f);};
8243
+ }
8244
+
8245
+ // MISC UTILITIES
8246
+
8247
+ // Number of pixels added to scroller and sizer to hide scrollbar
8248
+ var scrollerGap = 30;
8249
+
8250
+ // Returned or thrown by various protocols to signal 'I'm not
8251
+ // handling this'.
8252
+ var Pass = CodeMirror.Pass = {toString: function(){return "CodeMirror.Pass";}};
8253
+
8254
+ // Reused option objects for setSelection & friends
8255
+ var sel_dontScroll = {scroll: false}, sel_mouse = {origin: "*mouse"}, sel_move = {origin: "+move"};
8256
+
8257
+ function Delayed() {this.id = null;}
8258
+ Delayed.prototype.set = function(ms, f) {
8259
+ clearTimeout(this.id);
8260
+ this.id = setTimeout(f, ms);
8261
+ };
8262
+
8263
+ // Counts the column offset in a string, taking tabs into account.
8264
+ // Used mostly to find indentation.
8265
+ var countColumn = CodeMirror.countColumn = function(string, end, tabSize, startIndex, startValue) {
8266
+ if (end == null) {
8267
+ end = string.search(/[^\s\u00a0]/);
8268
+ if (end == -1) end = string.length;
8269
+ }
8270
+ for (var i = startIndex || 0, n = startValue || 0;;) {
8271
+ var nextTab = string.indexOf("\t", i);
8272
+ if (nextTab < 0 || nextTab >= end)
8273
+ return n + (end - i);
8274
+ n += nextTab - i;
8275
+ n += tabSize - (n % tabSize);
8276
+ i = nextTab + 1;
8277
+ }
8278
+ };
8279
+
8280
+ // The inverse of countColumn -- find the offset that corresponds to
8281
+ // a particular column.
8282
+ var findColumn = CodeMirror.findColumn = function(string, goal, tabSize) {
8283
+ for (var pos = 0, col = 0;;) {
8284
+ var nextTab = string.indexOf("\t", pos);
8285
+ if (nextTab == -1) nextTab = string.length;
8286
+ var skipped = nextTab - pos;
8287
+ if (nextTab == string.length || col + skipped >= goal)
8288
+ return pos + Math.min(skipped, goal - col);
8289
+ col += nextTab - pos;
8290
+ col += tabSize - (col % tabSize);
8291
+ pos = nextTab + 1;
8292
+ if (col >= goal) return pos;
8293
+ }
8294
+ }
8295
+
8296
+ var spaceStrs = [""];
8297
+ function spaceStr(n) {
8298
+ while (spaceStrs.length <= n)
8299
+ spaceStrs.push(lst(spaceStrs) + " ");
8300
+ return spaceStrs[n];
8301
+ }
8302
+
8303
+ function lst(arr) { return arr[arr.length-1]; }
8304
+
8305
+ var selectInput = function(node) { node.select(); };
8306
+ if (ios) // Mobile Safari apparently has a bug where select() is broken.
8307
+ selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length; };
8308
+ else if (ie) // Suppress mysterious IE10 errors
8309
+ selectInput = function(node) { try { node.select(); } catch(_e) {} };
8310
+
8311
+ function indexOf(array, elt) {
8312
+ for (var i = 0; i < array.length; ++i)
8313
+ if (array[i] == elt) return i;
8314
+ return -1;
8315
+ }
8316
+ function map(array, f) {
8317
+ var out = [];
8318
+ for (var i = 0; i < array.length; i++) out[i] = f(array[i], i);
8319
+ return out;
8320
+ }
8321
+
8322
+ function nothing() {}
8323
+
8324
+ function createObj(base, props) {
8325
+ var inst;
8326
+ if (Object.create) {
8327
+ inst = Object.create(base);
8328
+ } else {
8329
+ nothing.prototype = base;
8330
+ inst = new nothing();
8331
+ }
8332
+ if (props) copyObj(props, inst);
8333
+ return inst;
8334
+ };
8335
+
8336
+ function copyObj(obj, target, overwrite) {
8337
+ if (!target) target = {};
8338
+ for (var prop in obj)
8339
+ if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop)))
8340
+ target[prop] = obj[prop];
8341
+ return target;
8342
+ }
8343
+
8344
+ function bind(f) {
8345
+ var args = Array.prototype.slice.call(arguments, 1);
8346
+ return function(){return f.apply(null, args);};
8347
+ }
8348
+
8349
+ var nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/;
8350
+ var isWordCharBasic = CodeMirror.isWordChar = function(ch) {
8351
+ return /\w/.test(ch) || ch > "\x80" &&
8352
+ (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch));
8353
+ };
8354
+ function isWordChar(ch, helper) {
8355
+ if (!helper) return isWordCharBasic(ch);
8356
+ if (helper.source.indexOf("\\w") > -1 && isWordCharBasic(ch)) return true;
8357
+ return helper.test(ch);
8358
+ }
8359
+
8360
+ function isEmpty(obj) {
8361
+ for (var n in obj) if (obj.hasOwnProperty(n) && obj[n]) return false;
8362
+ return true;
8363
+ }
8364
+
8365
+ // Extending unicode characters. A series of a non-extending char +
8366
+ // any number of extending chars is treated as a single unit as far
8367
+ // as editing and measuring is concerned. This is not fully correct,
8368
+ // since some scripts/fonts/browsers also treat other configurations
8369
+ // of code points as a group.
8370
+ var extendingChars = /[\u0300-\u036f\u0483-\u0489\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u065e\u0670\u06d6-\u06dc\u06de-\u06e4\u06e7\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0900-\u0902\u093c\u0941-\u0948\u094d\u0951-\u0955\u0962\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2\u09e3\u0a01\u0a02\u0a3c\u0a41\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a70\u0a71\u0a75\u0a81\u0a82\u0abc\u0ac1-\u0ac5\u0ac7\u0ac8\u0acd\u0ae2\u0ae3\u0b01\u0b3c\u0b3e\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b82\u0bbe\u0bc0\u0bcd\u0bd7\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0cbc\u0cbf\u0cc2\u0cc6\u0ccc\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0d3e\u0d41-\u0d44\u0d4d\u0d57\u0d62\u0d63\u0dca\u0dcf\u0dd2-\u0dd4\u0dd6\u0ddf\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86\u0f87\u0f90-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039\u103a\u103d\u103e\u1058\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085\u1086\u108d\u109d\u135f\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193b\u1a17\u1a18\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80\u1b81\u1ba2-\u1ba5\u1ba8\u1ba9\u1c2c-\u1c33\u1c36\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1dc0-\u1de6\u1dfd-\u1dff\u200c\u200d\u20d0-\u20f0\u2cef-\u2cf1\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua66f-\ua672\ua67c\ua67d\ua6f0\ua6f1\ua802\ua806\ua80b\ua825\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\uaa29-\uaa2e\uaa31\uaa32\uaa35\uaa36\uaa43\uaa4c\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uabe5\uabe8\uabed\udc00-\udfff\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\uff9e\uff9f]/;
8371
+ function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch); }
8372
+
8373
+ // DOM UTILITIES
8374
+
8375
+ function elt(tag, content, className, style) {
8376
+ var e = document.createElement(tag);
8377
+ if (className) e.className = className;
8378
+ if (style) e.style.cssText = style;
8379
+ if (typeof content == "string") e.appendChild(document.createTextNode(content));
8380
+ else if (content) for (var i = 0; i < content.length; ++i) e.appendChild(content[i]);
8381
+ return e;
8382
+ }
8383
+
8384
+ var range;
8385
+ if (document.createRange) range = function(node, start, end, endNode) {
8386
+ var r = document.createRange();
8387
+ r.setEnd(endNode || node, end);
8388
+ r.setStart(node, start);
8389
+ return r;
8390
+ };
8391
+ else range = function(node, start, end) {
8392
+ var r = document.body.createTextRange();
8393
+ try { r.moveToElementText(node.parentNode); }
8394
+ catch(e) { return r; }
8395
+ r.collapse(true);
8396
+ r.moveEnd("character", end);
8397
+ r.moveStart("character", start);
8398
+ return r;
8399
+ };
8400
+
8401
+ function removeChildren(e) {
8402
+ for (var count = e.childNodes.length; count > 0; --count)
8403
+ e.removeChild(e.firstChild);
8404
+ return e;
8405
+ }
8406
+
8407
+ function removeChildrenAndAdd(parent, e) {
8408
+ return removeChildren(parent).appendChild(e);
8409
+ }
8410
+
8411
+ var contains = CodeMirror.contains = function(parent, child) {
8412
+ if (child.nodeType == 3) // Android browser always returns false when child is a textnode
8413
+ child = child.parentNode;
8414
+ if (parent.contains)
8415
+ return parent.contains(child);
8416
+ do {
8417
+ if (child.nodeType == 11) child = child.host;
8418
+ if (child == parent) return true;
8419
+ } while (child = child.parentNode);
8420
+ };
8421
+
8422
+ function activeElt() {
8423
+ var activeElement = document.activeElement;
8424
+ while (activeElement && activeElement.root && activeElement.root.activeElement)
8425
+ activeElement = activeElement.root.activeElement;
8426
+ return activeElement;
8427
+ }
8428
+ // Older versions of IE throws unspecified error when touching
8429
+ // document.activeElement in some cases (during loading, in iframe)
8430
+ if (ie && ie_version < 11) activeElt = function() {
8431
+ try { return document.activeElement; }
8432
+ catch(e) { return document.body; }
8433
+ };
8434
+
8435
+ function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*"); }
8436
+ var rmClass = CodeMirror.rmClass = function(node, cls) {
8437
+ var current = node.className;
8438
+ var match = classTest(cls).exec(current);
8439
+ if (match) {
8440
+ var after = current.slice(match.index + match[0].length);
8441
+ node.className = current.slice(0, match.index) + (after ? match[1] + after : "");
8442
+ }
8443
+ };
8444
+ var addClass = CodeMirror.addClass = function(node, cls) {
8445
+ var current = node.className;
8446
+ if (!classTest(cls).test(current)) node.className += (current ? " " : "") + cls;
8447
+ };
8448
+ function joinClasses(a, b) {
8449
+ var as = a.split(" ");
8450
+ for (var i = 0; i < as.length; i++)
8451
+ if (as[i] && !classTest(as[i]).test(b)) b += " " + as[i];
8452
+ return b;
8453
+ }
8454
+
8455
+ // WINDOW-WIDE EVENTS
8456
+
8457
+ // These must be handled carefully, because naively registering a
8458
+ // handler for each editor will cause the editors to never be
8459
+ // garbage collected.
8460
+
8461
+ function forEachCodeMirror(f) {
8462
+ if (!document.body.getElementsByClassName) return;
8463
+ var byClass = document.body.getElementsByClassName("CodeMirror");
8464
+ for (var i = 0; i < byClass.length; i++) {
8465
+ var cm = byClass[i].CodeMirror;
8466
+ if (cm) f(cm);
8467
+ }
8468
+ }
8469
+
8470
+ var globalsRegistered = false;
8471
+ function ensureGlobalHandlers() {
8472
+ if (globalsRegistered) return;
8473
+ registerGlobalHandlers();
8474
+ globalsRegistered = true;
8475
+ }
8476
+ function registerGlobalHandlers() {
8477
+ // When the window resizes, we need to refresh active editors.
8478
+ var resizeTimer;
8479
+ on(window, "resize", function() {
8480
+ if (resizeTimer == null) resizeTimer = setTimeout(function() {
8481
+ resizeTimer = null;
8482
+ forEachCodeMirror(onResize);
8483
+ }, 100);
8484
+ });
8485
+ // When the window loses focus, we want to show the editor as blurred
8486
+ on(window, "blur", function() {
8487
+ forEachCodeMirror(onBlur);
8488
+ });
8489
+ }
8490
+
8491
+ // FEATURE DETECTION
8492
+
8493
+ // Detect drag-and-drop
8494
+ var dragAndDrop = function() {
8495
+ // There is *some* kind of drag-and-drop support in IE6-8, but I
8496
+ // couldn't get it to work yet.
8497
+ if (ie && ie_version < 9) return false;
8498
+ var div = elt('div');
8499
+ return "draggable" in div || "dragDrop" in div;
8500
+ }();
8501
+
8502
+ var zwspSupported;
8503
+ function zeroWidthElement(measure) {
8504
+ if (zwspSupported == null) {
8505
+ var test = elt("span", "\u200b");
8506
+ removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")]));
8507
+ if (measure.firstChild.offsetHeight != 0)
8508
+ zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie && ie_version < 8);
8509
+ }
8510
+ var node = zwspSupported ? elt("span", "\u200b") :
8511
+ elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px");
8512
+ node.setAttribute("cm-text", "");
8513
+ return node;
8514
+ }
8515
+
8516
+ // Feature-detect IE's crummy client rect reporting for bidi text
8517
+ var badBidiRects;
8518
+ function hasBadBidiRects(measure) {
8519
+ if (badBidiRects != null) return badBidiRects;
8520
+ var txt = removeChildrenAndAdd(measure, document.createTextNode("A\u062eA"));
8521
+ var r0 = range(txt, 0, 1).getBoundingClientRect();
8522
+ if (!r0 || r0.left == r0.right) return false; // Safari returns null in some cases (#2780)
8523
+ var r1 = range(txt, 1, 2).getBoundingClientRect();
8524
+ return badBidiRects = (r1.right - r0.right < 3);
8525
+ }
8526
+
8527
+ // See if "".split is the broken IE version, if so, provide an
8528
+ // alternative way to split lines.
8529
+ var splitLinesAuto = CodeMirror.splitLines = "\n\nb".split(/\n/).length != 3 ? function(string) {
8530
+ var pos = 0, result = [], l = string.length;
8531
+ while (pos <= l) {
8532
+ var nl = string.indexOf("\n", pos);
8533
+ if (nl == -1) nl = string.length;
8534
+ var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl);
8535
+ var rt = line.indexOf("\r");
8536
+ if (rt != -1) {
8537
+ result.push(line.slice(0, rt));
8538
+ pos += rt + 1;
8539
+ } else {
8540
+ result.push(line);
8541
+ pos = nl + 1;
8542
+ }
8543
+ }
8544
+ return result;
8545
+ } : function(string){return string.split(/\r\n?|\n/);};
8546
+
8547
+ var hasSelection = window.getSelection ? function(te) {
8548
+ try { return te.selectionStart != te.selectionEnd; }
8549
+ catch(e) { return false; }
8550
+ } : function(te) {
8551
+ try {var range = te.ownerDocument.selection.createRange();}
8552
+ catch(e) {}
8553
+ if (!range || range.parentElement() != te) return false;
8554
+ return range.compareEndPoints("StartToEnd", range) != 0;
8555
+ };
8556
+
8557
+ var hasCopyEvent = (function() {
8558
+ var e = elt("div");
8559
+ if ("oncopy" in e) return true;
8560
+ e.setAttribute("oncopy", "return;");
8561
+ return typeof e.oncopy == "function";
8562
+ })();
8563
+
8564
+ var badZoomedRects = null;
8565
+ function hasBadZoomedRects(measure) {
8566
+ if (badZoomedRects != null) return badZoomedRects;
8567
+ var node = removeChildrenAndAdd(measure, elt("span", "x"));
8568
+ var normal = node.getBoundingClientRect();
8569
+ var fromRange = range(node, 0, 1).getBoundingClientRect();
8570
+ return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1;
8571
+ }
8572
+
8573
+ // KEY NAMES
8574
+
8575
+ var keyNames = CodeMirror.keyNames = {
8576
+ 3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
8577
+ 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
8578
+ 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
8579
+ 46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod",
8580
+ 106: "*", 107: "=", 109: "-", 110: ".", 111: "/", 127: "Delete",
8581
+ 173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\",
8582
+ 221: "]", 222: "'", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete",
8583
+ 63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Insert"
8584
+ };
8585
+ (function() {
8586
+ // Number keys
8587
+ for (var i = 0; i < 10; i++) keyNames[i + 48] = keyNames[i + 96] = String(i);
8588
+ // Alphabetic keys
8589
+ for (var i = 65; i <= 90; i++) keyNames[i] = String.fromCharCode(i);
8590
+ // Function keys
8591
+ for (var i = 1; i <= 12; i++) keyNames[i + 111] = keyNames[i + 63235] = "F" + i;
8592
+ })();
8593
+
8594
+ // BIDI HELPERS
8595
+
8596
+ function iterateBidiSections(order, from, to, f) {
8597
+ if (!order) return f(from, to, "ltr");
8598
+ var found = false;
8599
+ for (var i = 0; i < order.length; ++i) {
8600
+ var part = order[i];
8601
+ if (part.from < to && part.to > from || from == to && part.to == from) {
8602
+ f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr");
8603
+ found = true;
8604
+ }
8605
+ }
8606
+ if (!found) f(from, to, "ltr");
8607
+ }
8608
+
8609
+ function bidiLeft(part) { return part.level % 2 ? part.to : part.from; }
8610
+ function bidiRight(part) { return part.level % 2 ? part.from : part.to; }
8611
+
8612
+ function lineLeft(line) { var order = getOrder(line); return order ? bidiLeft(order[0]) : 0; }
8613
+ function lineRight(line) {
8614
+ var order = getOrder(line);
8615
+ if (!order) return line.text.length;
8616
+ return bidiRight(lst(order));
8617
+ }
8618
+
8619
+ function lineStart(cm, lineN) {
8620
+ var line = getLine(cm.doc, lineN);
8621
+ var visual = visualLine(line);
8622
+ if (visual != line) lineN = lineNo(visual);
8623
+ var order = getOrder(visual);
8624
+ var ch = !order ? 0 : order[0].level % 2 ? lineRight(visual) : lineLeft(visual);
8625
+ return Pos(lineN, ch);
8626
+ }
8627
+ function lineEnd(cm, lineN) {
8628
+ var merged, line = getLine(cm.doc, lineN);
8629
+ while (merged = collapsedSpanAtEnd(line)) {
8630
+ line = merged.find(1, true).line;
8631
+ lineN = null;
8632
+ }
8633
+ var order = getOrder(line);
8634
+ var ch = !order ? line.text.length : order[0].level % 2 ? lineLeft(line) : lineRight(line);
8635
+ return Pos(lineN == null ? lineNo(line) : lineN, ch);
8636
+ }
8637
+ function lineStartSmart(cm, pos) {
8638
+ var start = lineStart(cm, pos.line);
8639
+ var line = getLine(cm.doc, start.line);
8640
+ var order = getOrder(line);
8641
+ if (!order || order[0].level == 0) {
8642
+ var firstNonWS = Math.max(0, line.text.search(/\S/));
8643
+ var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch;
8644
+ return Pos(start.line, inWS ? 0 : firstNonWS);
8645
+ }
8646
+ return start;
8647
+ }
8648
+
8649
+ function compareBidiLevel(order, a, b) {
8650
+ var linedir = order[0].level;
8651
+ if (a == linedir) return true;
8652
+ if (b == linedir) return false;
8653
+ return a < b;
8654
+ }
8655
+ var bidiOther;
8656
+ function getBidiPartAt(order, pos) {
8657
+ bidiOther = null;
8658
+ for (var i = 0, found; i < order.length; ++i) {
8659
+ var cur = order[i];
8660
+ if (cur.from < pos && cur.to > pos) return i;
8661
+ if ((cur.from == pos || cur.to == pos)) {
8662
+ if (found == null) {
8663
+ found = i;
8664
+ } else if (compareBidiLevel(order, cur.level, order[found].level)) {
8665
+ if (cur.from != cur.to) bidiOther = found;
8666
+ return i;
8667
+ } else {
8668
+ if (cur.from != cur.to) bidiOther = i;
8669
+ return found;
8670
+ }
8671
+ }
8672
+ }
8673
+ return found;
8674
+ }
8675
+
8676
+ function moveInLine(line, pos, dir, byUnit) {
8677
+ if (!byUnit) return pos + dir;
8678
+ do pos += dir;
8679
+ while (pos > 0 && isExtendingChar(line.text.charAt(pos)));
8680
+ return pos;
8681
+ }
8682
+
8683
+ // This is needed in order to move 'visually' through bi-directional
8684
+ // text -- i.e., pressing left should make the cursor go left, even
8685
+ // when in RTL text. The tricky part is the 'jumps', where RTL and
8686
+ // LTR text touch each other. This often requires the cursor offset
8687
+ // to move more than one unit, in order to visually move one unit.
8688
+ function moveVisually(line, start, dir, byUnit) {
8689
+ var bidi = getOrder(line);
8690
+ if (!bidi) return moveLogically(line, start, dir, byUnit);
8691
+ var pos = getBidiPartAt(bidi, start), part = bidi[pos];
8692
+ var target = moveInLine(line, start, part.level % 2 ? -dir : dir, byUnit);
8693
+
8694
+ for (;;) {
8695
+ if (target > part.from && target < part.to) return target;
8696
+ if (target == part.from || target == part.to) {
8697
+ if (getBidiPartAt(bidi, target) == pos) return target;
8698
+ part = bidi[pos += dir];
8699
+ return (dir > 0) == part.level % 2 ? part.to : part.from;
8700
+ } else {
8701
+ part = bidi[pos += dir];
8702
+ if (!part) return null;
8703
+ if ((dir > 0) == part.level % 2)
8704
+ target = moveInLine(line, part.to, -1, byUnit);
8705
+ else
8706
+ target = moveInLine(line, part.from, 1, byUnit);
8707
+ }
8708
+ }
8709
+ }
8710
+
8711
+ function moveLogically(line, start, dir, byUnit) {
8712
+ var target = start + dir;
8713
+ if (byUnit) while (target > 0 && isExtendingChar(line.text.charAt(target))) target += dir;
8714
+ return target < 0 || target > line.text.length ? null : target;
8715
+ }
8716
+
8717
+ // Bidirectional ordering algorithm
8718
+ // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm
8719
+ // that this (partially) implements.
8720
+
8721
+ // One-char codes used for character types:
8722
+ // L (L): Left-to-Right
8723
+ // R (R): Right-to-Left
8724
+ // r (AL): Right-to-Left Arabic
8725
+ // 1 (EN): European Number
8726
+ // + (ES): European Number Separator
8727
+ // % (ET): European Number Terminator
8728
+ // n (AN): Arabic Number
8729
+ // , (CS): Common Number Separator
8730
+ // m (NSM): Non-Spacing Mark
8731
+ // b (BN): Boundary Neutral
8732
+ // s (B): Paragraph Separator
8733
+ // t (S): Segment Separator
8734
+ // w (WS): Whitespace
8735
+ // N (ON): Other Neutrals
8736
+
8737
+ // Returns null if characters are ordered as they appear
8738
+ // (left-to-right), or an array of sections ({from, to, level}
8739
+ // objects) in the order in which they occur visually.
8740
+ var bidiOrdering = (function() {
8741
+ // Character types for codepoints 0 to 0xff
8742
+ var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN";
8743
+ // Character types for codepoints 0x600 to 0x6ff
8744
+ var arabicTypes = "rrrrrrrrrrrr,rNNmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmrrrrrrrnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmNmmmm";
8745
+ function charType(code) {
8746
+ if (code <= 0xf7) return lowTypes.charAt(code);
8747
+ else if (0x590 <= code && code <= 0x5f4) return "R";
8748
+ else if (0x600 <= code && code <= 0x6ed) return arabicTypes.charAt(code - 0x600);
8749
+ else if (0x6ee <= code && code <= 0x8ac) return "r";
8750
+ else if (0x2000 <= code && code <= 0x200b) return "w";
8751
+ else if (code == 0x200c) return "b";
8752
+ else return "L";
8753
+ }
8754
+
8755
+ var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;
8756
+ var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/;
8757
+ // Browsers seem to always treat the boundaries of block elements as being L.
8758
+ var outerType = "L";
8759
+
8760
+ function BidiSpan(level, from, to) {
8761
+ this.level = level;
8762
+ this.from = from; this.to = to;
8763
+ }
8764
+
8765
+ return function(str) {
8766
+ if (!bidiRE.test(str)) return false;
8767
+ var len = str.length, types = [];
8768
+ for (var i = 0, type; i < len; ++i)
8769
+ types.push(type = charType(str.charCodeAt(i)));
8770
+
8771
+ // W1. Examine each non-spacing mark (NSM) in the level run, and
8772
+ // change the type of the NSM to the type of the previous
8773
+ // character. If the NSM is at the start of the level run, it will
8774
+ // get the type of sor.
8775
+ for (var i = 0, prev = outerType; i < len; ++i) {
8776
+ var type = types[i];
8777
+ if (type == "m") types[i] = prev;
8778
+ else prev = type;
8779
+ }
8780
+
8781
+ // W2. Search backwards from each instance of a European number
8782
+ // until the first strong type (R, L, AL, or sor) is found. If an
8783
+ // AL is found, change the type of the European number to Arabic
8784
+ // number.
8785
+ // W3. Change all ALs to R.
8786
+ for (var i = 0, cur = outerType; i < len; ++i) {
8787
+ var type = types[i];
8788
+ if (type == "1" && cur == "r") types[i] = "n";
8789
+ else if (isStrong.test(type)) { cur = type; if (type == "r") types[i] = "R"; }
8790
+ }
8791
+
8792
+ // W4. A single European separator between two European numbers
8793
+ // changes to a European number. A single common separator between
8794
+ // two numbers of the same type changes to that type.
8795
+ for (var i = 1, prev = types[0]; i < len - 1; ++i) {
8796
+ var type = types[i];
8797
+ if (type == "+" && prev == "1" && types[i+1] == "1") types[i] = "1";
8798
+ else if (type == "," && prev == types[i+1] &&
8799
+ (prev == "1" || prev == "n")) types[i] = prev;
8800
+ prev = type;
8801
+ }
8802
+
8803
+ // W5. A sequence of European terminators adjacent to European
8804
+ // numbers changes to all European numbers.
8805
+ // W6. Otherwise, separators and terminators change to Other
8806
+ // Neutral.
8807
+ for (var i = 0; i < len; ++i) {
8808
+ var type = types[i];
8809
+ if (type == ",") types[i] = "N";
8810
+ else if (type == "%") {
8811
+ for (var end = i + 1; end < len && types[end] == "%"; ++end) {}
8812
+ var replace = (i && types[i-1] == "!") || (end < len && types[end] == "1") ? "1" : "N";
8813
+ for (var j = i; j < end; ++j) types[j] = replace;
8814
+ i = end - 1;
8815
+ }
8816
+ }
8817
+
8818
+ // W7. Search backwards from each instance of a European number
8819
+ // until the first strong type (R, L, or sor) is found. If an L is
8820
+ // found, then change the type of the European number to L.
8821
+ for (var i = 0, cur = outerType; i < len; ++i) {
8822
+ var type = types[i];
8823
+ if (cur == "L" && type == "1") types[i] = "L";
8824
+ else if (isStrong.test(type)) cur = type;
8825
+ }
8826
+
8827
+ // N1. A sequence of neutrals takes the direction of the
8828
+ // surrounding strong text if the text on both sides has the same
8829
+ // direction. European and Arabic numbers act as if they were R in
8830
+ // terms of their influence on neutrals. Start-of-level-run (sor)
8831
+ // and end-of-level-run (eor) are used at level run boundaries.
8832
+ // N2. Any remaining neutrals take the embedding direction.
8833
+ for (var i = 0; i < len; ++i) {
8834
+ if (isNeutral.test(types[i])) {
8835
+ for (var end = i + 1; end < len && isNeutral.test(types[end]); ++end) {}
8836
+ var before = (i ? types[i-1] : outerType) == "L";
8837
+ var after = (end < len ? types[end] : outerType) == "L";
8838
+ var replace = before || after ? "L" : "R";
8839
+ for (var j = i; j < end; ++j) types[j] = replace;
8840
+ i = end - 1;
8841
+ }
8842
+ }
8843
+
8844
+ // Here we depart from the documented algorithm, in order to avoid
8845
+ // building up an actual levels array. Since there are only three
8846
+ // levels (0, 1, 2) in an implementation that doesn't take
8847
+ // explicit embedding into account, we can build up the order on
8848
+ // the fly, without following the level-based algorithm.
8849
+ var order = [], m;
8850
+ for (var i = 0; i < len;) {
8851
+ if (countsAsLeft.test(types[i])) {
8852
+ var start = i;
8853
+ for (++i; i < len && countsAsLeft.test(types[i]); ++i) {}
8854
+ order.push(new BidiSpan(0, start, i));
8855
+ } else {
8856
+ var pos = i, at = order.length;
8857
+ for (++i; i < len && types[i] != "L"; ++i) {}
8858
+ for (var j = pos; j < i;) {
8859
+ if (countsAsNum.test(types[j])) {
8860
+ if (pos < j) order.splice(at, 0, new BidiSpan(1, pos, j));
8861
+ var nstart = j;
8862
+ for (++j; j < i && countsAsNum.test(types[j]); ++j) {}
8863
+ order.splice(at, 0, new BidiSpan(2, nstart, j));
8864
+ pos = j;
8865
+ } else ++j;
8866
+ }
8867
+ if (pos < i) order.splice(at, 0, new BidiSpan(1, pos, i));
8868
+ }
8869
+ }
8870
+ if (order[0].level == 1 && (m = str.match(/^\s+/))) {
8871
+ order[0].from = m[0].length;
8872
+ order.unshift(new BidiSpan(0, 0, m[0].length));
8873
+ }
8874
+ if (lst(order).level == 1 && (m = str.match(/\s+$/))) {
8875
+ lst(order).to -= m[0].length;
8876
+ order.push(new BidiSpan(0, len - m[0].length, len));
8877
+ }
8878
+ if (order[0].level == 2)
8879
+ order.unshift(new BidiSpan(1, order[0].to, order[0].to));
8880
+ if (order[0].level != lst(order).level)
8881
+ order.push(new BidiSpan(order[0].level, len, len));
8882
+
8883
+ return order;
8884
+ };
8885
+ })();
8886
+
8887
+ // THE END
8888
+
8889
+ CodeMirror.version = "5.12.0";
8890
+
8891
+ return CodeMirror;
8892
+ });
codemirror/mode/clike/clike.js ADDED
@@ -0,0 +1,781 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // CodeMirror, copyright (c) by Marijn Haverbeke and others
2
+ // Distributed under an MIT license: http://codemirror.net/LICENSE
3
+
4
+ (function(mod) {
5
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
6
+ mod(require("../../lib/codemirror"));
7
+ else if (typeof define == "function" && define.amd) // AMD
8
+ define(["../../lib/codemirror"], mod);
9
+ else // Plain browser env
10
+ mod(CodeMirror);
11
+ })(function(CodeMirror) {
12
+ "use strict";
13
+
14
+ function Context(indented, column, type, align, prev) {
15
+ this.indented = indented;
16
+ this.column = column;
17
+ this.type = type;
18
+ this.align = align;
19
+ this.prev = prev;
20
+ }
21
+ function isStatement(type) {
22
+ return type == "statement" || type == "switchstatement" || type == "namespace";
23
+ }
24
+ function pushContext(state, col, type) {
25
+ var indent = state.indented;
26
+ if (state.context && isStatement(state.context.type) && !isStatement(type))
27
+ indent = state.context.indented;
28
+ return state.context = new Context(indent, col, type, null, state.context);
29
+ }
30
+ function popContext(state) {
31
+ var t = state.context.type;
32
+ if (t == ")" || t == "]" || t == "}")
33
+ state.indented = state.context.indented;
34
+ return state.context = state.context.prev;
35
+ }
36
+
37
+ function typeBefore(stream, state) {
38
+ if (state.prevToken == "variable" || state.prevToken == "variable-3") return true;
39
+ if (/\S(?:[^- ]>|[*\]])\s*$|\*$/.test(stream.string.slice(0, stream.start))) return true;
40
+ }
41
+
42
+ function isTopScope(context) {
43
+ for (;;) {
44
+ if (!context || context.type == "top") return true;
45
+ if (context.type == "}" && context.prev.type != "namespace") return false;
46
+ context = context.prev;
47
+ }
48
+ }
49
+
50
+ CodeMirror.defineMode("clike", function(config, parserConfig) {
51
+ var indentUnit = config.indentUnit,
52
+ statementIndentUnit = parserConfig.statementIndentUnit || indentUnit,
53
+ dontAlignCalls = parserConfig.dontAlignCalls,
54
+ keywords = parserConfig.keywords || {},
55
+ types = parserConfig.types || {},
56
+ builtin = parserConfig.builtin || {},
57
+ blockKeywords = parserConfig.blockKeywords || {},
58
+ defKeywords = parserConfig.defKeywords || {},
59
+ atoms = parserConfig.atoms || {},
60
+ hooks = parserConfig.hooks || {},
61
+ multiLineStrings = parserConfig.multiLineStrings,
62
+ indentStatements = parserConfig.indentStatements !== false,
63
+ indentSwitch = parserConfig.indentSwitch !== false,
64
+ namespaceSeparator = parserConfig.namespaceSeparator,
65
+ isPunctuationChar = parserConfig.isPunctuationChar || /[\[\]{}\(\),;\:\.]/,
66
+ numberStart = parserConfig.numberStart || /[\d\.]/,
67
+ number = parserConfig.number || /^(?:0x[a-f\d]+|0b[01]+|(?:\d+\.?\d*|\.\d+)(?:e[-+]?\d+)?)(u|ll?|l|f)?/i,
68
+ isOperatorChar = parserConfig.isOperatorChar || /[+\-*&%=<>!?|\/]/,
69
+ endStatement = parserConfig.endStatement || /^[;:,]$/;
70
+
71
+ var curPunc, isDefKeyword;
72
+
73
+ function tokenBase(stream, state) {
74
+ var ch = stream.next();
75
+ if (hooks[ch]) {
76
+ var result = hooks[ch](stream, state);
77
+ if (result !== false) return result;
78
+ }
79
+ if (ch == '"' || ch == "'") {
80
+ state.tokenize = tokenString(ch);
81
+ return state.tokenize(stream, state);
82
+ }
83
+ if (isPunctuationChar.test(ch)) {
84
+ curPunc = ch;
85
+ return null;
86
+ }
87
+ if (numberStart.test(ch)) {
88
+ stream.backUp(1)
89
+ if (stream.match(number)) return "number"
90
+ stream.next()
91
+ }
92
+ if (ch == "/") {
93
+ if (stream.eat("*")) {
94
+ state.tokenize = tokenComment;
95
+ return tokenComment(stream, state);
96
+ }
97
+ if (stream.eat("/")) {
98
+ stream.skipToEnd();
99
+ return "comment";
100
+ }
101
+ }
102
+ if (isOperatorChar.test(ch)) {
103
+ stream.eatWhile(isOperatorChar);
104
+ return "operator";
105
+ }
106
+ stream.eatWhile(/[\w\$_\xa1-\uffff]/);
107
+ if (namespaceSeparator) while (stream.match(namespaceSeparator))
108
+ stream.eatWhile(/[\w\$_\xa1-\uffff]/);
109
+
110
+ var cur = stream.current();
111
+ if (contains(keywords, cur)) {
112
+ if (contains(blockKeywords, cur)) curPunc = "newstatement";
113
+ if (contains(defKeywords, cur)) isDefKeyword = true;
114
+ return "keyword";
115
+ }
116
+ if (contains(types, cur)) return "variable-3";
117
+ if (contains(builtin, cur)) {
118
+ if (contains(blockKeywords, cur)) curPunc = "newstatement";
119
+ return "builtin";
120
+ }
121
+ if (contains(atoms, cur)) return "atom";
122
+ return "variable";
123
+ }
124
+
125
+ function tokenString(quote) {
126
+ return function(stream, state) {
127
+ var escaped = false, next, end = false;
128
+ while ((next = stream.next()) != null) {
129
+ if (next == quote && !escaped) {end = true; break;}
130
+ escaped = !escaped && next == "\\";
131
+ }
132
+ if (end || !(escaped || multiLineStrings))
133
+ state.tokenize = null;
134
+ return "string";
135
+ };
136
+ }
137
+
138
+ function tokenComment(stream, state) {
139
+ var maybeEnd = false, ch;
140
+ while (ch = stream.next()) {
141
+ if (ch == "/" && maybeEnd) {
142
+ state.tokenize = null;
143
+ break;
144
+ }
145
+ maybeEnd = (ch == "*");
146
+ }
147
+ return "comment";
148
+ }
149
+
150
+ // Interface
151
+
152
+ return {
153
+ startState: function(basecolumn) {
154
+ return {
155
+ tokenize: null,
156
+ context: new Context((basecolumn || 0) - indentUnit, 0, "top", false),
157
+ indented: 0,
158
+ startOfLine: true,
159
+ prevToken: null
160
+ };
161
+ },
162
+
163
+ token: function(stream, state) {
164
+ var ctx = state.context;
165
+ if (stream.sol()) {
166
+ if (ctx.align == null) ctx.align = false;
167
+ state.indented = stream.indentation();
168
+ state.startOfLine = true;
169
+ }
170
+ if (stream.eatSpace()) return null;
171
+ curPunc = isDefKeyword = null;
172
+ var style = (state.tokenize || tokenBase)(stream, state);
173
+ if (style == "comment" || style == "meta") return style;
174
+ if (ctx.align == null) ctx.align = true;
175
+
176
+ if (endStatement.test(curPunc)) while (isStatement(state.context.type)) popContext(state);
177
+ else if (curPunc == "{") pushContext(state, stream.column(), "}");
178
+ else if (curPunc == "[") pushContext(state, stream.column(), "]");
179
+ else if (curPunc == "(") pushContext(state, stream.column(), ")");
180
+ else if (curPunc == "}") {
181
+ while (isStatement(ctx.type)) ctx = popContext(state);
182
+ if (ctx.type == "}") ctx = popContext(state);
183
+ while (isStatement(ctx.type)) ctx = popContext(state);
184
+ }
185
+ else if (curPunc == ctx.type) popContext(state);
186
+ else if (indentStatements &&
187
+ (((ctx.type == "}" || ctx.type == "top") && curPunc != ";") ||
188
+ (isStatement(ctx.type) && curPunc == "newstatement"))) {
189
+ var type = "statement";
190
+ if (curPunc == "newstatement" && indentSwitch && stream.current() == "switch")
191
+ type = "switchstatement";
192
+ else if (style == "keyword" && stream.current() == "namespace")
193
+ type = "namespace";
194
+ pushContext(state, stream.column(), type);
195
+ }
196
+
197
+ if (style == "variable" &&
198
+ ((state.prevToken == "def" ||
199
+ (parserConfig.typeFirstDefinitions && typeBefore(stream, state) &&
200
+ isTopScope(state.context) && stream.match(/^\s*\(/, false)))))
201
+ style = "def";
202
+
203
+ if (hooks.token) {
204
+ var result = hooks.token(stream, state, style);
205
+ if (result !== undefined) style = result;
206
+ }
207
+
208
+ if (style == "def" && parserConfig.styleDefs === false) style = "variable";
209
+
210
+ state.startOfLine = false;
211
+ state.prevToken = isDefKeyword ? "def" : style || curPunc;
212
+ return style;
213
+ },
214
+
215
+ indent: function(state, textAfter) {
216
+ if (state.tokenize != tokenBase && state.tokenize != null) return CodeMirror.Pass;
217
+ var ctx = state.context, firstChar = textAfter && textAfter.charAt(0);
218
+ if (isStatement(ctx.type) && firstChar == "}") ctx = ctx.prev;
219
+ if (hooks.indent) {
220
+ var hook = hooks.indent(state, ctx, textAfter);
221
+ if (typeof hook == "number") return hook
222
+ }
223
+ var closing = firstChar == ctx.type;
224
+ var switchBlock = ctx.prev && ctx.prev.type == "switchstatement";
225
+ if (parserConfig.allmanIndentation && /[{(]/.test(firstChar)) {
226
+ while (ctx.type != "top" && ctx.type != "}") ctx = ctx.prev
227
+ return ctx.indented
228
+ }
229
+ if (isStatement(ctx.type))
230
+ return ctx.indented + (firstChar == "{" ? 0 : statementIndentUnit);
231
+ if (ctx.align && (!dontAlignCalls || ctx.type != ")"))
232
+ return ctx.column + (closing ? 0 : 1);
233
+ if (ctx.type == ")" && !closing)
234
+ return ctx.indented + statementIndentUnit;
235
+
236
+ return ctx.indented + (closing ? 0 : indentUnit) +
237
+ (!closing && switchBlock && !/^(?:case|default)\b/.test(textAfter) ? indentUnit : 0);
238
+ },
239
+
240
+ electricInput: indentSwitch ? /^\s*(?:case .*?:|default:|\{\}?|\})$/ : /^\s*[{}]$/,
241
+ blockCommentStart: "/*",
242
+ blockCommentEnd: "*/",
243
+ lineComment: "//",
244
+ fold: "brace"
245
+ };
246
+ });
247
+
248
+ function words(str) {
249
+ var obj = {}, words = str.split(" ");
250
+ for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
251
+ return obj;
252
+ }
253
+ function contains(words, word) {
254
+ if (typeof words === "function") {
255
+ return words(word);
256
+ } else {
257
+ return words.propertyIsEnumerable(word);
258
+ }
259
+ }
260
+ var cKeywords = "auto if break case register continue return default do sizeof " +
261
+ "static else struct switch extern typedef union for goto while enum const volatile";
262
+ var cTypes = "int long char short double float unsigned signed void size_t ptrdiff_t";
263
+
264
+ function cppHook(stream, state) {
265
+ if (!state.startOfLine) return false
266
+ for (var ch, next = null; ch = stream.peek();) {
267
+ if (ch == "\\" && stream.match(/^.$/)) {
268
+ next = cppHook
269
+ break
270
+ } else if (ch == "/" && stream.match(/^\/[\/\*]/, false)) {
271
+ break
272
+ }
273
+ stream.next()
274
+ }
275
+ state.tokenize = next
276
+ return "meta"
277
+ }
278
+
279
+ function pointerHook(_stream, state) {
280
+ if (state.prevToken == "variable-3") return "variable-3";
281
+ return false;
282
+ }
283
+
284
+ function cpp14Literal(stream) {
285
+ stream.eatWhile(/[\w\.']/);
286
+ return "number";
287
+ }
288
+
289
+ function cpp11StringHook(stream, state) {
290
+ stream.backUp(1);
291
+ // Raw strings.
292
+ if (stream.match(/(R|u8R|uR|UR|LR)/)) {
293
+ var match = stream.match(/"([^\s\\()]{0,16})\(/);
294
+ if (!match) {
295
+ return false;
296
+ }
297
+ state.cpp11RawStringDelim = match[1];
298
+ state.tokenize = tokenRawString;
299
+ return tokenRawString(stream, state);
300
+ }
301
+ // Unicode strings/chars.
302
+ if (stream.match(/(u8|u|U|L)/)) {
303
+ if (stream.match(/["']/, /* eat */ false)) {
304
+ return "string";
305
+ }
306
+ return false;
307
+ }
308
+ // Ignore this hook.
309
+ stream.next();
310
+ return false;
311
+ }
312
+
313
+ function cppLooksLikeConstructor(word) {
314
+ var lastTwo = /(\w+)::(\w+)$/.exec(word);
315
+ return lastTwo && lastTwo[1] == lastTwo[2];
316
+ }
317
+
318
+ // C#-style strings where "" escapes a quote.
319
+ function tokenAtString(stream, state) {
320
+ var next;
321
+ while ((next = stream.next()) != null) {
322
+ if (next == '"' && !stream.eat('"')) {
323
+ state.tokenize = null;
324
+ break;
325
+ }
326
+ }
327
+ return "string";
328
+ }
329
+
330
+ // C++11 raw string literal is <prefix>"<delim>( anything )<delim>", where
331
+ // <delim> can be a string up to 16 characters long.
332
+ function tokenRawString(stream, state) {
333
+ // Escape characters that have special regex meanings.
334
+ var delim = state.cpp11RawStringDelim.replace(/[^\w\s]/g, '\\$&');
335
+ var match = stream.match(new RegExp(".*?\\)" + delim + '"'));
336
+ if (match)
337
+ state.tokenize = null;
338
+ else
339
+ stream.skipToEnd();
340
+ return "string";
341
+ }
342
+
343
+ function def(mimes, mode) {
344
+ if (typeof mimes == "string") mimes = [mimes];
345
+ var words = [];
346
+ function add(obj) {
347
+ if (obj) for (var prop in obj) if (obj.hasOwnProperty(prop))
348
+ words.push(prop);
349
+ }
350
+ add(mode.keywords);
351
+ add(mode.types);
352
+ add(mode.builtin);
353
+ add(mode.atoms);
354
+ if (words.length) {
355
+ mode.helperType = mimes[0];
356
+ CodeMirror.registerHelper("hintWords", mimes[0], words);
357
+ }
358
+
359
+ for (var i = 0; i < mimes.length; ++i)
360
+ CodeMirror.defineMIME(mimes[i], mode);
361
+ }
362
+
363
+ def(["text/x-csrc", "text/x-c", "text/x-chdr"], {
364
+ name: "clike",
365
+ keywords: words(cKeywords),
366
+ types: words(cTypes + " bool _Complex _Bool float_t double_t intptr_t intmax_t " +
367
+ "int8_t int16_t int32_t int64_t uintptr_t uintmax_t uint8_t uint16_t " +
368
+ "uint32_t uint64_t"),
369
+ blockKeywords: words("case do else for if switch while struct"),
370
+ defKeywords: words("struct"),
371
+ typeFirstDefinitions: true,
372
+ atoms: words("null true false"),
373
+ hooks: {"#": cppHook, "*": pointerHook},
374
+ modeProps: {fold: ["brace", "include"]}
375
+ });
376
+
377
+ def(["text/x-c++src", "text/x-c++hdr"], {
378
+ name: "clike",
379
+ keywords: words(cKeywords + " asm dynamic_cast namespace reinterpret_cast try explicit new " +
380
+ "static_cast typeid catch operator template typename class friend private " +
381
+ "this using const_cast inline public throw virtual delete mutable protected " +
382
+ "alignas alignof constexpr decltype nullptr noexcept thread_local final " +
383
+ "static_assert override"),
384
+ types: words(cTypes + " bool wchar_t"),
385
+ blockKeywords: words("catch class do else finally for if struct switch try while"),
386
+ defKeywords: words("class namespace struct enum union"),
387
+ typeFirstDefinitions: true,
388
+ atoms: words("true false null"),
389
+ hooks: {
390
+ "#": cppHook,
391
+ "*": pointerHook,
392
+ "u": cpp11StringHook,
393
+ "U": cpp11StringHook,
394
+ "L": cpp11StringHook,
395
+ "R": cpp11StringHook,
396
+ "0": cpp14Literal,
397
+ "1": cpp14Literal,
398
+ "2": cpp14Literal,
399
+ "3": cpp14Literal,
400
+ "4": cpp14Literal,
401
+ "5": cpp14Literal,
402
+ "6": cpp14Literal,
403
+ "7": cpp14Literal,
404
+ "8": cpp14Literal,
405
+ "9": cpp14Literal,
406
+ token: function(stream, state, style) {
407
+ if (style == "variable" && stream.peek() == "(" &&
408
+ (state.prevToken == ";" || state.prevToken == null ||
409
+ state.prevToken == "}") &&
410
+ cppLooksLikeConstructor(stream.current()))
411
+ return "def";
412
+ }
413
+ },
414
+ namespaceSeparator: "::",
415
+ modeProps: {fold: ["brace", "include"]}
416
+ });
417
+
418
+ def("text/x-java", {
419
+ name: "clike",
420
+ keywords: words("abstract assert break case catch class const continue default " +
421
+ "do else enum extends final finally float for goto if implements import " +
422
+ "instanceof interface native new package private protected public " +
423
+ "return static strictfp super switch synchronized this throw throws transient " +
424
+ "try volatile while"),
425
+ types: words("byte short int long float double boolean char void Boolean Byte Character Double Float " +
426
+ "Integer Long Number Object Short String StringBuffer StringBuilder Void"),
427
+ blockKeywords: words("catch class do else finally for if switch try while"),
428
+ defKeywords: words("class interface package enum"),
429
+ typeFirstDefinitions: true,
430
+ atoms: words("true false null"),
431
+ endStatement: /^[;:]$/,
432
+ hooks: {
433
+ "@": function(stream) {
434
+ stream.eatWhile(/[\w\$_]/);
435
+ return "meta";
436
+ }
437
+ },
438
+ modeProps: {fold: ["brace", "import"]}
439
+ });
440
+
441
+ def("text/x-csharp", {
442
+ name: "clike",
443
+ keywords: words("abstract as async await base break case catch checked class const continue" +
444
+ " default delegate do else enum event explicit extern finally fixed for" +
445
+ " foreach goto if implicit in interface internal is lock namespace new" +
446
+ " operator out override params private protected public readonly ref return sealed" +
447
+ " sizeof stackalloc static struct switch this throw try typeof unchecked" +
448
+ " unsafe using virtual void volatile while add alias ascending descending dynamic from get" +
449
+ " global group into join let orderby partial remove select set value var yield"),
450
+ types: words("Action Boolean Byte Char DateTime DateTimeOffset Decimal Double Func" +
451
+ " Guid Int16 Int32 Int64 Object SByte Single String Task TimeSpan UInt16 UInt32" +
452
+ " UInt64 bool byte char decimal double short int long object" +
453
+ " sbyte float string ushort uint ulong"),
454
+ blockKeywords: words("catch class do else finally for foreach if struct switch try while"),
455
+ defKeywords: words("class interface namespace struct var"),
456
+ typeFirstDefinitions: true,
457
+ atoms: words("true false null"),
458
+ hooks: {
459
+ "@": function(stream, state) {
460
+ if (stream.eat('"')) {
461
+ state.tokenize = tokenAtString;
462
+ return tokenAtString(stream, state);
463
+ }
464
+ stream.eatWhile(/[\w\$_]/);
465
+ return "meta";
466
+ }
467
+ }
468
+ });
469
+
470
+ function tokenTripleString(stream, state) {
471
+ var escaped = false;
472
+ while (!stream.eol()) {
473
+ if (!escaped && stream.match('"""')) {
474
+ state.tokenize = null;
475
+ break;
476
+ }
477
+ escaped = stream.next() == "\\" && !escaped;
478
+ }
479
+ return "string";
480
+ }
481
+
482
+ def("text/x-scala", {
483
+ name: "clike",
484
+ keywords: words(
485
+
486
+ /* scala */
487
+ "abstract case catch class def do else extends final finally for forSome if " +
488
+ "implicit import lazy match new null object override package private protected return " +
489
+ "sealed super this throw trait try type val var while with yield _ : = => <- <: " +
490
+ "<% >: # @ " +
491
+
492
+ /* package scala */
493
+ "assert assume require print println printf readLine readBoolean readByte readShort " +
494
+ "readChar readInt readLong readFloat readDouble " +
495
+
496
+ ":: #:: "
497
+ ),
498
+ types: words(
499
+ "AnyVal App Application Array BufferedIterator BigDecimal BigInt Char Console Either " +
500
+ "Enumeration Equiv Error Exception Fractional Function IndexedSeq Int Integral Iterable " +
501
+ "Iterator List Map Numeric Nil NotNull Option Ordered Ordering PartialFunction PartialOrdering " +
502
+ "Product Proxy Range Responder Seq Serializable Set Specializable Stream StringBuilder " +
503
+ "StringContext Symbol Throwable Traversable TraversableOnce Tuple Unit Vector " +
504
+
505
+ /* package java.lang */
506
+ "Boolean Byte Character CharSequence Class ClassLoader Cloneable Comparable " +
507
+ "Compiler Double Exception Float Integer Long Math Number Object Package Pair Process " +
508
+ "Runtime Runnable SecurityManager Short StackTraceElement StrictMath String " +
509
+ "StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void"
510
+ ),
511
+ multiLineStrings: true,
512
+ blockKeywords: words("catch class do else finally for forSome if match switch try while"),
513
+ defKeywords: words("class def object package trait type val var"),
514
+ atoms: words("true false null"),
515
+ indentStatements: false,
516
+ indentSwitch: false,
517
+ hooks: {
518
+ "@": function(stream) {
519
+ stream.eatWhile(/[\w\$_]/);
520
+ return "meta";
521
+ },
522
+ '"': function(stream, state) {
523
+ if (!stream.match('""')) return false;
524
+ state.tokenize = tokenTripleString;
525
+ return state.tokenize(stream, state);
526
+ },
527
+ "'": function(stream) {
528
+ stream.eatWhile(/[\w\$_\xa1-\uffff]/);
529
+ return "atom";
530
+ },
531
+ "=": function(stream, state) {
532
+ var cx = state.context
533
+ if (cx.type == "}" && cx.align && stream.eat(">")) {
534
+ state.context = new Context(cx.indented, cx.column, cx.type, null, cx.prev)
535
+ return "operator"
536
+ } else {
537
+ return false
538
+ }
539
+ }
540
+ },
541
+ modeProps: {closeBrackets: {triples: '"'}}
542
+ });
543
+
544
+ function tokenKotlinString(tripleString){
545
+ return function (stream, state) {
546
+ var escaped = false, next, end = false;
547
+ while (!stream.eol()) {
548
+ if (!tripleString && !escaped && stream.match('"') ) {end = true; break;}
549
+ if (tripleString && stream.match('"""')) {end = true; break;}
550
+ next = stream.next();
551
+ if(!escaped && next == "$" && stream.match('{'))
552
+ stream.skipTo("}");
553
+ escaped = !escaped && next == "\\" && !tripleString;
554
+ }
555
+ if (end || !tripleString)
556
+ state.tokenize = null;
557
+ return "string";
558
+ }
559
+ }
560
+
561
+ def("text/x-kotlin", {
562
+ name: "clike",
563
+ keywords: words(
564
+ /*keywords*/
565
+ "package as typealias class interface this super val " +
566
+ "var fun for is in This throw return " +
567
+ "break continue object if else while do try when !in !is as? " +
568
+
569
+ /*soft keywords*/
570
+ "file import where by get set abstract enum open inner override private public internal " +
571
+ "protected catch finally out final vararg reified dynamic companion constructor init " +
572
+ "sealed field property receiver param sparam lateinit data inline noinline tailrec " +
573
+ "external annotation crossinline const operator infix"
574
+ ),
575
+ types: words(
576
+ /* package java.lang */
577
+ "Boolean Byte Character CharSequence Class ClassLoader Cloneable Comparable " +
578
+ "Compiler Double Exception Float Integer Long Math Number Object Package Pair Process " +
579
+ "Runtime Runnable SecurityManager Short StackTraceElement StrictMath String " +
580
+ "StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void"
581
+ ),
582
+ intendSwitch: false,
583
+ indentStatements: false,
584
+ multiLineStrings: true,
585
+ blockKeywords: words("catch class do else finally for if where try while enum"),
586
+ defKeywords: words("class val var object package interface fun"),
587
+ atoms: words("true false null this"),
588
+ hooks: {
589
+ '"': function(stream, state) {
590
+ state.tokenize = tokenKotlinString(stream.match('""'));
591
+ return state.tokenize(stream, state);
592
+ }
593
+ },
594
+ modeProps: {closeBrackets: {triples: '"'}}
595
+ });
596
+
597
+ def(["x-shader/x-vertex", "x-shader/x-fragment"], {
598
+ name: "clike",
599
+ keywords: words("sampler1D sampler2D sampler3D samplerCube " +
600
+ "sampler1DShadow sampler2DShadow " +
601
+ "const attribute uniform varying " +
602
+ "break continue discard return " +
603
+ "for while do if else struct " +
604
+ "in out inout"),
605
+ types: words("float int bool void " +
606
+ "vec2 vec3 vec4 ivec2 ivec3 ivec4 bvec2 bvec3 bvec4 " +
607
+ "mat2 mat3 mat4"),
608
+ blockKeywords: words("for while do if else struct"),
609
+ builtin: words("radians degrees sin cos tan asin acos atan " +
610
+ "pow exp log exp2 sqrt inversesqrt " +
611
+ "abs sign floor ceil fract mod min max clamp mix step smoothstep " +
612
+ "length distance dot cross normalize ftransform faceforward " +
613
+ "reflect refract matrixCompMult " +
614
+ "lessThan lessThanEqual greaterThan greaterThanEqual " +
615
+ "equal notEqual any all not " +
616
+ "texture1D texture1DProj texture1DLod texture1DProjLod " +
617
+ "texture2D texture2DProj texture2DLod texture2DProjLod " +
618
+ "texture3D texture3DProj texture3DLod texture3DProjLod " +
619
+ "textureCube textureCubeLod " +
620
+ "shadow1D shadow2D shadow1DProj shadow2DProj " +
621
+ "shadow1DLod shadow2DLod shadow1DProjLod shadow2DProjLod " +
622
+ "dFdx dFdy fwidth " +
623
+ "noise1 noise2 noise3 noise4"),
624
+ atoms: words("true false " +
625
+ "gl_FragColor gl_SecondaryColor gl_Normal gl_Vertex " +
626
+ "gl_MultiTexCoord0 gl_MultiTexCoord1 gl_MultiTexCoord2 gl_MultiTexCoord3 " +
627
+ "gl_MultiTexCoord4 gl_MultiTexCoord5 gl_MultiTexCoord6 gl_MultiTexCoord7 " +
628
+ "gl_FogCoord gl_PointCoord " +
629
+ "gl_Position gl_PointSize gl_ClipVertex " +
630
+ "gl_FrontColor gl_BackColor gl_FrontSecondaryColor gl_BackSecondaryColor " +
631
+ "gl_TexCoord gl_FogFragCoord " +
632
+ "gl_FragCoord gl_FrontFacing " +
633
+ "gl_FragData gl_FragDepth " +
634
+ "gl_ModelViewMatrix gl_ProjectionMatrix gl_ModelViewProjectionMatrix " +
635
+ "gl_TextureMatrix gl_NormalMatrix gl_ModelViewMatrixInverse " +
636
+ "gl_ProjectionMatrixInverse gl_ModelViewProjectionMatrixInverse " +
637
+ "gl_TexureMatrixTranspose gl_ModelViewMatrixInverseTranspose " +
638
+ "gl_ProjectionMatrixInverseTranspose " +
639
+ "gl_ModelViewProjectionMatrixInverseTranspose " +
640
+ "gl_TextureMatrixInverseTranspose " +
641
+ "gl_NormalScale gl_DepthRange gl_ClipPlane " +
642
+ "gl_Point gl_FrontMaterial gl_BackMaterial gl_LightSource gl_LightModel " +
643
+ "gl_FrontLightModelProduct gl_BackLightModelProduct " +
644
+ "gl_TextureColor gl_EyePlaneS gl_EyePlaneT gl_EyePlaneR gl_EyePlaneQ " +
645
+ "gl_FogParameters " +
646
+ "gl_MaxLights gl_MaxClipPlanes gl_MaxTextureUnits gl_MaxTextureCoords " +
647
+ "gl_MaxVertexAttribs gl_MaxVertexUniformComponents gl_MaxVaryingFloats " +
648
+ "gl_MaxVertexTextureImageUnits gl_MaxTextureImageUnits " +
649
+ "gl_MaxFragmentUniformComponents gl_MaxCombineTextureImageUnits " +
650
+ "gl_MaxDrawBuffers"),
651
+ indentSwitch: false,
652
+ hooks: {"#": cppHook},
653
+ modeProps: {fold: ["brace", "include"]}
654
+ });
655
+
656
+ def("text/x-nesc", {
657
+ name: "clike",
658
+ keywords: words(cKeywords + "as atomic async call command component components configuration event generic " +
659
+ "implementation includes interface module new norace nx_struct nx_union post provides " +
660
+ "signal task uses abstract extends"),
661
+ types: words(cTypes),
662
+ blockKeywords: words("case do else for if switch while struct"),
663
+ atoms: words("null true false"),
664
+ hooks: {"#": cppHook},
665
+ modeProps: {fold: ["brace", "include"]}
666
+ });
667
+
668
+ def("text/x-objectivec", {
669
+ name: "clike",
670
+ keywords: words(cKeywords + "inline restrict _Bool _Complex _Imaginery BOOL Class bycopy byref id IMP in " +
671
+ "inout nil oneway out Protocol SEL self super atomic nonatomic retain copy readwrite readonly"),
672
+ types: words(cTypes),
673
+ atoms: words("YES NO NULL NILL ON OFF true false"),
674
+ hooks: {
675
+ "@": function(stream) {
676
+ stream.eatWhile(/[\w\$]/);
677
+ return "keyword";
678
+ },
679
+ "#": cppHook,
680
+ indent: function(_state, ctx, textAfter) {
681
+ if (ctx.type == "statement" && /^@\w/.test(textAfter)) return ctx.indented
682
+ }
683
+ },
684
+ modeProps: {fold: "brace"}
685
+ });
686
+
687
+ def("text/x-squirrel", {
688
+ name: "clike",
689
+ keywords: words("base break clone continue const default delete enum extends function in class" +
690
+ " foreach local resume return this throw typeof yield constructor instanceof static"),
691
+ types: words(cTypes),
692
+ blockKeywords: words("case catch class else for foreach if switch try while"),
693
+ defKeywords: words("function local class"),
694
+ typeFirstDefinitions: true,
695
+ atoms: words("true false null"),
696
+ hooks: {"#": cppHook},
697
+ modeProps: {fold: ["brace", "include"]}
698
+ });
699
+
700
+ // Ceylon Strings need to deal with interpolation
701
+ var stringTokenizer = null;
702
+ function tokenCeylonString(type) {
703
+ return function(stream, state) {
704
+ var escaped = false, next, end = false;
705
+ while (!stream.eol()) {
706
+ if (!escaped && stream.match('"') &&
707
+ (type == "single" || stream.match('""'))) {
708
+ end = true;
709
+ break;
710
+ }
711
+ if (!escaped && stream.match('``')) {
712
+ stringTokenizer = tokenCeylonString(type);
713
+ end = true;
714
+ break;
715
+ }
716
+ next = stream.next();
717
+ escaped = type == "single" && !escaped && next == "\\";
718
+ }
719
+ if (end)
720
+ state.tokenize = null;
721
+ return "string";
722
+ }
723
+ }
724
+
725
+ def("text/x-ceylon", {
726
+ name: "clike",
727
+ keywords: words("abstracts alias assembly assert assign break case catch class continue dynamic else" +
728
+ " exists extends finally for function given if import in interface is let module new" +
729
+ " nonempty object of out outer package return satisfies super switch then this throw" +
730
+ " try value void while"),
731
+ types: function(word) {
732
+ // In Ceylon all identifiers that start with an uppercase are types
733
+ var first = word.charAt(0);
734
+ return (first === first.toUpperCase() && first !== first.toLowerCase());
735
+ },
736
+ blockKeywords: words("case catch class dynamic else finally for function if interface module new object switch try while"),
737
+ defKeywords: words("class dynamic function interface module object package value"),
738
+ builtin: words("abstract actual aliased annotation by default deprecated doc final formal late license" +
739
+ " native optional sealed see serializable shared suppressWarnings tagged throws variable"),
740
+ isPunctuationChar: /[\[\]{}\(\),;\:\.`]/,
741
+ isOperatorChar: /[+\-*&%=<>!?|^~:\/]/,
742
+ numberStart: /[\d#$]/,
743
+ number: /^(?:#[\da-fA-F_]+|\$[01_]+|[\d_]+[kMGTPmunpf]?|[\d_]+\.[\d_]+(?:[eE][-+]?\d+|[kMGTPmunpf]|)|)/i,
744
+ multiLineStrings: true,
745
+ typeFirstDefinitions: true,
746
+ atoms: words("true false null larger smaller equal empty finished"),
747
+ indentSwitch: false,
748
+ styleDefs: false,
749
+ hooks: {
750
+ "@": function(stream) {
751
+ stream.eatWhile(/[\w\$_]/);
752
+ return "meta";
753
+ },
754
+ '"': function(stream, state) {
755
+ state.tokenize = tokenCeylonString(stream.match('""') ? "triple" : "single");
756
+ return state.tokenize(stream, state);
757
+ },
758
+ '`': function(stream, state) {
759
+ if (!stringTokenizer || !stream.match('`')) return false;
760
+ state.tokenize = stringTokenizer;
761
+ stringTokenizer = null;
762
+ return state.tokenize(stream, state);
763
+ },
764
+ "'": function(stream) {
765
+ stream.eatWhile(/[\w\$_\xa1-\uffff]/);
766
+ return "atom";
767
+ },
768
+ token: function(_stream, state, style) {
769
+ if ((style == "variable" || style == "variable-3") &&
770
+ state.prevToken == ".") {
771
+ return "variable-2";
772
+ }
773
+ }
774
+ },
775
+ modeProps: {
776
+ fold: ["brace", "import"],
777
+ closeBrackets: {triples: '"'}
778
+ }
779
+ });
780
+
781
+ });
codemirror/mode/clike/index.html ADDED
@@ -0,0 +1,360 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!doctype html>
2
+
3
+ <title>CodeMirror: C-like mode</title>
4
+ <meta charset="utf-8"/>
5
+ <link rel=stylesheet href="../../doc/docs.css">
6
+
7
+ <link rel="stylesheet" href="../../lib/codemirror.css">
8
+ <script src="../../lib/codemirror.js"></script>
9
+ <script src="../../addon/edit/matchbrackets.js"></script>
10
+ <link rel="stylesheet" href="../../addon/hint/show-hint.css">
11
+ <script src="../../addon/hint/show-hint.js"></script>
12
+ <script src="clike.js"></script>
13
+ <style>.CodeMirror {border: 2px inset #dee;}</style>
14
+ <div id=nav>
15
+ <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
16
+
17
+ <ul>
18
+ <li><a href="../../index.html">Home</a>
19
+ <li><a href="../../doc/manual.html">Manual</a>
20
+ <li><a href="https://github.com/codemirror/codemirror">Code</a>
21
+ </ul>
22
+ <ul>
23
+ <li><a href="../index.html">Language modes</a>
24
+ <li><a class=active href="#">C-like</a>
25
+ </ul>
26
+ </div>
27
+
28
+ <article>
29
+ <h2>C-like mode</h2>
30
+
31
+ <div><textarea id="c-code">
32
+ /* C demo code */
33
+
34
+ #include <zmq.h>
35
+ #include <pthread.h>
36
+ #include <semaphore.h>
37
+ #include <time.h>
38
+ #include <stdio.h>
39
+ #include <fcntl.h>
40
+ #include <malloc.h>
41
+
42
+ typedef struct {
43
+ void* arg_socket;
44
+ zmq_msg_t* arg_msg;
45
+ char* arg_string;
46
+ unsigned long arg_len;
47
+ int arg_int, arg_command;
48
+
49
+ int signal_fd;
50
+ int pad;
51
+ void* context;
52
+ sem_t sem;
53
+ } acl_zmq_context;
54
+
55
+ #define p(X) (context->arg_##X)
56
+
57
+ void* zmq_thread(void* context_pointer) {
58
+ acl_zmq_context* context = (acl_zmq_context*)context_pointer;
59
+ char ok = 'K', err = 'X';
60
+ int res;
61
+
62
+ while (1) {
63
+ while ((res = sem_wait(&amp;context->sem)) == EINTR);
64
+ if (res) {write(context->signal_fd, &amp;err, 1); goto cleanup;}
65
+ switch(p(command)) {
66
+ case 0: goto cleanup;
67
+ case 1: p(socket) = zmq_socket(context->context, p(int)); break;
68
+ case 2: p(int) = zmq_close(p(socket)); break;
69
+ case 3: p(int) = zmq_bind(p(socket), p(string)); break;
70
+ case 4: p(int) = zmq_connect(p(socket), p(string)); break;
71
+ case 5: p(int) = zmq_getsockopt(p(socket), p(int), (void*)p(string), &amp;p(len)); break;
72
+ case 6: p(int) = zmq_setsockopt(p(socket), p(int), (void*)p(string), p(len)); break;
73
+ case 7: p(int) = zmq_send(p(socket), p(msg), p(int)); break;
74
+ case 8: p(int) = zmq_recv(p(socket), p(msg), p(int)); break;
75
+ case 9: p(int) = zmq_poll(p(socket), p(int), p(len)); break;
76
+ }
77
+ p(command) = errno;
78
+ write(context->signal_fd, &amp;ok, 1);
79
+ }
80
+ cleanup:
81
+ close(context->signal_fd);
82
+ free(context_pointer);
83
+ return 0;
84
+ }
85
+
86
+ void* zmq_thread_init(void* zmq_context, int signal_fd) {
87
+ acl_zmq_context* context = malloc(sizeof(acl_zmq_context));
88
+ pthread_t thread;
89
+
90
+ context->context = zmq_context;
91
+ context->signal_fd = signal_fd;
92
+ sem_init(&amp;context->sem, 1, 0);
93
+ pthread_create(&amp;thread, 0, &amp;zmq_thread, context);
94
+ pthread_detach(thread);
95
+ return context;
96
+ }
97
+ </textarea></div>
98
+
99
+ <h2>C++ example</h2>
100
+
101
+ <div><textarea id="cpp-code">
102
+ #include <iostream>
103
+ #include "mystuff/util.h"
104
+
105
+ namespace {
106
+ enum Enum {
107
+ VAL1, VAL2, VAL3
108
+ };
109
+
110
+ char32_t unicode_string = U"\U0010FFFF";
111
+ string raw_string = R"delim(anything
112
+ you
113
+ want)delim";
114
+
115
+ int Helper(const MyType& param) {
116
+ return 0;
117
+ }
118
+ } // namespace
119
+
120
+ class ForwardDec;
121
+
122
+ template <class T, class V>
123
+ class Class : public BaseClass {
124
+ const MyType<T, V> member_;
125
+
126
+ public:
127
+ const MyType<T, V>& Method() const {
128
+ return member_;
129
+ }
130
+
131
+ void Method2(MyType<T, V>* value);
132
+ }
133
+
134
+ template <class T, class V>
135
+ void Class::Method2(MyType<T, V>* value) {
136
+ std::out << 1 >> method();
137
+ value->Method3(member_);
138
+ member_ = value;
139
+ }
140
+ </textarea></div>
141
+
142
+ <h2>Objective-C example</h2>
143
+
144
+ <div><textarea id="objectivec-code">
145
+ /*
146
+ This is a longer comment
147
+ That spans two lines
148
+ */
149
+
150
+ #import <Test/Test.h>
151
+ @implementation YourAppDelegate
152
+
153
+ // This is a one-line comment
154
+
155
+ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
156
+ char myString[] = "This is a C character array";
157
+ int test = 5;
158
+ return YES;
159
+ }
160
+ </textarea></div>
161
+
162
+ <h2>Java example</h2>
163
+
164
+ <div><textarea id="java-code">
165
+ import com.demo.util.MyType;
166
+ import com.demo.util.MyInterface;
167
+
168
+ public enum Enum {
169
+ VAL1, VAL2, VAL3
170
+ }
171
+
172
+ public class Class<T, V> implements MyInterface {
173
+ public static final MyType<T, V> member;
174
+
175
+ private class InnerClass {
176
+ public int zero() {
177
+ return 0;
178
+ }
179
+ }
180
+
181
+ @Override
182
+ public MyType method() {
183
+ return member;
184
+ }
185
+
186
+ public void method2(MyType<T, V> value) {
187
+ method();
188
+ value.method3();
189
+ member = value;
190
+ }
191
+ }
192
+ </textarea></div>
193
+
194
+ <h2>Scala example</h2>
195
+
196
+ <div><textarea id="scala-code">
197
+ object FilterTest extends App {
198
+ def filter(xs: List[Int], threshold: Int) = {
199
+ def process(ys: List[Int]): List[Int] =
200
+ if (ys.isEmpty) ys
201
+ else if (ys.head < threshold) ys.head :: process(ys.tail)
202
+ else process(ys.tail)
203
+ process(xs)
204
+ }
205
+ println(filter(List(1, 9, 2, 8, 3, 7, 4), 5))
206
+ }
207
+ </textarea></div>
208
+
209
+ <h2>Kotlin mode</h2>
210
+
211
+ <div><textarea id="kotlin-code">
212
+ package org.wasabi.http
213
+
214
+ import java.util.concurrent.Executors
215
+ import java.net.InetSocketAddress
216
+ import org.wasabi.app.AppConfiguration
217
+ import io.netty.bootstrap.ServerBootstrap
218
+ import io.netty.channel.nio.NioEventLoopGroup
219
+ import io.netty.channel.socket.nio.NioServerSocketChannel
220
+ import org.wasabi.app.AppServer
221
+
222
+ public class HttpServer(private val appServer: AppServer) {
223
+
224
+ val bootstrap: ServerBootstrap
225
+ val primaryGroup: NioEventLoopGroup
226
+ val workerGroup: NioEventLoopGroup
227
+
228
+ init {
229
+ // Define worker groups
230
+ primaryGroup = NioEventLoopGroup()
231
+ workerGroup = NioEventLoopGroup()
232
+
233
+ // Initialize bootstrap of server
234
+ bootstrap = ServerBootstrap()
235
+
236
+ bootstrap.group(primaryGroup, workerGroup)
237
+ bootstrap.channel(javaClass<NioServerSocketChannel>())
238
+ bootstrap.childHandler(NettyPipelineInitializer(appServer))
239
+ }
240
+
241
+ public fun start(wait: Boolean = true) {
242
+ val channel = bootstrap.bind(appServer.configuration.port)?.sync()?.channel()
243
+
244
+ if (wait) {
245
+ channel?.closeFuture()?.sync()
246
+ }
247
+ }
248
+
249
+ public fun stop() {
250
+ // Shutdown all event loops
251
+ primaryGroup.shutdownGracefully()
252
+ workerGroup.shutdownGracefully()
253
+
254
+ // Wait till all threads are terminated
255
+ primaryGroup.terminationFuture().sync()
256
+ workerGroup.terminationFuture().sync()
257
+ }
258
+ }
259
+ </textarea></div>
260
+
261
+ <h2>Ceylon mode</h2>
262
+
263
+ <div><textarea id="ceylon-code">
264
+ "Produces the [[stream|Iterable]] that results from repeated
265
+ application of the given [[function|next]] to the given
266
+ [[first]] element of the stream, until the function first
267
+ returns [[finished]]. If the given function never returns
268
+ `finished`, the resulting stream is infinite.
269
+
270
+ For example:
271
+
272
+ loop(0)(2.plus).takeWhile(10.largerThan)
273
+
274
+ produces the stream `{ 0, 2, 4, 6, 8 }`."
275
+ tagged("Streams")
276
+ shared {Element+} loop&lt;Element&gt;(
277
+ "The first element of the resulting stream."
278
+ Element first)(
279
+ "The function that produces the next element of the
280
+ stream, given the current element. The function may
281
+ return [[finished]] to indicate the end of the
282
+ stream."
283
+ Element|Finished next(Element element))
284
+ =&gt; let (start = first)
285
+ object satisfies {Element+} {
286
+ first =&gt; start;
287
+ empty =&gt; false;
288
+ function nextElement(Element element)
289
+ =&gt; next(element);
290
+ iterator()
291
+ =&gt; object satisfies Iterator&lt;Element&gt; {
292
+ variable Element|Finished current = start;
293
+ shared actual Element|Finished next() {
294
+ if (!is Finished result = current) {
295
+ current = nextElement(result);
296
+ return result;
297
+ }
298
+ else {
299
+ return finished;
300
+ }
301
+ }
302
+ };
303
+ };
304
+ </textarea></div>
305
+
306
+ <script>
307
+ var cEditor = CodeMirror.fromTextArea(document.getElementById("c-code"), {
308
+ lineNumbers: true,
309
+ matchBrackets: true,
310
+ mode: "text/x-csrc"
311
+ });
312
+ var cppEditor = CodeMirror.fromTextArea(document.getElementById("cpp-code"), {
313
+ lineNumbers: true,
314
+ matchBrackets: true,
315
+ mode: "text/x-c++src"
316
+ });
317
+ var javaEditor = CodeMirror.fromTextArea(document.getElementById("java-code"), {
318
+ lineNumbers: true,
319
+ matchBrackets: true,
320
+ mode: "text/x-java"
321
+ });
322
+ var objectivecEditor = CodeMirror.fromTextArea(document.getElementById("objectivec-code"), {
323
+ lineNumbers: true,
324
+ matchBrackets: true,
325
+ mode: "text/x-objectivec"
326
+ });
327
+ var scalaEditor = CodeMirror.fromTextArea(document.getElementById("scala-code"), {
328
+ lineNumbers: true,
329
+ matchBrackets: true,
330
+ mode: "text/x-scala"
331
+ });
332
+ var kotlinEditor = CodeMirror.fromTextArea(document.getElementById("kotlin-code"), {
333
+ lineNumbers: true,
334
+ matchBrackets: true,
335
+ mode: "text/x-kotlin"
336
+ });
337
+ var ceylonEditor = CodeMirror.fromTextArea(document.getElementById("ceylon-code"), {
338
+ lineNumbers: true,
339
+ matchBrackets: true,
340
+ mode: "text/x-ceylon"
341
+ });
342
+ var mac = CodeMirror.keyMap.default == CodeMirror.keyMap.macDefault;
343
+ CodeMirror.keyMap.default[(mac ? "Cmd" : "Ctrl") + "-Space"] = "autocomplete";
344
+ </script>
345
+
346
+ <p>Simple mode that tries to handle C-like languages as well as it
347
+ can. Takes two configuration parameters: <code>keywords</code>, an
348
+ object whose property names are the keywords in the language,
349
+ and <code>useCPP</code>, which determines whether C preprocessor
350
+ directives are recognized.</p>
351
+
352
+ <p><strong>MIME types defined:</strong> <code>text/x-csrc</code>
353
+ (C), <code>text/x-c++src</code> (C++), <code>text/x-java</code>
354
+ (Java), <code>text/x-csharp</code> (C#),
355
+ <code>text/x-objectivec</code> (Objective-C),
356
+ <code>text/x-scala</code> (Scala), <code>text/x-vertex</code>
357
+ <code>x-shader/x-fragment</code> (shader programs),
358
+ <code>text/x-squirrel</code> (Squirrel) and
359
+ <code>text/x-ceylon</code> (Ceylon)</p>
360
+ </article>
codemirror/mode/clike/scala.html ADDED
@@ -0,0 +1,767 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!doctype html>
2
+
3
+ <title>CodeMirror: Scala mode</title>
4
+ <meta charset="utf-8"/>
5
+ <link rel=stylesheet href="../../doc/docs.css">
6
+
7
+ <link rel="stylesheet" href="../../lib/codemirror.css">
8
+ <link rel="stylesheet" href="../../theme/ambiance.css">
9
+ <script src="../../lib/codemirror.js"></script>
10
+ <script src="../../addon/edit/matchbrackets.js"></script>
11
+ <script src="clike.js"></script>
12
+ <div id=nav>
13
+ <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
14
+
15
+ <ul>
16
+ <li><a href="../../index.html">Home</a>
17
+ <li><a href="../../doc/manual.html">Manual</a>
18
+ <li><a href="https://github.com/codemirror/codemirror">Code</a>
19
+ </ul>
20
+ <ul>
21
+ <li><a href="../index.html">Language modes</a>
22
+ <li><a class=active href="#">Scala</a>
23
+ </ul>
24
+ </div>
25
+
26
+ <article>
27
+ <h2>Scala mode</h2>
28
+ <form>
29
+ <textarea id="code" name="code">
30
+
31
+ /* __ *\
32
+ ** ________ ___ / / ___ Scala API **
33
+ ** / __/ __// _ | / / / _ | (c) 2003-2011, LAMP/EPFL **
34
+ ** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
35
+ ** /____/\___/_/ |_/____/_/ | | **
36
+ ** |/ **
37
+ \* */
38
+
39
+ package scala.collection
40
+
41
+ import generic._
42
+ import mutable.{ Builder, ListBuffer }
43
+ import annotation.{tailrec, migration, bridge}
44
+ import annotation.unchecked.{ uncheckedVariance => uV }
45
+ import parallel.ParIterable
46
+
47
+ /** A template trait for traversable collections of type `Traversable[A]`.
48
+ *
49
+ * $traversableInfo
50
+ * @define mutability
51
+ * @define traversableInfo
52
+ * This is a base trait of all kinds of $mutability Scala collections. It
53
+ * implements the behavior common to all collections, in terms of a method
54
+ * `foreach` with signature:
55
+ * {{{
56
+ * def foreach[U](f: Elem => U): Unit
57
+ * }}}
58
+ * Collection classes mixing in this trait provide a concrete
59
+ * `foreach` method which traverses all the
60
+ * elements contained in the collection, applying a given function to each.
61
+ * They also need to provide a method `newBuilder`
62
+ * which creates a builder for collections of the same kind.
63
+ *
64
+ * A traversable class might or might not have two properties: strictness
65
+ * and orderedness. Neither is represented as a type.
66
+ *
67
+ * The instances of a strict collection class have all their elements
68
+ * computed before they can be used as values. By contrast, instances of
69
+ * a non-strict collection class may defer computation of some of their
70
+ * elements until after the instance is available as a value.
71
+ * A typical example of a non-strict collection class is a
72
+ * <a href="../immutable/Stream.html" target="ContentFrame">
73
+ * `scala.collection.immutable.Stream`</a>.
74
+ * A more general class of examples are `TraversableViews`.
75
+ *
76
+ * If a collection is an instance of an ordered collection class, traversing
77
+ * its elements with `foreach` will always visit elements in the
78
+ * same order, even for different runs of the program. If the class is not
79
+ * ordered, `foreach` can visit elements in different orders for
80
+ * different runs (but it will keep the same order in the same run).'
81
+ *
82
+ * A typical example of a collection class which is not ordered is a
83
+ * `HashMap` of objects. The traversal order for hash maps will
84
+ * depend on the hash codes of its elements, and these hash codes might
85
+ * differ from one run to the next. By contrast, a `LinkedHashMap`
86
+ * is ordered because it's `foreach` method visits elements in the
87
+ * order they were inserted into the `HashMap`.
88
+ *
89
+ * @author Martin Odersky
90
+ * @version 2.8
91
+ * @since 2.8
92
+ * @tparam A the element type of the collection
93
+ * @tparam Repr the type of the actual collection containing the elements.
94
+ *
95
+ * @define Coll Traversable
96
+ * @define coll traversable collection
97
+ */
98
+ trait TraversableLike[+A, +Repr] extends HasNewBuilder[A, Repr]
99
+ with FilterMonadic[A, Repr]
100
+ with TraversableOnce[A]
101
+ with GenTraversableLike[A, Repr]
102
+ with Parallelizable[A, ParIterable[A]]
103
+ {
104
+ self =>
105
+
106
+ import Traversable.breaks._
107
+
108
+ /** The type implementing this traversable */
109
+ protected type Self = Repr
110
+
111
+ /** The collection of type $coll underlying this `TraversableLike` object.
112
+ * By default this is implemented as the `TraversableLike` object itself,
113
+ * but this can be overridden.
114
+ */
115
+ def repr: Repr = this.asInstanceOf[Repr]
116
+
117
+ /** The underlying collection seen as an instance of `$Coll`.
118
+ * By default this is implemented as the current collection object itself,
119
+ * but this can be overridden.
120
+ */
121
+ protected[this] def thisCollection: Traversable[A] = this.asInstanceOf[Traversable[A]]
122
+
123
+ /** A conversion from collections of type `Repr` to `$Coll` objects.
124
+ * By default this is implemented as just a cast, but this can be overridden.
125
+ */
126
+ protected[this] def toCollection(repr: Repr): Traversable[A] = repr.asInstanceOf[Traversable[A]]
127
+
128
+ /** Creates a new builder for this collection type.
129
+ */
130
+ protected[this] def newBuilder: Builder[A, Repr]
131
+
132
+ protected[this] def parCombiner = ParIterable.newCombiner[A]
133
+
134
+ /** Applies a function `f` to all elements of this $coll.
135
+ *
136
+ * Note: this method underlies the implementation of most other bulk operations.
137
+ * It's important to implement this method in an efficient way.
138
+ *
139
+ *
140
+ * @param f the function that is applied for its side-effect to every element.
141
+ * The result of function `f` is discarded.
142
+ *
143
+ * @tparam U the type parameter describing the result of function `f`.
144
+ * This result will always be ignored. Typically `U` is `Unit`,
145
+ * but this is not necessary.
146
+ *
147
+ * @usecase def foreach(f: A => Unit): Unit
148
+ */
149
+ def foreach[U](f: A => U): Unit
150
+
151
+ /** Tests whether this $coll is empty.
152
+ *
153
+ * @return `true` if the $coll contain no elements, `false` otherwise.
154
+ */
155
+ def isEmpty: Boolean = {
156
+ var result = true
157
+ breakable {
158
+ for (x <- this) {
159
+ result = false
160
+ break
161
+ }
162
+ }
163
+ result
164
+ }
165
+
166
+ /** Tests whether this $coll is known to have a finite size.
167
+ * All strict collections are known to have finite size. For a non-strict collection
168
+ * such as `Stream`, the predicate returns `true` if all elements have been computed.
169
+ * It returns `false` if the stream is not yet evaluated to the end.
170
+ *
171
+ * Note: many collection methods will not work on collections of infinite sizes.
172
+ *
173
+ * @return `true` if this collection is known to have finite size, `false` otherwise.
174
+ */
175
+ def hasDefiniteSize = true
176
+
177
+ def ++[B >: A, That](that: GenTraversableOnce[B])(implicit bf: CanBuildFrom[Repr, B, That]): That = {
178
+ val b = bf(repr)
179
+ if (that.isInstanceOf[IndexedSeqLike[_, _]]) b.sizeHint(this, that.seq.size)
180
+ b ++= thisCollection
181
+ b ++= that.seq
182
+ b.result
183
+ }
184
+
185
+ @bridge
186
+ def ++[B >: A, That](that: TraversableOnce[B])(implicit bf: CanBuildFrom[Repr, B, That]): That =
187
+ ++(that: GenTraversableOnce[B])(bf)
188
+
189
+ /** Concatenates this $coll with the elements of a traversable collection.
190
+ * It differs from ++ in that the right operand determines the type of the
191
+ * resulting collection rather than the left one.
192
+ *
193
+ * @param that the traversable to append.
194
+ * @tparam B the element type of the returned collection.
195
+ * @tparam That $thatinfo
196
+ * @param bf $bfinfo
197
+ * @return a new collection of type `That` which contains all elements
198
+ * of this $coll followed by all elements of `that`.
199
+ *
200
+ * @usecase def ++:[B](that: TraversableOnce[B]): $Coll[B]
201
+ *
202
+ * @return a new $coll which contains all elements of this $coll
203
+ * followed by all elements of `that`.
204
+ */
205
+ def ++:[B >: A, That](that: TraversableOnce[B])(implicit bf: CanBuildFrom[Repr, B, That]): That = {
206
+ val b = bf(repr)
207
+ if (that.isInstanceOf[IndexedSeqLike[_, _]]) b.sizeHint(this, that.size)
208
+ b ++= that
209
+ b ++= thisCollection
210
+ b.result
211
+ }
212
+
213
+ /** This overload exists because: for the implementation of ++: we should reuse
214
+ * that of ++ because many collections override it with more efficient versions.
215
+ * Since TraversableOnce has no '++' method, we have to implement that directly,
216
+ * but Traversable and down can use the overload.
217
+ */
218
+ def ++:[B >: A, That](that: Traversable[B])(implicit bf: CanBuildFrom[Repr, B, That]): That =
219
+ (that ++ seq)(breakOut)
220
+
221
+ def map[B, That](f: A => B)(implicit bf: CanBuildFrom[Repr, B, That]): That = {
222
+ val b = bf(repr)
223
+ b.sizeHint(this)
224
+ for (x <- this) b += f(x)
225
+ b.result
226
+ }
227
+
228
+ def flatMap[B, That](f: A => GenTraversableOnce[B])(implicit bf: CanBuildFrom[Repr, B, That]): That = {
229
+ val b = bf(repr)
230
+ for (x <- this) b ++= f(x).seq
231
+ b.result
232
+ }
233
+
234
+ /** Selects all elements of this $coll which satisfy a predicate.
235
+ *
236
+ * @param p the predicate used to test elements.
237
+ * @return a new $coll consisting of all elements of this $coll that satisfy the given
238
+ * predicate `p`. The order of the elements is preserved.
239
+ */
240
+ def filter(p: A => Boolean): Repr = {
241
+ val b = newBuilder
242
+ for (x <- this)
243
+ if (p(x)) b += x
244
+ b.result
245
+ }
246
+
247
+ /** Selects all elements of this $coll which do not satisfy a predicate.
248
+ *
249
+ * @param p the predicate used to test elements.
250
+ * @return a new $coll consisting of all elements of this $coll that do not satisfy the given
251
+ * predicate `p`. The order of the elements is preserved.
252
+ */
253
+ def filterNot(p: A => Boolean): Repr = filter(!p(_))
254
+
255
+ def collect[B, That](pf: PartialFunction[A, B])(implicit bf: CanBuildFrom[Repr, B, That]): That = {
256
+ val b = bf(repr)
257
+ for (x <- this) if (pf.isDefinedAt(x)) b += pf(x)
258
+ b.result
259
+ }
260
+
261
+ /** Builds a new collection by applying an option-valued function to all
262
+ * elements of this $coll on which the function is defined.
263
+ *
264
+ * @param f the option-valued function which filters and maps the $coll.
265
+ * @tparam B the element type of the returned collection.
266
+ * @tparam That $thatinfo
267
+ * @param bf $bfinfo
268
+ * @return a new collection of type `That` resulting from applying the option-valued function
269
+ * `f` to each element and collecting all defined results.
270
+ * The order of the elements is preserved.
271
+ *
272
+ * @usecase def filterMap[B](f: A => Option[B]): $Coll[B]
273
+ *
274
+ * @param pf the partial function which filters and maps the $coll.
275
+ * @return a new $coll resulting from applying the given option-valued function
276
+ * `f` to each element and collecting all defined results.
277
+ * The order of the elements is preserved.
278
+ def filterMap[B, That](f: A => Option[B])(implicit bf: CanBuildFrom[Repr, B, That]): That = {
279
+ val b = bf(repr)
280
+ for (x <- this)
281
+ f(x) match {
282
+ case Some(y) => b += y
283
+ case _ =>
284
+ }
285
+ b.result
286
+ }
287
+ */
288
+
289
+ /** Partitions this $coll in two ${coll}s according to a predicate.
290
+ *
291
+ * @param p the predicate on which to partition.
292
+ * @return a pair of ${coll}s: the first $coll consists of all elements that
293
+ * satisfy the predicate `p` and the second $coll consists of all elements
294
+ * that don't. The relative order of the elements in the resulting ${coll}s
295
+ * is the same as in the original $coll.
296
+ */
297
+ def partition(p: A => Boolean): (Repr, Repr) = {
298
+ val l, r = newBuilder
299
+ for (x <- this) (if (p(x)) l else r) += x
300
+ (l.result, r.result)
301
+ }
302
+
303
+ def groupBy[K](f: A => K): immutable.Map[K, Repr] = {
304
+ val m = mutable.Map.empty[K, Builder[A, Repr]]
305
+ for (elem <- this) {
306
+ val key = f(elem)
307
+ val bldr = m.getOrElseUpdate(key, newBuilder)
308
+ bldr += elem
309
+ }
310
+ val b = immutable.Map.newBuilder[K, Repr]
311
+ for ((k, v) <- m)
312
+ b += ((k, v.result))
313
+
314
+ b.result
315
+ }
316
+
317
+ /** Tests whether a predicate holds for all elements of this $coll.
318
+ *
319
+ * $mayNotTerminateInf
320
+ *
321
+ * @param p the predicate used to test elements.
322
+ * @return `true` if the given predicate `p` holds for all elements
323
+ * of this $coll, otherwise `false`.
324
+ */
325
+ def forall(p: A => Boolean): Boolean = {
326
+ var result = true
327
+ breakable {
328
+ for (x <- this)
329
+ if (!p(x)) { result = false; break }
330
+ }
331
+ result
332
+ }
333
+
334
+ /** Tests whether a predicate holds for some of the elements of this $coll.
335
+ *
336
+ * $mayNotTerminateInf
337
+ *
338
+ * @param p the predicate used to test elements.
339
+ * @return `true` if the given predicate `p` holds for some of the
340
+ * elements of this $coll, otherwise `false`.
341
+ */
342
+ def exists(p: A => Boolean): Boolean = {
343
+ var result = false
344
+ breakable {
345
+ for (x <- this)
346
+ if (p(x)) { result = true; break }
347
+ }
348
+ result
349
+ }
350
+
351
+ /** Finds the first element of the $coll satisfying a predicate, if any.
352
+ *
353
+ * $mayNotTerminateInf
354
+ * $orderDependent
355
+ *
356
+ * @param p the predicate used to test elements.
357
+ * @return an option value containing the first element in the $coll
358
+ * that satisfies `p`, or `None` if none exists.
359
+ */
360
+ def find(p: A => Boolean): Option[A] = {
361
+ var result: Option[A] = None
362
+ breakable {
363
+ for (x <- this)
364
+ if (p(x)) { result = Some(x); break }
365
+ }
366
+ result
367
+ }
368
+
369
+ def scan[B >: A, That](z: B)(op: (B, B) => B)(implicit cbf: CanBuildFrom[Repr, B, That]): That = scanLeft(z)(op)
370
+
371
+ def scanLeft[B, That](z: B)(op: (B, A) => B)(implicit bf: CanBuildFrom[Repr, B, That]): That = {
372
+ val b = bf(repr)
373
+ b.sizeHint(this, 1)
374
+ var acc = z
375
+ b += acc
376
+ for (x <- this) { acc = op(acc, x); b += acc }
377
+ b.result
378
+ }
379
+
380
+ @migration(2, 9,
381
+ "This scanRight definition has changed in 2.9.\n" +
382
+ "The previous behavior can be reproduced with scanRight.reverse."
383
+ )
384
+ def scanRight[B, That](z: B)(op: (A, B) => B)(implicit bf: CanBuildFrom[Repr, B, That]): That = {
385
+ var scanned = List(z)
386
+ var acc = z
387
+ for (x <- reversed) {
388
+ acc = op(x, acc)
389
+ scanned ::= acc
390
+ }
391
+ val b = bf(repr)
392
+ for (elem <- scanned) b += elem
393
+ b.result
394
+ }
395
+
396
+ /** Selects the first element of this $coll.
397
+ * $orderDependent
398
+ * @return the first element of this $coll.
399
+ * @throws `NoSuchElementException` if the $coll is empty.
400
+ */
401
+ def head: A = {
402
+ var result: () => A = () => throw new NoSuchElementException
403
+ breakable {
404
+ for (x <- this) {
405
+ result = () => x
406
+ break
407
+ }
408
+ }
409
+ result()
410
+ }
411
+
412
+ /** Optionally selects the first element.
413
+ * $orderDependent
414
+ * @return the first element of this $coll if it is nonempty, `None` if it is empty.
415
+ */
416
+ def headOption: Option[A] = if (isEmpty) None else Some(head)
417
+
418
+ /** Selects all elements except the first.
419
+ * $orderDependent
420
+ * @return a $coll consisting of all elements of this $coll
421
+ * except the first one.
422
+ * @throws `UnsupportedOperationException` if the $coll is empty.
423
+ */
424
+ override def tail: Repr = {
425
+ if (isEmpty) throw new UnsupportedOperationException("empty.tail")
426
+ drop(1)
427
+ }
428
+
429
+ /** Selects the last element.
430
+ * $orderDependent
431
+ * @return The last element of this $coll.
432
+ * @throws NoSuchElementException If the $coll is empty.
433
+ */
434
+ def last: A = {
435
+ var lst = head
436
+ for (x <- this)
437
+ lst = x
438
+ lst
439
+ }
440
+
441
+ /** Optionally selects the last element.
442
+ * $orderDependent
443
+ * @return the last element of this $coll$ if it is nonempty, `None` if it is empty.
444
+ */
445
+ def lastOption: Option[A] = if (isEmpty) None else Some(last)
446
+
447
+ /** Selects all elements except the last.
448
+ * $orderDependent
449
+ * @return a $coll consisting of all elements of this $coll
450
+ * except the last one.
451
+ * @throws `UnsupportedOperationException` if the $coll is empty.
452
+ */
453
+ def init: Repr = {
454
+ if (isEmpty) throw new UnsupportedOperationException("empty.init")
455
+ var lst = head
456
+ var follow = false
457
+ val b = newBuilder
458
+ b.sizeHint(this, -1)
459
+ for (x <- this.seq) {
460
+ if (follow) b += lst
461
+ else follow = true
462
+ lst = x
463
+ }
464
+ b.result
465
+ }
466
+
467
+ def take(n: Int): Repr = slice(0, n)
468
+
469
+ def drop(n: Int): Repr =
470
+ if (n <= 0) {
471
+ val b = newBuilder
472
+ b.sizeHint(this)
473
+ b ++= thisCollection result
474
+ }
475
+ else sliceWithKnownDelta(n, Int.MaxValue, -n)
476
+
477
+ def slice(from: Int, until: Int): Repr = sliceWithKnownBound(math.max(from, 0), until)
478
+
479
+ // Precondition: from >= 0, until > 0, builder already configured for building.
480
+ private[this] def sliceInternal(from: Int, until: Int, b: Builder[A, Repr]): Repr = {
481
+ var i = 0
482
+ breakable {
483
+ for (x <- this.seq) {
484
+ if (i >= from) b += x
485
+ i += 1
486
+ if (i >= until) break
487
+ }
488
+ }
489
+ b.result
490
+ }
491
+ // Precondition: from >= 0
492
+ private[scala] def sliceWithKnownDelta(from: Int, until: Int, delta: Int): Repr = {
493
+ val b = newBuilder
494
+ if (until <= from) b.result
495
+ else {
496
+ b.sizeHint(this, delta)
497
+ sliceInternal(from, until, b)
498
+ }
499
+ }
500
+ // Precondition: from >= 0
501
+ private[scala] def sliceWithKnownBound(from: Int, until: Int): Repr = {
502
+ val b = newBuilder
503
+ if (until <= from) b.result
504
+ else {
505
+ b.sizeHintBounded(until - from, this)
506
+ sliceInternal(from, until, b)
507
+ }
508
+ }
509
+
510
+ def takeWhile(p: A => Boolean): Repr = {
511
+ val b = newBuilder
512
+ breakable {
513
+ for (x <- this) {
514
+ if (!p(x)) break
515
+ b += x
516
+ }
517
+ }
518
+ b.result
519
+ }
520
+
521
+ def dropWhile(p: A => Boolean): Repr = {
522
+ val b = newBuilder
523
+ var go = false
524
+ for (x <- this) {
525
+ if (!p(x)) go = true
526
+ if (go) b += x
527
+ }
528
+ b.result
529
+ }
530
+
531
+ def span(p: A => Boolean): (Repr, Repr) = {
532
+ val l, r = newBuilder
533
+ var toLeft = true
534
+ for (x <- this) {
535
+ toLeft = toLeft && p(x)
536
+ (if (toLeft) l else r) += x
537
+ }
538
+ (l.result, r.result)
539
+ }
540
+
541
+ def splitAt(n: Int): (Repr, Repr) = {
542
+ val l, r = newBuilder
543
+ l.sizeHintBounded(n, this)
544
+ if (n >= 0) r.sizeHint(this, -n)
545
+ var i = 0
546
+ for (x <- this) {
547
+ (if (i < n) l else r) += x
548
+ i += 1
549
+ }
550
+ (l.result, r.result)
551
+ }
552
+
553
+ /** Iterates over the tails of this $coll. The first value will be this
554
+ * $coll and the final one will be an empty $coll, with the intervening
555
+ * values the results of successive applications of `tail`.
556
+ *
557
+ * @return an iterator over all the tails of this $coll
558
+ * @example `List(1,2,3).tails = Iterator(List(1,2,3), List(2,3), List(3), Nil)`
559
+ */
560
+ def tails: Iterator[Repr] = iterateUntilEmpty(_.tail)
561
+
562
+ /** Iterates over the inits of this $coll. The first value will be this
563
+ * $coll and the final one will be an empty $coll, with the intervening
564
+ * values the results of successive applications of `init`.
565
+ *
566
+ * @return an iterator over all the inits of this $coll
567
+ * @example `List(1,2,3).inits = Iterator(List(1,2,3), List(1,2), List(1), Nil)`
568
+ */
569
+ def inits: Iterator[Repr] = iterateUntilEmpty(_.init)
570
+
571
+ /** Copies elements of this $coll to an array.
572
+ * Fills the given array `xs` with at most `len` elements of
573
+ * this $coll, starting at position `start`.
574
+ * Copying will stop once either the end of the current $coll is reached,
575
+ * or the end of the array is reached, or `len` elements have been copied.
576
+ *
577
+ * $willNotTerminateInf
578
+ *
579
+ * @param xs the array to fill.
580
+ * @param start the starting index.
581
+ * @param len the maximal number of elements to copy.
582
+ * @tparam B the type of the elements of the array.
583
+ *
584
+ *
585
+ * @usecase def copyToArray(xs: Array[A], start: Int, len: Int): Unit
586
+ */
587
+ def copyToArray[B >: A](xs: Array[B], start: Int, len: Int) {
588
+ var i = start
589
+ val end = (start + len) min xs.length
590
+ breakable {
591
+ for (x <- this) {
592
+ if (i >= end) break
593
+ xs(i) = x
594
+ i += 1
595
+ }
596
+ }
597
+ }
598
+
599
+ def toTraversable: Traversable[A] = thisCollection
600
+ def toIterator: Iterator[A] = toStream.iterator
601
+ def toStream: Stream[A] = toBuffer.toStream
602
+
603
+ /** Converts this $coll to a string.
604
+ *
605
+ * @return a string representation of this collection. By default this
606
+ * string consists of the `stringPrefix` of this $coll,
607
+ * followed by all elements separated by commas and enclosed in parentheses.
608
+ */
609
+ override def toString = mkString(stringPrefix + "(", ", ", ")")
610
+
611
+ /** Defines the prefix of this object's `toString` representation.
612
+ *
613
+ * @return a string representation which starts the result of `toString`
614
+ * applied to this $coll. By default the string prefix is the
615
+ * simple name of the collection class $coll.
616
+ */
617
+ def stringPrefix : String = {
618
+ var string = repr.asInstanceOf[AnyRef].getClass.getName
619
+ val idx1 = string.lastIndexOf('.' : Int)
620
+ if (idx1 != -1) string = string.substring(idx1 + 1)
621
+ val idx2 = string.indexOf('$')
622
+ if (idx2 != -1) string = string.substring(0, idx2)
623
+ string
624
+ }
625
+
626
+ /** Creates a non-strict view of this $coll.
627
+ *
628
+ * @return a non-strict view of this $coll.
629
+ */
630
+ def view = new TraversableView[A, Repr] {
631
+ protected lazy val underlying = self.repr
632
+ override def foreach[U](f: A => U) = self foreach f
633
+ }
634
+
635
+ /** Creates a non-strict view of a slice of this $coll.
636
+ *
637
+ * Note: the difference between `view` and `slice` is that `view` produces
638
+ * a view of the current $coll, whereas `slice` produces a new $coll.
639
+ *
640
+ * Note: `view(from, to)` is equivalent to `view.slice(from, to)`
641
+ * $orderDependent
642
+ *
643
+ * @param from the index of the first element of the view
644
+ * @param until the index of the element following the view
645
+ * @return a non-strict view of a slice of this $coll, starting at index `from`
646
+ * and extending up to (but not including) index `until`.
647
+ */
648
+ def view(from: Int, until: Int): TraversableView[A, Repr] = view.slice(from, until)
649
+
650
+ /** Creates a non-strict filter of this $coll.
651
+ *
652
+ * Note: the difference between `c filter p` and `c withFilter p` is that
653
+ * the former creates a new collection, whereas the latter only
654
+ * restricts the domain of subsequent `map`, `flatMap`, `foreach`,
655
+ * and `withFilter` operations.
656
+ * $orderDependent
657
+ *
658
+ * @param p the predicate used to test elements.
659
+ * @return an object of class `WithFilter`, which supports
660
+ * `map`, `flatMap`, `foreach`, and `withFilter` operations.
661
+ * All these operations apply to those elements of this $coll which
662
+ * satisfy the predicate `p`.
663
+ */
664
+ def withFilter(p: A => Boolean): FilterMonadic[A, Repr] = new WithFilter(p)
665
+
666
+ /** A class supporting filtered operations. Instances of this class are
667
+ * returned by method `withFilter`.
668
+ */
669
+ class WithFilter(p: A => Boolean) extends FilterMonadic[A, Repr] {
670
+
671
+ /** Builds a new collection by applying a function to all elements of the
672
+ * outer $coll containing this `WithFilter` instance that satisfy predicate `p`.
673
+ *
674
+ * @param f the function to apply to each element.
675
+ * @tparam B the element type of the returned collection.
676
+ * @tparam That $thatinfo
677
+ * @param bf $bfinfo
678
+ * @return a new collection of type `That` resulting from applying
679
+ * the given function `f` to each element of the outer $coll
680
+ * that satisfies predicate `p` and collecting the results.
681
+ *
682
+ * @usecase def map[B](f: A => B): $Coll[B]
683
+ *
684
+ * @return a new $coll resulting from applying the given function
685
+ * `f` to each element of the outer $coll that satisfies
686
+ * predicate `p` and collecting the results.
687
+ */
688
+ def map[B, That](f: A => B)(implicit bf: CanBuildFrom[Repr, B, That]): That = {
689
+ val b = bf(repr)
690
+ for (x <- self)
691
+ if (p(x)) b += f(x)
692
+ b.result
693
+ }
694
+
695
+ /** Builds a new collection by applying a function to all elements of the
696
+ * outer $coll containing this `WithFilter` instance that satisfy
697
+ * predicate `p` and concatenating the results.
698
+ *
699
+ * @param f the function to apply to each element.
700
+ * @tparam B the element type of the returned collection.
701
+ * @tparam That $thatinfo
702
+ * @param bf $bfinfo
703
+ * @return a new collection of type `That` resulting from applying
704
+ * the given collection-valued function `f` to each element
705
+ * of the outer $coll that satisfies predicate `p` and
706
+ * concatenating the results.
707
+ *
708
+ * @usecase def flatMap[B](f: A => TraversableOnce[B]): $Coll[B]
709
+ *
710
+ * @return a new $coll resulting from applying the given collection-valued function
711
+ * `f` to each element of the outer $coll that satisfies predicate `p` and concatenating the results.
712
+ */
713
+ def flatMap[B, That](f: A => GenTraversableOnce[B])(implicit bf: CanBuildFrom[Repr, B, That]): That = {
714
+ val b = bf(repr)
715
+ for (x <- self)
716
+ if (p(x)) b ++= f(x).seq
717
+ b.result
718
+ }
719
+
720
+ /** Applies a function `f` to all elements of the outer $coll containing
721
+ * this `WithFilter` instance that satisfy predicate `p`.
722
+ *
723
+ * @param f the function that is applied for its side-effect to every element.
724
+ * The result of function `f` is discarded.
725
+ *
726
+ * @tparam U the type parameter describing the result of function `f`.
727
+ * This result will always be ignored. Typically `U` is `Unit`,
728
+ * but this is not necessary.
729
+ *
730
+ * @usecase def foreach(f: A => Unit): Unit
731
+ */
732
+ def foreach[U](f: A => U): Unit =
733
+ for (x <- self)
734
+ if (p(x)) f(x)
735
+
736
+ /** Further refines the filter for this $coll.
737
+ *
738
+ * @param q the predicate used to test elements.
739
+ * @return an object of class `WithFilter`, which supports
740
+ * `map`, `flatMap`, `foreach`, and `withFilter` operations.
741
+ * All these operations apply to those elements of this $coll which
742
+ * satisfy the predicate `q` in addition to the predicate `p`.
743
+ */
744
+ def withFilter(q: A => Boolean): WithFilter =
745
+ new WithFilter(x => p(x) && q(x))
746
+ }
747
+
748
+ // A helper for tails and inits.
749
+ private def iterateUntilEmpty(f: Traversable[A @uV] => Traversable[A @uV]): Iterator[Repr] = {
750
+ val it = Iterator.iterate(thisCollection)(f) takeWhile (x => !x.isEmpty)
751
+ it ++ Iterator(Nil) map (newBuilder ++= _ result)
752
+ }
753
+ }
754
+
755
+
756
+ </textarea>
757
+ </form>
758
+
759
+ <script>
760
+ var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
761
+ lineNumbers: true,
762
+ matchBrackets: true,
763
+ theme: "ambiance",
764
+ mode: "text/x-scala"
765
+ });
766
+ </script>
767
+ </article>
codemirror/mode/clike/test.js ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // CodeMirror, copyright (c) by Marijn Haverbeke and others
2
+ // Distributed under an MIT license: http://codemirror.net/LICENSE
3
+
4
+ (function() {
5
+ var mode = CodeMirror.getMode({indentUnit: 2}, "text/x-c");
6
+ function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); }
7
+
8
+ MT("indent",
9
+ "[variable-3 void] [def foo]([variable-3 void*] [variable a], [variable-3 int] [variable b]) {",
10
+ " [variable-3 int] [variable c] [operator =] [variable b] [operator +]",
11
+ " [number 1];",
12
+ " [keyword return] [operator *][variable a];",
13
+ "}");
14
+
15
+ MT("indent_switch",
16
+ "[keyword switch] ([variable x]) {",
17
+ " [keyword case] [number 10]:",
18
+ " [keyword return] [number 20];",
19
+ " [keyword default]:",
20
+ " [variable printf]([string \"foo %c\"], [variable x]);",
21
+ "}");
22
+
23
+ MT("def",
24
+ "[variable-3 void] [def foo]() {}",
25
+ "[keyword struct] [def bar]{}",
26
+ "[variable-3 int] [variable-3 *][def baz]() {}");
27
+
28
+ MT("double_block",
29
+ "[keyword for] (;;)",
30
+ " [keyword for] (;;)",
31
+ " [variable x][operator ++];",
32
+ "[keyword return];");
33
+
34
+ MT("preprocessor",
35
+ "[meta #define FOO 3]",
36
+ "[variable-3 int] [variable foo];",
37
+ "[meta #define BAR\\]",
38
+ "[meta 4]",
39
+ "[variable-3 unsigned] [variable-3 int] [variable bar] [operator =] [number 8];",
40
+ "[meta #include <baz> ][comment // comment]")
41
+
42
+
43
+ var mode_cpp = CodeMirror.getMode({indentUnit: 2}, "text/x-c++src");
44
+ function MTCPP(name) { test.mode(name, mode_cpp, Array.prototype.slice.call(arguments, 1)); }
45
+
46
+ MTCPP("cpp14_literal",
47
+ "[number 10'000];",
48
+ "[number 0b10'000];",
49
+ "[number 0x10'000];",
50
+ "[string '100000'];");
51
+ })();
codemirror/mode/css/css.js ADDED
@@ -0,0 +1,825 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // CodeMirror, copyright (c) by Marijn Haverbeke and others
2
+ // Distributed under an MIT license: http://codemirror.net/LICENSE
3
+
4
+ (function(mod) {
5
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
6
+ mod(require("../../lib/codemirror"));
7
+ else if (typeof define == "function" && define.amd) // AMD
8
+ define(["../../lib/codemirror"], mod);
9
+ else // Plain browser env
10
+ mod(CodeMirror);
11
+ })(function(CodeMirror) {
12
+ "use strict";
13
+
14
+ CodeMirror.defineMode("css", function(config, parserConfig) {
15
+ var inline = parserConfig.inline
16
+ if (!parserConfig.propertyKeywords) parserConfig = CodeMirror.resolveMode("text/css");
17
+
18
+ var indentUnit = config.indentUnit,
19
+ tokenHooks = parserConfig.tokenHooks,
20
+ documentTypes = parserConfig.documentTypes || {},
21
+ mediaTypes = parserConfig.mediaTypes || {},
22
+ mediaFeatures = parserConfig.mediaFeatures || {},
23
+ mediaValueKeywords = parserConfig.mediaValueKeywords || {},
24
+ propertyKeywords = parserConfig.propertyKeywords || {},
25
+ nonStandardPropertyKeywords = parserConfig.nonStandardPropertyKeywords || {},
26
+ fontProperties = parserConfig.fontProperties || {},
27
+ counterDescriptors = parserConfig.counterDescriptors || {},
28
+ colorKeywords = parserConfig.colorKeywords || {},
29
+ valueKeywords = parserConfig.valueKeywords || {},
30
+ allowNested = parserConfig.allowNested,
31
+ supportsAtComponent = parserConfig.supportsAtComponent === true;
32
+
33
+ var type, override;
34
+ function ret(style, tp) { type = tp; return style; }
35
+
36
+ // Tokenizers
37
+
38
+ function tokenBase(stream, state) {
39
+ var ch = stream.next();
40
+ if (tokenHooks[ch]) {
41
+ var result = tokenHooks[ch](stream, state);
42
+ if (result !== false) return result;
43
+ }
44
+ if (ch == "@") {
45
+ stream.eatWhile(/[\w\\\-]/);
46
+ return ret("def", stream.current());
47
+ } else if (ch == "=" || (ch == "~" || ch == "|") && stream.eat("=")) {
48
+ return ret(null, "compare");
49
+ } else if (ch == "\"" || ch == "'") {
50
+ state.tokenize = tokenString(ch);
51
+ return state.tokenize(stream, state);
52
+ } else if (ch == "#") {
53
+ stream.eatWhile(/[\w\\\-]/);
54
+ return ret("atom", "hash");
55
+ } else if (ch == "!") {
56
+ stream.match(/^\s*\w*/);
57
+ return ret("keyword", "important");
58
+ } else if (/\d/.test(ch) || ch == "." && stream.eat(/\d/)) {
59
+ stream.eatWhile(/[\w.%]/);
60
+ return ret("number", "unit");
61
+ } else if (ch === "-") {
62
+ if (/[\d.]/.test(stream.peek())) {
63
+ stream.eatWhile(/[\w.%]/);
64
+ return ret("number", "unit");
65
+ } else if (stream.match(/^-[\w\\\-]+/)) {
66
+ stream.eatWhile(/[\w\\\-]/);
67
+ if (stream.match(/^\s*:/, false))
68
+ return ret("variable-2", "variable-definition");
69
+ return ret("variable-2", "variable");
70
+ } else if (stream.match(/^\w+-/)) {
71
+ return ret("meta", "meta");
72
+ }
73
+ } else if (/[,+>*\/]/.test(ch)) {
74
+ return ret(null, "select-op");
75
+ } else if (ch == "." && stream.match(/^-?[_a-z][_a-z0-9-]*/i)) {
76
+ return ret("qualifier", "qualifier");
77
+ } else if (/[:;{}\[\]\(\)]/.test(ch)) {
78
+ return ret(null, ch);
79
+ } else if ((ch == "u" && stream.match(/rl(-prefix)?\(/)) ||
80
+ (ch == "d" && stream.match("omain(")) ||
81
+ (ch == "r" && stream.match("egexp("))) {
82
+ stream.backUp(1);
83
+ state.tokenize = tokenParenthesized;
84
+ return ret("property", "word");
85
+ } else if (/[\w\\\-]/.test(ch)) {
86
+ stream.eatWhile(/[\w\\\-]/);
87
+ return ret("property", "word");
88
+ } else {
89
+ return ret(null, null);
90
+ }
91
+ }
92
+
93
+ function tokenString(quote) {
94
+ return function(stream, state) {
95
+ var escaped = false, ch;
96
+ while ((ch = stream.next()) != null) {
97
+ if (ch == quote && !escaped) {
98
+ if (quote == ")") stream.backUp(1);
99
+ break;
100
+ }
101
+ escaped = !escaped && ch == "\\";
102
+ }
103
+ if (ch == quote || !escaped && quote != ")") state.tokenize = null;
104
+ return ret("string", "string");
105
+ };
106
+ }
107
+
108
+ function tokenParenthesized(stream, state) {
109
+ stream.next(); // Must be '('
110
+ if (!stream.match(/\s*[\"\')]/, false))
111
+ state.tokenize = tokenString(")");
112
+ else
113
+ state.tokenize = null;
114
+ return ret(null, "(");
115
+ }
116
+
117
+ // Context management
118
+
119
+ function Context(type, indent, prev) {
120
+ this.type = type;
121
+ this.indent = indent;
122
+ this.prev = prev;
123
+ }
124
+
125
+ function pushContext(state, stream, type, indent) {
126
+ state.context = new Context(type, stream.indentation() + (indent === false ? 0 : indentUnit), state.context);
127
+ return type;
128
+ }
129
+
130
+ function popContext(state) {
131
+ if (state.context.prev)
132
+ state.context = state.context.prev;
133
+ return state.context.type;
134
+ }
135
+
136
+ function pass(type, stream, state) {
137
+ return states[state.context.type](type, stream, state);
138
+ }
139
+ function popAndPass(type, stream, state, n) {
140
+ for (var i = n || 1; i > 0; i--)
141
+ state.context = state.context.prev;
142
+ return pass(type, stream, state);
143
+ }
144
+
145
+ // Parser
146
+
147
+ function wordAsValue(stream) {
148
+ var word = stream.current().toLowerCase();
149
+ if (valueKeywords.hasOwnProperty(word))
150
+ override = "atom";
151
+ else if (colorKeywords.hasOwnProperty(word))
152
+ override = "keyword";
153
+ else
154
+ override = "variable";
155
+ }
156
+
157
+ var states = {};
158
+
159
+ states.top = function(type, stream, state) {
160
+ if (type == "{") {
161
+ return pushContext(state, stream, "block");
162
+ } else if (type == "}" && state.context.prev) {
163
+ return popContext(state);
164
+ } else if (supportsAtComponent && /@component/.test(type)) {
165
+ return pushContext(state, stream, "atComponentBlock");
166
+ } else if (/^@(-moz-)?document$/.test(type)) {
167
+ return pushContext(state, stream, "documentTypes");
168
+ } else if (/^@(media|supports|(-moz-)?document|import)$/.test(type)) {
169
+ return pushContext(state, stream, "atBlock");
170
+ } else if (/^@(font-face|counter-style)/.test(type)) {
171
+ state.stateArg = type;
172
+ return "restricted_atBlock_before";
173
+ } else if (/^@(-(moz|ms|o|webkit)-)?keyframes$/.test(type)) {
174
+ return "keyframes";
175
+ } else if (type && type.charAt(0) == "@") {
176
+ return pushContext(state, stream, "at");
177
+ } else if (type == "hash") {
178
+ override = "builtin";
179
+ } else if (type == "word") {
180
+ override = "tag";
181
+ } else if (type == "variable-definition") {
182
+ return "maybeprop";
183
+ } else if (type == "interpolation") {
184
+ return pushContext(state, stream, "interpolation");
185
+ } else if (type == ":") {
186
+ return "pseudo";
187
+ } else if (allowNested && type == "(") {
188
+ return pushContext(state, stream, "parens");
189
+ }
190
+ return state.context.type;
191
+ };
192
+
193
+ states.block = function(type, stream, state) {
194
+ if (type == "word") {
195
+ var word = stream.current().toLowerCase();
196
+ if (propertyKeywords.hasOwnProperty(word)) {
197
+ override = "property";
198
+ return "maybeprop";
199
+ } else if (nonStandardPropertyKeywords.hasOwnProperty(word)) {
200
+ override = "string-2";
201
+ return "maybeprop";
202
+ } else if (allowNested) {
203
+ override = stream.match(/^\s*:(?:\s|$)/, false) ? "property" : "tag";
204
+ return "block";
205
+ } else {
206
+ override += " error";
207
+ return "maybeprop";
208
+ }
209
+ } else if (type == "meta") {
210
+ return "block";
211
+ } else if (!allowNested && (type == "hash" || type == "qualifier")) {
212
+ override = "error";
213
+ return "block";
214
+ } else {
215
+ return states.top(type, stream, state);
216
+ }
217
+ };
218
+
219
+ states.maybeprop = function(type, stream, state) {
220
+ if (type == ":") return pushContext(state, stream, "prop");
221
+ return pass(type, stream, state);
222
+ };
223
+
224
+ states.prop = function(type, stream, state) {
225
+ if (type == ";") return popContext(state);
226
+ if (type == "{" && allowNested) return pushContext(state, stream, "propBlock");
227
+ if (type == "}" || type == "{") return popAndPass(type, stream, state);
228
+ if (type == "(") return pushContext(state, stream, "parens");
229
+
230
+ if (type == "hash" && !/^#([0-9a-fA-f]{3,4}|[0-9a-fA-f]{6}|[0-9a-fA-f]{8})$/.test(stream.current())) {
231
+ override += " error";
232
+ } else if (type == "word") {
233
+ wordAsValue(stream);
234
+ } else if (type == "interpolation") {
235
+ return pushContext(state, stream, "interpolation");
236
+ }
237
+ return "prop";
238
+ };
239
+
240
+ states.propBlock = function(type, _stream, state) {
241
+ if (type == "}") return popContext(state);
242
+ if (type == "word") { override = "property"; return "maybeprop"; }
243
+ return state.context.type;
244
+ };
245
+
246
+ states.parens = function(type, stream, state) {
247
+ if (type == "{" || type == "}") return popAndPass(type, stream, state);
248
+ if (type == ")") return popContext(state);
249
+ if (type == "(") return pushContext(state, stream, "parens");
250
+ if (type == "interpolation") return pushContext(state, stream, "interpolation");
251
+ if (type == "word") wordAsValue(stream);
252
+ return "parens";
253
+ };
254
+
255
+ states.pseudo = function(type, stream, state) {
256
+ if (type == "word") {
257
+ override = "variable-3";
258
+ return state.context.type;
259
+ }
260
+ return pass(type, stream, state);
261
+ };
262
+
263
+ states.documentTypes = function(type, stream, state) {
264
+ if (type == "word" && documentTypes.hasOwnProperty(stream.current())) {
265
+ override = "tag";
266
+ return state.context.type;
267
+ } else {
268
+ return states.atBlock(type, stream, state);
269
+ }
270
+ };
271
+
272
+ states.atBlock = function(type, stream, state) {
273
+ if (type == "(") return pushContext(state, stream, "atBlock_parens");
274
+ if (type == "}" || type == ";") return popAndPass(type, stream, state);
275
+ if (type == "{") return popContext(state) && pushContext(state, stream, allowNested ? "block" : "top");
276
+
277
+ if (type == "interpolation") return pushContext(state, stream, "interpolation");
278
+
279
+ if (type == "word") {
280
+ var word = stream.current().toLowerCase();
281
+ if (word == "only" || word == "not" || word == "and" || word == "or")
282
+ override = "keyword";
283
+ else if (mediaTypes.hasOwnProperty(word))
284
+ override = "attribute";
285
+ else if (mediaFeatures.hasOwnProperty(word))
286
+ override = "property";
287
+ else if (mediaValueKeywords.hasOwnProperty(word))
288
+ override = "keyword";
289
+ else if (propertyKeywords.hasOwnProperty(word))
290
+ override = "property";
291
+ else if (nonStandardPropertyKeywords.hasOwnProperty(word))
292
+ override = "string-2";
293
+ else if (valueKeywords.hasOwnProperty(word))
294
+ override = "atom";
295
+ else if (colorKeywords.hasOwnProperty(word))
296
+ override = "keyword";
297
+ else
298
+ override = "error";
299
+ }
300
+ return state.context.type;
301
+ };
302
+
303
+ states.atComponentBlock = function(type, stream, state) {
304
+ if (type == "}")
305
+ return popAndPass(type, stream, state);
306
+ if (type == "{")
307
+ return popContext(state) && pushContext(state, stream, allowNested ? "block" : "top", false);
308
+ if (type == "word")
309
+ override = "error";
310
+ return state.context.type;
311
+ };
312
+
313
+ states.atBlock_parens = function(type, stream, state) {
314
+ if (type == ")") return popContext(state);
315
+ if (type == "{" || type == "}") return popAndPass(type, stream, state, 2);
316
+ return states.atBlock(type, stream, state);
317
+ };
318
+
319
+ states.restricted_atBlock_before = function(type, stream, state) {
320
+ if (type == "{")
321
+ return pushContext(state, stream, "restricted_atBlock");
322
+ if (type == "word" && state.stateArg == "@counter-style") {
323
+ override = "variable";
324
+ return "restricted_atBlock_before";
325
+ }
326
+ return pass(type, stream, state);
327
+ };
328
+
329
+ states.restricted_atBlock = function(type, stream, state) {
330
+ if (type == "}") {
331
+ state.stateArg = null;
332
+ return popContext(state);
333
+ }
334
+ if (type == "word") {
335
+ if ((state.stateArg == "@font-face" && !fontProperties.hasOwnProperty(stream.current().toLowerCase())) ||
336
+ (state.stateArg == "@counter-style" && !counterDescriptors.hasOwnProperty(stream.current().toLowerCase())))
337
+ override = "error";
338
+ else
339
+ override = "property";
340
+ return "maybeprop";
341
+ }
342
+ return "restricted_atBlock";
343
+ };
344
+
345
+ states.keyframes = function(type, stream, state) {
346
+ if (type == "word") { override = "variable"; return "keyframes"; }
347
+ if (type == "{") return pushContext(state, stream, "top");
348
+ return pass(type, stream, state);
349
+ };
350
+
351
+ states.at = function(type, stream, state) {
352
+ if (type == ";") return popContext(state);
353
+ if (type == "{" || type == "}") return popAndPass(type, stream, state);
354
+ if (type == "word") override = "tag";
355
+ else if (type == "hash") override = "builtin";
356
+ return "at";
357
+ };
358
+
359
+ states.interpolation = function(type, stream, state) {
360
+ if (type == "}") return popContext(state);
361
+ if (type == "{" || type == ";") return popAndPass(type, stream, state);
362
+ if (type == "word") override = "variable";
363
+ else if (type != "variable" && type != "(" && type != ")") override = "error";
364
+ return "interpolation";
365
+ };
366
+
367
+ return {
368
+ startState: function(base) {
369
+ return {tokenize: null,
370
+ state: inline ? "block" : "top",
371
+ stateArg: null,
372
+ context: new Context(inline ? "block" : "top", base || 0, null)};
373
+ },
374
+
375
+ token: function(stream, state) {
376
+ if (!state.tokenize && stream.eatSpace()) return null;
377
+ var style = (state.tokenize || tokenBase)(stream, state);
378
+ if (style && typeof style == "object") {
379
+ type = style[1];
380
+ style = style[0];
381
+ }
382
+ override = style;
383
+ state.state = states[state.state](type, stream, state);
384
+ return override;
385
+ },
386
+
387
+ indent: function(state, textAfter) {
388
+ var cx = state.context, ch = textAfter && textAfter.charAt(0);
389
+ var indent = cx.indent;
390
+ if (cx.type == "prop" && (ch == "}" || ch == ")")) cx = cx.prev;
391
+ if (cx.prev) {
392
+ if (ch == "}" && (cx.type == "block" || cx.type == "top" ||
393
+ cx.type == "interpolation" || cx.type == "restricted_atBlock")) {
394
+ // Resume indentation from parent context.
395
+ cx = cx.prev;
396
+ indent = cx.indent;
397
+ } else if (ch == ")" && (cx.type == "parens" || cx.type == "atBlock_parens") ||
398
+ ch == "{" && (cx.type == "at" || cx.type == "atBlock")) {
399
+ // Dedent relative to current context.
400
+ indent = Math.max(0, cx.indent - indentUnit);
401
+ cx = cx.prev;
402
+ }
403
+ }
404
+ return indent;
405
+ },
406
+
407
+ electricChars: "}",
408
+ blockCommentStart: "/*",
409
+ blockCommentEnd: "*/",
410
+ fold: "brace"
411
+ };
412
+ });
413
+
414
+ function keySet(array) {
415
+ var keys = {};
416
+ for (var i = 0; i < array.length; ++i) {
417
+ keys[array[i]] = true;
418
+ }
419
+ return keys;
420
+ }
421
+
422
+ var documentTypes_ = [
423
+ "domain", "regexp", "url", "url-prefix"
424
+ ], documentTypes = keySet(documentTypes_);
425
+
426
+ var mediaTypes_ = [
427
+ "all", "aural", "braille", "handheld", "print", "projection", "screen",
428
+ "tty", "tv", "embossed"
429
+ ], mediaTypes = keySet(mediaTypes_);
430
+
431
+ var mediaFeatures_ = [
432
+ "width", "min-width", "max-width", "height", "min-height", "max-height",
433
+ "device-width", "min-device-width", "max-device-width", "device-height",
434
+ "min-device-height", "max-device-height", "aspect-ratio",
435
+ "min-aspect-ratio", "max-aspect-ratio", "device-aspect-ratio",
436
+ "min-device-aspect-ratio", "max-device-aspect-ratio", "color", "min-color",
437
+ "max-color", "color-index", "min-color-index", "max-color-index",
438
+ "monochrome", "min-monochrome", "max-monochrome", "resolution",
439
+ "min-resolution", "max-resolution", "scan", "grid", "orientation",
440
+ "device-pixel-ratio", "min-device-pixel-ratio", "max-device-pixel-ratio",
441
+ "pointer", "any-pointer", "hover", "any-hover"
442
+ ], mediaFeatures = keySet(mediaFeatures_);
443
+
444
+ var mediaValueKeywords_ = [
445
+ "landscape", "portrait", "none", "coarse", "fine", "on-demand", "hover",
446
+ "interlace", "progressive"
447
+ ], mediaValueKeywords = keySet(mediaValueKeywords_);
448
+
449
+ var propertyKeywords_ = [
450
+ "align-content", "align-items", "align-self", "alignment-adjust",
451
+ "alignment-baseline", "anchor-point", "animation", "animation-delay",
452
+ "animation-direction", "animation-duration", "animation-fill-mode",
453
+ "animation-iteration-count", "animation-name", "animation-play-state",
454
+ "animation-timing-function", "appearance", "azimuth", "backface-visibility",
455
+ "background", "background-attachment", "background-blend-mode", "background-clip",
456
+ "background-color", "background-image", "background-origin", "background-position",
457
+ "background-repeat", "background-size", "baseline-shift", "binding",
458
+ "bleed", "bookmark-label", "bookmark-level", "bookmark-state",
459
+ "bookmark-target", "border", "border-bottom", "border-bottom-color",
460
+ "border-bottom-left-radius", "border-bottom-right-radius",
461
+ "border-bottom-style", "border-bottom-width", "border-collapse",
462
+ "border-color", "border-image", "border-image-outset",
463
+ "border-image-repeat", "border-image-slice", "border-image-source",
464
+ "border-image-width", "border-left", "border-left-color",
465
+ "border-left-style", "border-left-width", "border-radius", "border-right",
466
+ "border-right-color", "border-right-style", "border-right-width",
467
+ "border-spacing", "border-style", "border-top", "border-top-color",
468
+ "border-top-left-radius", "border-top-right-radius", "border-top-style",
469
+ "border-top-width", "border-width", "bottom", "box-decoration-break",
470
+ "box-shadow", "box-sizing", "break-after", "break-before", "break-inside",
471
+ "caption-side", "clear", "clip", "color", "color-profile", "column-count",
472
+ "column-fill", "column-gap", "column-rule", "column-rule-color",
473
+ "column-rule-style", "column-rule-width", "column-span", "column-width",
474
+ "columns", "content", "counter-increment", "counter-reset", "crop", "cue",
475
+ "cue-after", "cue-before", "cursor", "direction", "display",
476
+ "dominant-baseline", "drop-initial-after-adjust",
477
+ "drop-initial-after-align", "drop-initial-before-adjust",
478
+ "drop-initial-before-align", "drop-initial-size", "drop-initial-value",
479
+ "elevation", "empty-cells", "fit", "fit-position", "flex", "flex-basis",
480
+ "flex-direction", "flex-flow", "flex-grow", "flex-shrink", "flex-wrap",
481
+ "float", "float-offset", "flow-from", "flow-into", "font", "font-feature-settings",
482
+ "font-family", "font-kerning", "font-language-override", "font-size", "font-size-adjust",
483
+ "font-stretch", "font-style", "font-synthesis", "font-variant",
484
+ "font-variant-alternates", "font-variant-caps", "font-variant-east-asian",
485
+ "font-variant-ligatures", "font-variant-numeric", "font-variant-position",
486
+ "font-weight", "grid", "grid-area", "grid-auto-columns", "grid-auto-flow",
487
+ "grid-auto-position", "grid-auto-rows", "grid-column", "grid-column-end",
488
+ "grid-column-start", "grid-row", "grid-row-end", "grid-row-start",
489
+ "grid-template", "grid-template-areas", "grid-template-columns",
490
+ "grid-template-rows", "hanging-punctuation", "height", "hyphens",
491
+ "icon", "image-orientation", "image-rendering", "image-resolution",
492
+ "inline-box-align", "justify-content", "left", "letter-spacing",
493
+ "line-break", "line-height", "line-stacking", "line-stacking-ruby",
494
+ "line-stacking-shift", "line-stacking-strategy", "list-style",
495
+ "list-style-image", "list-style-position", "list-style-type", "margin",
496
+ "margin-bottom", "margin-left", "margin-right", "margin-top",
497
+ "marker-offset", "marks", "marquee-direction", "marquee-loop",
498
+ "marquee-play-count", "marquee-speed", "marquee-style", "max-height",
499
+ "max-width", "min-height", "min-width", "move-to", "nav-down", "nav-index",
500
+ "nav-left", "nav-right", "nav-up", "object-fit", "object-position",
501
+ "opacity", "order", "orphans", "outline",
502
+ "outline-color", "outline-offset", "outline-style", "outline-width",
503
+ "overflow", "overflow-style", "overflow-wrap", "overflow-x", "overflow-y",
504
+ "padding", "padding-bottom", "padding-left", "padding-right", "padding-top",
505
+ "page", "page-break-after", "page-break-before", "page-break-inside",
506
+ "page-policy", "pause", "pause-after", "pause-before", "perspective",
507
+ "perspective-origin", "pitch", "pitch-range", "play-during", "position",
508
+ "presentation-level", "punctuation-trim", "quotes", "region-break-after",
509
+ "region-break-before", "region-break-inside", "region-fragment",
510
+ "rendering-intent", "resize", "rest", "rest-after", "rest-before", "richness",
511
+ "right", "rotation", "rotation-point", "ruby-align", "ruby-overhang",
512
+ "ruby-position", "ruby-span", "shape-image-threshold", "shape-inside", "shape-margin",
513
+ "shape-outside", "size", "speak", "speak-as", "speak-header",
514
+ "speak-numeral", "speak-punctuation", "speech-rate", "stress", "string-set",
515
+ "tab-size", "table-layout", "target", "target-name", "target-new",
516
+ "target-position", "text-align", "text-align-last", "text-decoration",
517
+ "text-decoration-color", "text-decoration-line", "text-decoration-skip",
518
+ "text-decoration-style", "text-emphasis", "text-emphasis-color",
519
+ "text-emphasis-position", "text-emphasis-style", "text-height",
520
+ "text-indent", "text-justify", "text-outline", "text-overflow", "text-shadow",
521
+ "text-size-adjust", "text-space-collapse", "text-transform", "text-underline-position",
522
+ "text-wrap", "top", "transform", "transform-origin", "transform-style",
523
+ "transition", "transition-delay", "transition-duration",
524
+ "transition-property", "transition-timing-function", "unicode-bidi",
525
+ "vertical-align", "visibility", "voice-balance", "voice-duration",
526
+ "voice-family", "voice-pitch", "voice-range", "voice-rate", "voice-stress",
527
+ "voice-volume", "volume", "white-space", "widows", "width", "word-break",
528
+ "word-spacing", "word-wrap", "z-index",
529
+ // SVG-specific
530
+ "clip-path", "clip-rule", "mask", "enable-background", "filter", "flood-color",
531
+ "flood-opacity", "lighting-color", "stop-color", "stop-opacity", "pointer-events",
532
+ "color-interpolation", "color-interpolation-filters",
533
+ "color-rendering", "fill", "fill-opacity", "fill-rule", "image-rendering",
534
+ "marker", "marker-end", "marker-mid", "marker-start", "shape-rendering", "stroke",
535
+ "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin",
536
+ "stroke-miterlimit", "stroke-opacity", "stroke-width", "text-rendering",
537
+ "baseline-shift", "dominant-baseline", "glyph-orientation-horizontal",
538
+ "glyph-orientation-vertical", "text-anchor", "writing-mode"
539
+ ], propertyKeywords = keySet(propertyKeywords_);
540
+
541
+ var nonStandardPropertyKeywords_ = [
542
+ "scrollbar-arrow-color", "scrollbar-base-color", "scrollbar-dark-shadow-color",
543
+ "scrollbar-face-color", "scrollbar-highlight-color", "scrollbar-shadow-color",
544
+ "scrollbar-3d-light-color", "scrollbar-track-color", "shape-inside",
545
+ "searchfield-cancel-button", "searchfield-decoration", "searchfield-results-button",
546
+ "searchfield-results-decoration", "zoom"
547
+ ], nonStandardPropertyKeywords = keySet(nonStandardPropertyKeywords_);
548
+
549
+ var fontProperties_ = [
550
+ "font-family", "src", "unicode-range", "font-variant", "font-feature-settings",
551
+ "font-stretch", "font-weight", "font-style"
552
+ ], fontProperties = keySet(fontProperties_);
553
+
554
+ var counterDescriptors_ = [
555
+ "additive-symbols", "fallback", "negative", "pad", "prefix", "range",
556
+ "speak-as", "suffix", "symbols", "system"
557
+ ], counterDescriptors = keySet(counterDescriptors_);
558
+
559
+ var colorKeywords_ = [
560
+ "aliceblue", "antiquewhite", "aqua", "aquamarine", "azure", "beige",
561
+ "bisque", "black", "blanchedalmond", "blue", "blueviolet", "brown",
562
+ "burlywood", "cadetblue", "chartreuse", "chocolate", "coral", "cornflowerblue",
563
+ "cornsilk", "crimson", "cyan", "darkblue", "darkcyan", "darkgoldenrod",
564
+ "darkgray", "darkgreen", "darkkhaki", "darkmagenta", "darkolivegreen",
565
+ "darkorange", "darkorchid", "darkred", "darksalmon", "darkseagreen",
566
+ "darkslateblue", "darkslategray", "darkturquoise", "darkviolet",
567
+ "deeppink", "deepskyblue", "dimgray", "dodgerblue", "firebrick",
568
+ "floralwhite", "forestgreen", "fuchsia", "gainsboro", "ghostwhite",
569
+ "gold", "goldenrod", "gray", "grey", "green", "greenyellow", "honeydew",
570
+ "hotpink", "indianred", "indigo", "ivory", "khaki", "lavender",
571
+ "lavenderblush", "lawngreen", "lemonchiffon", "lightblue", "lightcoral",
572
+ "lightcyan", "lightgoldenrodyellow", "lightgray", "lightgreen", "lightpink",
573
+ "lightsalmon", "lightseagreen", "lightskyblue", "lightslategray",
574
+ "lightsteelblue", "lightyellow", "lime", "limegreen", "linen", "magenta",
575
+ "maroon", "mediumaquamarine", "mediumblue", "mediumorchid", "mediumpurple",
576
+ "mediumseagreen", "mediumslateblue", "mediumspringgreen", "mediumturquoise",
577
+ "mediumvioletred", "midnightblue", "mintcream", "mistyrose", "moccasin",
578
+ "navajowhite", "navy", "oldlace", "olive", "olivedrab", "orange", "orangered",
579
+ "orchid", "palegoldenrod", "palegreen", "paleturquoise", "palevioletred",
580
+ "papayawhip", "peachpuff", "peru", "pink", "plum", "powderblue",
581
+ "purple", "rebeccapurple", "red", "rosybrown", "royalblue", "saddlebrown",
582
+ "salmon", "sandybrown", "seagreen", "seashell", "sienna", "silver", "skyblue",
583
+ "slateblue", "slategray", "snow", "springgreen", "steelblue", "tan",
584
+ "teal", "thistle", "tomato", "turquoise", "violet", "wheat", "white",
585
+ "whitesmoke", "yellow", "yellowgreen"
586
+ ], colorKeywords = keySet(colorKeywords_);
587
+
588
+ var valueKeywords_ = [
589
+ "above", "absolute", "activeborder", "additive", "activecaption", "afar",
590
+ "after-white-space", "ahead", "alias", "all", "all-scroll", "alphabetic", "alternate",
591
+ "always", "amharic", "amharic-abegede", "antialiased", "appworkspace",
592
+ "arabic-indic", "armenian", "asterisks", "attr", "auto", "avoid", "avoid-column", "avoid-page",
593
+ "avoid-region", "background", "backwards", "baseline", "below", "bidi-override", "binary",
594
+ "bengali", "blink", "block", "block-axis", "bold", "bolder", "border", "border-box",
595
+ "both", "bottom", "break", "break-all", "break-word", "bullets", "button", "button-bevel",
596
+ "buttonface", "buttonhighlight", "buttonshadow", "buttontext", "calc", "cambodian",
597
+ "capitalize", "caps-lock-indicator", "caption", "captiontext", "caret",
598
+ "cell", "center", "checkbox", "circle", "cjk-decimal", "cjk-earthly-branch",
599
+ "cjk-heavenly-stem", "cjk-ideographic", "clear", "clip", "close-quote",
600
+ "col-resize", "collapse", "color", "color-burn", "color-dodge", "column", "column-reverse",
601
+ "compact", "condensed", "contain", "content",
602
+ "content-box", "context-menu", "continuous", "copy", "counter", "counters", "cover", "crop",
603
+ "cross", "crosshair", "currentcolor", "cursive", "cyclic", "darken", "dashed", "decimal",
604
+ "decimal-leading-zero", "default", "default-button", "destination-atop",
605
+ "destination-in", "destination-out", "destination-over", "devanagari", "difference",
606
+ "disc", "discard", "disclosure-closed", "disclosure-open", "document",
607
+ "dot-dash", "dot-dot-dash",
608
+ "dotted", "double", "down", "e-resize", "ease", "ease-in", "ease-in-out", "ease-out",
609
+ "element", "ellipse", "ellipsis", "embed", "end", "ethiopic", "ethiopic-abegede",
610
+ "ethiopic-abegede-am-et", "ethiopic-abegede-gez", "ethiopic-abegede-ti-er",
611
+ "ethiopic-abegede-ti-et", "ethiopic-halehame-aa-er",
612
+ "ethiopic-halehame-aa-et", "ethiopic-halehame-am-et",
613
+ "ethiopic-halehame-gez", "ethiopic-halehame-om-et",
614
+ "ethiopic-halehame-sid-et", "ethiopic-halehame-so-et",
615
+ "ethiopic-halehame-ti-er", "ethiopic-halehame-ti-et", "ethiopic-halehame-tig",
616
+ "ethiopic-numeric", "ew-resize", "exclusion", "expanded", "extends", "extra-condensed",
617
+ "extra-expanded", "fantasy", "fast", "fill", "fixed", "flat", "flex", "flex-end", "flex-start", "footnotes",
618
+ "forwards", "from", "geometricPrecision", "georgian", "graytext", "groove",
619
+ "gujarati", "gurmukhi", "hand", "hangul", "hangul-consonant", "hard-light", "hebrew",
620
+ "help", "hidden", "hide", "higher", "highlight", "highlighttext",
621
+ "hiragana", "hiragana-iroha", "horizontal", "hsl", "hsla", "hue", "icon", "ignore",
622
+ "inactiveborder", "inactivecaption", "inactivecaptiontext", "infinite",
623
+ "infobackground", "infotext", "inherit", "initial", "inline", "inline-axis",
624
+ "inline-block", "inline-flex", "inline-table", "inset", "inside", "intrinsic", "invert",
625
+ "italic", "japanese-formal", "japanese-informal", "justify", "kannada",
626
+ "katakana", "katakana-iroha", "keep-all", "khmer",
627
+ "korean-hangul-formal", "korean-hanja-formal", "korean-hanja-informal",
628
+ "landscape", "lao", "large", "larger", "left", "level", "lighter", "lighten",
629
+ "line-through", "linear", "linear-gradient", "lines", "list-item", "listbox", "listitem",
630
+ "local", "logical", "loud", "lower", "lower-alpha", "lower-armenian",
631
+ "lower-greek", "lower-hexadecimal", "lower-latin", "lower-norwegian",
632
+ "lower-roman", "lowercase", "ltr", "luminosity", "malayalam", "match", "matrix", "matrix3d",
633
+ "media-controls-background", "media-current-time-display",
634
+ "media-fullscreen-button", "media-mute-button", "media-play-button",
635
+ "media-return-to-realtime-button", "media-rewind-button",
636
+ "media-seek-back-button", "media-seek-forward-button", "media-slider",
637
+ "media-sliderthumb", "media-time-remaining-display", "media-volume-slider",
638
+ "media-volume-slider-container", "media-volume-sliderthumb", "medium",
639
+ "menu", "menulist", "menulist-button", "menulist-text",
640
+ "menulist-textfield", "menutext", "message-box", "middle", "min-intrinsic",
641
+ "mix", "mongolian", "monospace", "move", "multiple", "multiply", "myanmar", "n-resize",
642
+ "narrower", "ne-resize", "nesw-resize", "no-close-quote", "no-drop",
643
+ "no-open-quote", "no-repeat", "none", "normal", "not-allowed", "nowrap",
644
+ "ns-resize", "numbers", "numeric", "nw-resize", "nwse-resize", "oblique", "octal", "open-quote",
645
+ "optimizeLegibility", "optimizeSpeed", "oriya", "oromo", "outset",
646
+ "outside", "outside-shape", "overlay", "overline", "padding", "padding-box",
647
+ "painted", "page", "paused", "persian", "perspective", "plus-darker", "plus-lighter",
648
+ "pointer", "polygon", "portrait", "pre", "pre-line", "pre-wrap", "preserve-3d",
649
+ "progress", "push-button", "radial-gradient", "radio", "read-only",
650
+ "read-write", "read-write-plaintext-only", "rectangle", "region",
651
+ "relative", "repeat", "repeating-linear-gradient",
652
+ "repeating-radial-gradient", "repeat-x", "repeat-y", "reset", "reverse",
653
+ "rgb", "rgba", "ridge", "right", "rotate", "rotate3d", "rotateX", "rotateY",
654
+ "rotateZ", "round", "row", "row-resize", "row-reverse", "rtl", "run-in", "running",
655
+ "s-resize", "sans-serif", "saturation", "scale", "scale3d", "scaleX", "scaleY", "scaleZ", "screen",
656
+ "scroll", "scrollbar", "se-resize", "searchfield",
657
+ "searchfield-cancel-button", "searchfield-decoration",
658
+ "searchfield-results-button", "searchfield-results-decoration",
659
+ "semi-condensed", "semi-expanded", "separate", "serif", "show", "sidama",
660
+ "simp-chinese-formal", "simp-chinese-informal", "single",
661
+ "skew", "skewX", "skewY", "skip-white-space", "slide", "slider-horizontal",
662
+ "slider-vertical", "sliderthumb-horizontal", "sliderthumb-vertical", "slow",
663
+ "small", "small-caps", "small-caption", "smaller", "soft-light", "solid", "somali",
664
+ "source-atop", "source-in", "source-out", "source-over", "space", "space-around", "space-between", "spell-out", "square",
665
+ "square-button", "start", "static", "status-bar", "stretch", "stroke", "sub",
666
+ "subpixel-antialiased", "super", "sw-resize", "symbolic", "symbols", "table",
667
+ "table-caption", "table-cell", "table-column", "table-column-group",
668
+ "table-footer-group", "table-header-group", "table-row", "table-row-group",
669
+ "tamil",
670
+ "telugu", "text", "text-bottom", "text-top", "textarea", "textfield", "thai",
671
+ "thick", "thin", "threeddarkshadow", "threedface", "threedhighlight",
672
+ "threedlightshadow", "threedshadow", "tibetan", "tigre", "tigrinya-er",
673
+ "tigrinya-er-abegede", "tigrinya-et", "tigrinya-et-abegede", "to", "top",
674
+ "trad-chinese-formal", "trad-chinese-informal",
675
+ "translate", "translate3d", "translateX", "translateY", "translateZ",
676
+ "transparent", "ultra-condensed", "ultra-expanded", "underline", "up",
677
+ "upper-alpha", "upper-armenian", "upper-greek", "upper-hexadecimal",
678
+ "upper-latin", "upper-norwegian", "upper-roman", "uppercase", "urdu", "url",
679
+ "var", "vertical", "vertical-text", "visible", "visibleFill", "visiblePainted",
680
+ "visibleStroke", "visual", "w-resize", "wait", "wave", "wider",
681
+ "window", "windowframe", "windowtext", "words", "wrap", "wrap-reverse", "x-large", "x-small", "xor",
682
+ "xx-large", "xx-small"
683
+ ], valueKeywords = keySet(valueKeywords_);
684
+
685
+ var allWords = documentTypes_.concat(mediaTypes_).concat(mediaFeatures_).concat(mediaValueKeywords_)
686
+ .concat(propertyKeywords_).concat(nonStandardPropertyKeywords_).concat(colorKeywords_)
687
+ .concat(valueKeywords_);
688
+ CodeMirror.registerHelper("hintWords", "css", allWords);
689
+
690
+ function tokenCComment(stream, state) {
691
+ var maybeEnd = false, ch;
692
+ while ((ch = stream.next()) != null) {
693
+ if (maybeEnd && ch == "/") {
694
+ state.tokenize = null;
695
+ break;
696
+ }
697
+ maybeEnd = (ch == "*");
698
+ }
699
+ return ["comment", "comment"];
700
+ }
701
+
702
+ CodeMirror.defineMIME("text/css", {
703
+ documentTypes: documentTypes,
704
+ mediaTypes: mediaTypes,
705
+ mediaFeatures: mediaFeatures,
706
+ mediaValueKeywords: mediaValueKeywords,
707
+ propertyKeywords: propertyKeywords,
708
+ nonStandardPropertyKeywords: nonStandardPropertyKeywords,
709
+ fontProperties: fontProperties,
710
+ counterDescriptors: counterDescriptors,
711
+ colorKeywords: colorKeywords,
712
+ valueKeywords: valueKeywords,
713
+ tokenHooks: {
714
+ "/": function(stream, state) {
715
+ if (!stream.eat("*")) return false;
716
+ state.tokenize = tokenCComment;
717
+ return tokenCComment(stream, state);
718
+ }
719
+ },
720
+ name: "css"
721
+ });
722
+
723
+ CodeMirror.defineMIME("text/x-scss", {
724
+ mediaTypes: mediaTypes,
725
+ mediaFeatures: mediaFeatures,
726
+ mediaValueKeywords: mediaValueKeywords,
727
+ propertyKeywords: propertyKeywords,
728
+ nonStandardPropertyKeywords: nonStandardPropertyKeywords,
729
+ colorKeywords: colorKeywords,
730
+ valueKeywords: valueKeywords,
731
+ fontProperties: fontProperties,
732
+ allowNested: true,
733
+ tokenHooks: {
734
+ "/": function(stream, state) {
735
+ if (stream.eat("/")) {
736
+ stream.skipToEnd();
737
+ return ["comment", "comment"];
738
+ } else if (stream.eat("*")) {
739
+ state.tokenize = tokenCComment;
740
+ return tokenCComment(stream, state);
741
+ } else {
742
+ return ["operator", "operator"];
743
+ }
744
+ },
745
+ ":": function(stream) {
746
+ if (stream.match(/\s*\{/))
747
+ return [null, "{"];
748
+ return false;
749
+ },
750
+ "$": function(stream) {
751
+ stream.match(/^[\w-]+/);
752
+ if (stream.match(/^\s*:/, false))
753
+ return ["variable-2", "variable-definition"];
754
+ return ["variable-2", "variable"];
755
+ },
756
+ "#": function(stream) {
757
+ if (!stream.eat("{")) return false;
758
+ return [null, "interpolation"];
759
+ }
760
+ },
761
+ name: "css",
762
+ helperType: "scss"
763
+ });
764
+
765
+ CodeMirror.defineMIME("text/x-less", {
766
+ mediaTypes: mediaTypes,
767
+ mediaFeatures: mediaFeatures,
768
+ mediaValueKeywords: mediaValueKeywords,
769
+ propertyKeywords: propertyKeywords,
770
+ nonStandardPropertyKeywords: nonStandardPropertyKeywords,
771
+ colorKeywords: colorKeywords,
772
+ valueKeywords: valueKeywords,
773
+ fontProperties: fontProperties,
774
+ allowNested: true,
775
+ tokenHooks: {
776
+ "/": function(stream, state) {
777
+ if (stream.eat("/")) {
778
+ stream.skipToEnd();
779
+ return ["comment", "comment"];
780
+ } else if (stream.eat("*")) {
781
+ state.tokenize = tokenCComment;
782
+ return tokenCComment(stream, state);
783
+ } else {
784
+ return ["operator", "operator"];
785
+ }
786
+ },
787
+ "@": function(stream) {
788
+ if (stream.eat("{")) return [null, "interpolation"];
789
+ if (stream.match(/^(charset|document|font-face|import|(-(moz|ms|o|webkit)-)?keyframes|media|namespace|page|supports)\b/, false)) return false;
790
+ stream.eatWhile(/[\w\\\-]/);
791
+ if (stream.match(/^\s*:/, false))
792
+ return ["variable-2", "variable-definition"];
793
+ return ["variable-2", "variable"];
794
+ },
795
+ "&": function() {
796
+ return ["atom", "atom"];
797
+ }
798
+ },
799
+ name: "css",
800
+ helperType: "less"
801
+ });
802
+
803
+ CodeMirror.defineMIME("text/x-gss", {
804
+ documentTypes: documentTypes,
805
+ mediaTypes: mediaTypes,
806
+ mediaFeatures: mediaFeatures,
807
+ propertyKeywords: propertyKeywords,
808
+ nonStandardPropertyKeywords: nonStandardPropertyKeywords,
809
+ fontProperties: fontProperties,
810
+ counterDescriptors: counterDescriptors,
811
+ colorKeywords: colorKeywords,
812
+ valueKeywords: valueKeywords,
813
+ supportsAtComponent: true,
814
+ tokenHooks: {
815
+ "/": function(stream, state) {
816
+ if (!stream.eat("*")) return false;
817
+ state.tokenize = tokenCComment;
818
+ return tokenCComment(stream, state);
819
+ }
820
+ },
821
+ name: "css",
822
+ helperType: "gss"
823
+ });
824
+
825
+ });
codemirror/mode/css/gss.html ADDED
@@ -0,0 +1,103 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!doctype html>
2
+
3
+ <title>CodeMirror: Closure Stylesheets (GSS) mode</title>
4
+ <meta charset="utf-8"/>
5
+ <link rel=stylesheet href="../../doc/docs.css">
6
+
7
+ <link rel="stylesheet" href="../../lib/codemirror.css">
8
+ <link rel="stylesheet" href="../../addon/hint/show-hint.css">
9
+ <script src="../../lib/codemirror.js"></script>
10
+ <script src="css.js"></script>
11
+ <script src="../../addon/hint/show-hint.js"></script>
12
+ <script src="../../addon/hint/css-hint.js"></script>
13
+ <style>.CodeMirror {background: #f8f8f8;}</style>
14
+ <div id=nav>
15
+ <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
16
+
17
+ <ul>
18
+ <li><a href="../../index.html">Home</a>
19
+ <li><a href="../../doc/manual.html">Manual</a>
20
+ <li><a href="https://github.com/codemirror/codemirror">Code</a>
21
+ </ul>
22
+ <ul>
23
+ <li><a href="../index.html">Language modes</a>
24
+ <li><a class=active href="#">Closure Stylesheets (GSS)</a>
25
+ </ul>
26
+ </div>
27
+
28
+ <article>
29
+ <h2>Closure Stylesheets (GSS) mode</h2>
30
+ <form><textarea id="code" name="code">
31
+ /* Some example Closure Stylesheets */
32
+
33
+ @provide 'some.styles';
34
+
35
+ @require 'other.styles';
36
+
37
+ @component {
38
+
39
+ @def FONT_FAMILY "Times New Roman", Georgia, Serif;
40
+ @def FONT_SIZE_NORMAL 15px;
41
+ @def FONT_NORMAL normal FONT_SIZE_NORMAL FONT_FAMILY;
42
+
43
+ @def BG_COLOR rgb(235, 239, 249);
44
+
45
+ @def DIALOG_BORDER_COLOR rgb(107, 144, 218);
46
+ @def DIALOG_BG_COLOR BG_COLOR;
47
+
48
+ @def LEFT_HAND_NAV_WIDTH 180px;
49
+ @def LEFT_HAND_NAV_PADDING 3px;
50
+
51
+ @defmixin size(WIDTH, HEIGHT) {
52
+ width: WIDTH;
53
+ height: HEIGHT;
54
+ }
55
+
56
+ body {
57
+ background-color: BG_COLOR;
58
+ margin: 0;
59
+ padding: 3em 6em;
60
+ font: FONT_NORMAL;
61
+ color: #000;
62
+ }
63
+
64
+ #navigation a {
65
+ font-weight: bold;
66
+ text-decoration: none !important;
67
+ }
68
+
69
+ .dialog {
70
+ background-color: DIALOG_BG_COLOR;
71
+ border: 1px solid DIALOG_BORDER_COLOR;
72
+ }
73
+
74
+ .content {
75
+ position: absolute;
76
+ margin-left: add(LEFT_HAND_NAV_PADDING, /* padding left */
77
+ LEFT_HAND_NAV_WIDTH,
78
+ LEFT_HAND_NAV_PADDING); /* padding right */
79
+
80
+ }
81
+
82
+ .logo {
83
+ @mixin size(150px, 55px);
84
+ background-image: url('http://www.google.com/images/logo_sm.gif');
85
+ }
86
+
87
+ }
88
+ </textarea></form>
89
+ <script>
90
+ var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
91
+ extraKeys: {"Ctrl-Space": "autocomplete"},
92
+ lineNumbers: true,
93
+ matchBrackets: "text/x-less",
94
+ mode: "text/x-gss"
95
+ });
96
+ </script>
97
+
98
+ <p>A mode for <a href="https://github.com/google/closure-stylesheets">Closure Stylesheets</a> (GSS).</p>
99
+ <p><strong>MIME type defined:</strong> <code>text/x-gss</code>.</p>
100
+
101
+ <p><strong>Parsing/Highlighting Tests:</strong> <a href="../../test/index.html#gss_*">normal</a>, <a href="../../test/index.html#verbose,gss_*">verbose</a>.</p>
102
+
103
+ </article>
codemirror/mode/css/gss_test.js ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // CodeMirror, copyright (c) by Marijn Haverbeke and others
2
+ // Distributed under an MIT license: http://codemirror.net/LICENSE
3
+
4
+ (function() {
5
+ "use strict";
6
+
7
+ var mode = CodeMirror.getMode({indentUnit: 2}, "text/x-gss");
8
+ function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1), "gss"); }
9
+
10
+ MT("atComponent",
11
+ "[def @component] {",
12
+ "[tag foo] {",
13
+ " [property color]: [keyword black];",
14
+ "}",
15
+ "}");
16
+
17
+ })();
codemirror/mode/css/index.html ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!doctype html>
2
+
3
+ <title>CodeMirror: CSS mode</title>
4
+ <meta charset="utf-8"/>
5
+ <link rel=stylesheet href="../../doc/docs.css">
6
+
7
+ <link rel="stylesheet" href="../../lib/codemirror.css">
8
+ <link rel="stylesheet" href="../../addon/hint/show-hint.css">
9
+ <script src="../../lib/codemirror.js"></script>
10
+ <script src="css.js"></script>
11
+ <script src="../../addon/hint/show-hint.js"></script>
12
+ <script src="../../addon/hint/css-hint.js"></script>
13
+ <style>.CodeMirror {background: #f8f8f8;}</style>
14
+ <div id=nav>
15
+ <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
16
+
17
+ <ul>
18
+ <li><a href="../../index.html">Home</a>
19
+ <li><a href="../../doc/manual.html">Manual</a>
20
+ <li><a href="https://github.com/codemirror/codemirror">Code</a>
21
+ </ul>
22
+ <ul>
23
+ <li><a href="../index.html">Language modes</a>
24
+ <li><a class=active href="#">CSS</a>
25
+ </ul>
26
+ </div>
27
+
28
+ <article>
29
+ <h2>CSS mode</h2>
30
+ <form><textarea id="code" name="code">
31
+ /* Some example CSS */
32
+
33
+ @import url("something.css");
34
+
35
+ body {
36
+ margin: 0;
37
+ padding: 3em 6em;
38
+ font-family: tahoma, arial, sans-serif;
39
+ color: #000;
40
+ }
41
+
42
+ #navigation a {
43
+ font-weight: bold;
44
+ text-decoration: none !important;
45
+ }
46
+
47
+ h1 {
48
+ font-size: 2.5em;
49
+ }
50
+
51
+ h2 {
52
+ font-size: 1.7em;
53
+ }
54
+
55
+ h1:before, h2:before {
56
+ content: "::";
57
+ }
58
+
59
+ code {
60
+ font-family: courier, monospace;
61
+ font-size: 80%;
62
+ color: #418A8A;
63
+ }
64
+ </textarea></form>
65
+ <script>
66
+ var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
67
+ extraKeys: {"Ctrl-Space": "autocomplete"},
68
+ });
69
+ </script>
70
+
71
+ <p><strong>MIME types defined:</strong> <code>text/css</code>, <code>text/x-scss</code> (<a href="scss.html">demo</a>), <code>text/x-less</code> (<a href="less.html">demo</a>).</p>
72
+
73
+ <p><strong>Parsing/Highlighting Tests:</strong> <a href="../../test/index.html#css_*">normal</a>, <a href="../../test/index.html#verbose,css_*">verbose</a>.</p>
74
+
75
+ </article>
codemirror/mode/css/less.html ADDED
@@ -0,0 +1,152 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!doctype html>
2
+
3
+ <title>CodeMirror: LESS mode</title>
4
+ <meta charset="utf-8"/>
5
+ <link rel=stylesheet href="../../doc/docs.css">
6
+
7
+ <link rel="stylesheet" href="../../lib/codemirror.css">
8
+ <script src="../../lib/codemirror.js"></script>
9
+ <script src="../../addon/edit/matchbrackets.js"></script>
10
+ <script src="css.js"></script>
11
+ <style>.CodeMirror {border: 1px solid #ddd; line-height: 1.2;}</style>
12
+ <div id=nav>
13
+ <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
14
+
15
+ <ul>
16
+ <li><a href="../../index.html">Home</a>
17
+ <li><a href="../../doc/manual.html">Manual</a>
18
+ <li><a href="https://github.com/codemirror/codemirror">Code</a>
19
+ </ul>
20
+ <ul>
21
+ <li><a href="../index.html">Language modes</a>
22
+ <li><a class=active href="#">LESS</a>
23
+ </ul>
24
+ </div>
25
+
26
+ <article>
27
+ <h2>LESS mode</h2>
28
+ <form><textarea id="code" name="code">@media screen and (device-aspect-ratio: 16/9) { … }
29
+ @media screen and (device-aspect-ratio: 1280/720) { … }
30
+ @media screen and (device-aspect-ratio: 2560/1440) { … }
31
+
32
+ html:lang(fr-be)
33
+
34
+ tr:nth-child(2n+1) /* represents every odd row of an HTML table */
35
+
36
+ img:nth-of-type(2n+1) { float: right; }
37
+ img:nth-of-type(2n) { float: left; }
38
+
39
+ body > h2:not(:first-of-type):not(:last-of-type)
40
+
41
+ html|*:not(:link):not(:visited)
42
+ *|*:not(:hover)
43
+ p::first-line { text-transform: uppercase }
44
+
45
+ @namespace foo url(http://www.example.com);
46
+ foo|h1 { color: blue } /* first rule */
47
+
48
+ span[hello="Ocean"][goodbye="Land"]
49
+
50
+ E[foo]{
51
+ padding:65px;
52
+ }
53
+
54
+ input[type="search"]::-webkit-search-decoration,
55
+ input[type="search"]::-webkit-search-cancel-button {
56
+ -webkit-appearance: none; // Inner-padding issues in Chrome OSX, Safari 5
57
+ }
58
+ button::-moz-focus-inner,
59
+ input::-moz-focus-inner { // Inner padding and border oddities in FF3/4
60
+ padding: 0;
61
+ border: 0;
62
+ }
63
+ .btn {
64
+ // reset here as of 2.0.3 due to Recess property order
65
+ border-color: #ccc;
66
+ border-color: rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);
67
+ }
68
+ fieldset span button, fieldset span input[type="file"] {
69
+ font-size:12px;
70
+ font-family:Arial, Helvetica, sans-serif;
71
+ }
72
+
73
+ .rounded-corners (@radius: 5px) {
74
+ border-radius: @radius;
75
+ -webkit-border-radius: @radius;
76
+ -moz-border-radius: @radius;
77
+ }
78
+
79
+ @import url("something.css");
80
+
81
+ @light-blue: hsl(190, 50%, 65%);
82
+
83
+ #menu {
84
+ position: absolute;
85
+ width: 100%;
86
+ z-index: 3;
87
+ clear: both;
88
+ display: block;
89
+ background-color: @blue;
90
+ height: 42px;
91
+ border-top: 2px solid lighten(@alpha-blue, 20%);
92
+ border-bottom: 2px solid darken(@alpha-blue, 25%);
93
+ .box-shadow(0, 1px, 8px, 0.6);
94
+ -moz-box-shadow: 0 0 0 #000; // Because firefox sucks.
95
+
96
+ &.docked {
97
+ background-color: hsla(210, 60%, 40%, 0.4);
98
+ }
99
+ &:hover {
100
+ background-color: @blue;
101
+ }
102
+
103
+ #dropdown {
104
+ margin: 0 0 0 117px;
105
+ padding: 0;
106
+ padding-top: 5px;
107
+ display: none;
108
+ width: 190px;
109
+ border-top: 2px solid @medium;
110
+ color: @highlight;
111
+ border: 2px solid darken(@medium, 25%);
112
+ border-left-color: darken(@medium, 15%);
113
+ border-right-color: darken(@medium, 15%);
114
+ border-top-width: 0;
115
+ background-color: darken(@medium, 10%);
116
+ ul {
117
+ padding: 0px;
118
+ }
119
+ li {
120
+ font-size: 14px;
121
+ display: block;
122
+ text-align: left;
123
+ padding: 0;
124
+ border: 0;
125
+ a {
126
+ display: block;
127
+ padding: 0px 15px;
128
+ text-decoration: none;
129
+ color: white;
130
+ &:hover {
131
+ background-color: darken(@medium, 15%);
132
+ text-decoration: none;
133
+ }
134
+ }
135
+ }
136
+ .border-radius(5px, bottom);
137
+ .box-shadow(0, 6px, 8px, 0.5);
138
+ }
139
+ }
140
+ </textarea></form>
141
+ <script>
142
+ var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
143
+ lineNumbers : true,
144
+ matchBrackets : true,
145
+ mode: "text/x-less"
146
+ });
147
+ </script>
148
+
149
+ <p>The LESS mode is a sub-mode of the <a href="index.html">CSS mode</a> (defined in <code>css.js</code>).</p>
150
+
151
+ <p><strong>Parsing/Highlighting Tests:</strong> <a href="../../test/index.html#less_*">normal</a>, <a href="../../test/index.html#verbose,less_*">verbose</a>.</p>
152
+ </article>
codemirror/mode/css/less_test.js ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // CodeMirror, copyright (c) by Marijn Haverbeke and others
2
+ // Distributed under an MIT license: http://codemirror.net/LICENSE
3
+
4
+ (function() {
5
+ "use strict";
6
+
7
+ var mode = CodeMirror.getMode({indentUnit: 2}, "text/x-less");
8
+ function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1), "less"); }
9
+
10
+ MT("variable",
11
+ "[variable-2 @base]: [atom #f04615];",
12
+ "[qualifier .class] {",
13
+ " [property width]: [variable percentage]([number 0.5]); [comment // returns `50%`]",
14
+ " [property color]: [variable saturate]([variable-2 @base], [number 5%]);",
15
+ "}");
16
+
17
+ MT("amp",
18
+ "[qualifier .child], [qualifier .sibling] {",
19
+ " [qualifier .parent] [atom &] {",
20
+ " [property color]: [keyword black];",
21
+ " }",
22
+ " [atom &] + [atom &] {",
23
+ " [property color]: [keyword red];",
24
+ " }",
25
+ "}");
26
+
27
+ MT("mixin",
28
+ "[qualifier .mixin] ([variable dark]; [variable-2 @color]) {",
29
+ " [property color]: [atom darken]([variable-2 @color], [number 10%]);",
30
+ "}",
31
+ "[qualifier .mixin] ([variable light]; [variable-2 @color]) {",
32
+ " [property color]: [atom lighten]([variable-2 @color], [number 10%]);",
33
+ "}",
34
+ "[qualifier .mixin] ([variable-2 @_]; [variable-2 @color]) {",
35
+ " [property display]: [atom block];",
36
+ "}",
37
+ "[variable-2 @switch]: [variable light];",
38
+ "[qualifier .class] {",
39
+ " [qualifier .mixin]([variable-2 @switch]; [atom #888]);",
40
+ "}");
41
+
42
+ MT("nest",
43
+ "[qualifier .one] {",
44
+ " [def @media] ([property width]: [number 400px]) {",
45
+ " [property font-size]: [number 1.2em];",
46
+ " [def @media] [attribute print] [keyword and] [property color] {",
47
+ " [property color]: [keyword blue];",
48
+ " }",
49
+ " }",
50
+ "}");
51
+
52
+
53
+ MT("interpolation", ".@{[variable foo]} { [property font-weight]: [atom bold]; }");
54
+ })();
codemirror/mode/css/scss.html ADDED
@@ -0,0 +1,157 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!doctype html>
2
+
3
+ <title>CodeMirror: SCSS mode</title>
4
+ <meta charset="utf-8"/>
5
+ <link rel=stylesheet href="../../doc/docs.css">
6
+
7
+ <link rel="stylesheet" href="../../lib/codemirror.css">
8
+ <script src="../../lib/codemirror.js"></script>
9
+ <script src="css.js"></script>
10
+ <style>.CodeMirror {background: #f8f8f8;}</style>
11
+ <div id=nav>
12
+ <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
13
+
14
+ <ul>
15
+ <li><a href="../../index.html">Home</a>
16
+ <li><a href="../../doc/manual.html">Manual</a>
17
+ <li><a href="https://github.com/codemirror/codemirror">Code</a>
18
+ </ul>
19
+ <ul>
20
+ <li><a href="../index.html">Language modes</a>
21
+ <li><a class=active href="#">SCSS</a>
22
+ </ul>
23
+ </div>
24
+
25
+ <article>
26
+ <h2>SCSS mode</h2>
27
+ <form><textarea id="code" name="code">
28
+ /* Some example SCSS */
29
+
30
+ @import "compass/css3";
31
+ $variable: #333;
32
+
33
+ $blue: #3bbfce;
34
+ $margin: 16px;
35
+
36
+ .content-navigation {
37
+ #nested {
38
+ background-color: black;
39
+ }
40
+ border-color: $blue;
41
+ color:
42
+ darken($blue, 9%);
43
+ }
44
+
45
+ .border {
46
+ padding: $margin / 2;
47
+ margin: $margin / 2;
48
+ border-color: $blue;
49
+ }
50
+
51
+ @mixin table-base {
52
+ th {
53
+ text-align: center;
54
+ font-weight: bold;
55
+ }
56
+ td, th {padding: 2px}
57
+ }
58
+
59
+ table.hl {
60
+ margin: 2em 0;
61
+ td.ln {
62
+ text-align: right;
63
+ }
64
+ }
65
+
66
+ li {
67
+ font: {
68
+ family: serif;
69
+ weight: bold;
70
+ size: 1.2em;
71
+ }
72
+ }
73
+
74
+ @mixin left($dist) {
75
+ float: left;
76
+ margin-left: $dist;
77
+ }
78
+
79
+ #data {
80
+ @include left(10px);
81
+ @include table-base;
82
+ }
83
+
84
+ .source {
85
+ @include flow-into(target);
86
+ border: 10px solid green;
87
+ margin: 20px;
88
+ width: 200px; }
89
+
90
+ .new-container {
91
+ @include flow-from(target);
92
+ border: 10px solid red;
93
+ margin: 20px;
94
+ width: 200px; }
95
+
96
+ body {
97
+ margin: 0;
98
+ padding: 3em 6em;
99
+ font-family: tahoma, arial, sans-serif;
100
+ color: #000;
101
+ }
102
+
103
+ @mixin yellow() {
104
+ background: yellow;
105
+ }
106
+
107
+ .big {
108
+ font-size: 14px;
109
+ }
110
+
111
+ .nested {
112
+ @include border-radius(3px);
113
+ @extend .big;
114
+ p {
115
+ background: whitesmoke;
116
+ a {
117
+ color: red;
118
+ }
119
+ }
120
+ }
121
+
122
+ #navigation a {
123
+ font-weight: bold;
124
+ text-decoration: none !important;
125
+ }
126
+
127
+ h1 {
128
+ font-size: 2.5em;
129
+ }
130
+
131
+ h2 {
132
+ font-size: 1.7em;
133
+ }
134
+
135
+ h1:before, h2:before {
136
+ content: "::";
137
+ }
138
+
139
+ code {
140
+ font-family: courier, monospace;
141
+ font-size: 80%;
142
+ color: #418A8A;
143
+ }
144
+ </textarea></form>
145
+ <script>
146
+ var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
147
+ lineNumbers: true,
148
+ matchBrackets: true,
149
+ mode: "text/x-scss"
150
+ });
151
+ </script>
152
+
153
+ <p>The SCSS mode is a sub-mode of the <a href="index.html">CSS mode</a> (defined in <code>css.js</code>).</p>
154
+
155
+ <p><strong>Parsing/Highlighting Tests:</strong> <a href="../../test/index.html#scss_*">normal</a>, <a href="../../test/index.html#verbose,scss_*">verbose</a>.</p>
156
+
157
+ </article>
codemirror/mode/css/scss_test.js ADDED
@@ -0,0 +1,110 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // CodeMirror, copyright (c) by Marijn Haverbeke and others
2
+ // Distributed under an MIT license: http://codemirror.net/LICENSE
3
+
4
+ (function() {
5
+ var mode = CodeMirror.getMode({indentUnit: 2}, "text/x-scss");
6
+ function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1), "scss"); }
7
+
8
+ MT('url_with_quotation',
9
+ "[tag foo] { [property background]:[atom url]([string test.jpg]) }");
10
+
11
+ MT('url_with_double_quotes',
12
+ "[tag foo] { [property background]:[atom url]([string \"test.jpg\"]) }");
13
+
14
+ MT('url_with_single_quotes',
15
+ "[tag foo] { [property background]:[atom url]([string \'test.jpg\']) }");
16
+
17
+ MT('string',
18
+ "[def @import] [string \"compass/css3\"]");
19
+
20
+ MT('important_keyword',
21
+ "[tag foo] { [property background]:[atom url]([string \'test.jpg\']) [keyword !important] }");
22
+
23
+ MT('variable',
24
+ "[variable-2 $blue]:[atom #333]");
25
+
26
+ MT('variable_as_attribute',
27
+ "[tag foo] { [property color]:[variable-2 $blue] }");
28
+
29
+ MT('numbers',
30
+ "[tag foo] { [property padding]:[number 10px] [number 10] [number 10em] [number 8in] }");
31
+
32
+ MT('number_percentage',
33
+ "[tag foo] { [property width]:[number 80%] }");
34
+
35
+ MT('selector',
36
+ "[builtin #hello][qualifier .world]{}");
37
+
38
+ MT('singleline_comment',
39
+ "[comment // this is a comment]");
40
+
41
+ MT('multiline_comment',
42
+ "[comment /*foobar*/]");
43
+
44
+ MT('attribute_with_hyphen',
45
+ "[tag foo] { [property font-size]:[number 10px] }");
46
+
47
+ MT('string_after_attribute',
48
+ "[tag foo] { [property content]:[string \"::\"] }");
49
+
50
+ MT('directives',
51
+ "[def @include] [qualifier .mixin]");
52
+
53
+ MT('basic_structure',
54
+ "[tag p] { [property background]:[keyword red]; }");
55
+
56
+ MT('nested_structure',
57
+ "[tag p] { [tag a] { [property color]:[keyword red]; } }");
58
+
59
+ MT('mixin',
60
+ "[def @mixin] [tag table-base] {}");
61
+
62
+ MT('number_without_semicolon',
63
+ "[tag p] {[property width]:[number 12]}",
64
+ "[tag a] {[property color]:[keyword red];}");
65
+
66
+ MT('atom_in_nested_block',
67
+ "[tag p] { [tag a] { [property color]:[atom #000]; } }");
68
+
69
+ MT('interpolation_in_property',
70
+ "[tag foo] { #{[variable-2 $hello]}:[number 2]; }");
71
+
72
+ MT('interpolation_in_selector',
73
+ "[tag foo]#{[variable-2 $hello]} { [property color]:[atom #000]; }");
74
+
75
+ MT('interpolation_error',
76
+ "[tag foo]#{[variable foo]} { [property color]:[atom #000]; }");
77
+
78
+ MT("divide_operator",
79
+ "[tag foo] { [property width]:[number 4] [operator /] [number 2] }");
80
+
81
+ MT('nested_structure_with_id_selector',
82
+ "[tag p] { [builtin #hello] { [property color]:[keyword red]; } }");
83
+
84
+ MT('indent_mixin',
85
+ "[def @mixin] [tag container] (",
86
+ " [variable-2 $a]: [number 10],",
87
+ " [variable-2 $b]: [number 10])",
88
+ "{}");
89
+
90
+ MT('indent_nested',
91
+ "[tag foo] {",
92
+ " [tag bar] {",
93
+ " }",
94
+ "}");
95
+
96
+ MT('indent_parentheses',
97
+ "[tag foo] {",
98
+ " [property color]: [atom darken]([variable-2 $blue],",
99
+ " [number 9%]);",
100
+ "}");
101
+
102
+ MT('indent_vardef',
103
+ "[variable-2 $name]:",
104
+ " [string 'val'];",
105
+ "[tag tag] {",
106
+ " [tag inner] {",
107
+ " [property margin]: [number 3px];",
108
+ " }",
109
+ "}");
110
+ })();
codemirror/mode/css/test.js ADDED
@@ -0,0 +1,200 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // CodeMirror, copyright (c) by Marijn Haverbeke and others
2
+ // Distributed under an MIT license: http://codemirror.net/LICENSE
3
+
4
+ (function() {
5
+ var mode = CodeMirror.getMode({indentUnit: 2}, "css");
6
+ function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); }
7
+
8
+ // Error, because "foobarhello" is neither a known type or property, but
9
+ // property was expected (after "and"), and it should be in parentheses.
10
+ MT("atMediaUnknownType",
11
+ "[def @media] [attribute screen] [keyword and] [error foobarhello] { }");
12
+
13
+ // Soft error, because "foobarhello" is not a known property or type.
14
+ MT("atMediaUnknownProperty",
15
+ "[def @media] [attribute screen] [keyword and] ([error foobarhello]) { }");
16
+
17
+ // Make sure nesting works with media queries
18
+ MT("atMediaMaxWidthNested",
19
+ "[def @media] [attribute screen] [keyword and] ([property max-width]: [number 25px]) { [tag foo] { } }");
20
+
21
+ MT("atMediaFeatureValueKeyword",
22
+ "[def @media] ([property orientation]: [keyword landscape]) { }");
23
+
24
+ MT("atMediaUnknownFeatureValueKeyword",
25
+ "[def @media] ([property orientation]: [error upsidedown]) { }");
26
+
27
+ MT("tagSelector",
28
+ "[tag foo] { }");
29
+
30
+ MT("classSelector",
31
+ "[qualifier .foo-bar_hello] { }");
32
+
33
+ MT("idSelector",
34
+ "[builtin #foo] { [error #foo] }");
35
+
36
+ MT("tagSelectorUnclosed",
37
+ "[tag foo] { [property margin]: [number 0] } [tag bar] { }");
38
+
39
+ MT("tagStringNoQuotes",
40
+ "[tag foo] { [property font-family]: [variable hello] [variable world]; }");
41
+
42
+ MT("tagStringDouble",
43
+ "[tag foo] { [property font-family]: [string \"hello world\"]; }");
44
+
45
+ MT("tagStringSingle",
46
+ "[tag foo] { [property font-family]: [string 'hello world']; }");
47
+
48
+ MT("tagColorKeyword",
49
+ "[tag foo] {",
50
+ " [property color]: [keyword black];",
51
+ " [property color]: [keyword navy];",
52
+ " [property color]: [keyword yellow];",
53
+ "}");
54
+
55
+ MT("tagColorHex3",
56
+ "[tag foo] { [property background]: [atom #fff]; }");
57
+
58
+ MT("tagColorHex4",
59
+ "[tag foo] { [property background]: [atom #ffff]; }");
60
+
61
+ MT("tagColorHex6",
62
+ "[tag foo] { [property background]: [atom #ffffff]; }");
63
+
64
+ MT("tagColorHex8",
65
+ "[tag foo] { [property background]: [atom #ffffffff]; }");
66
+
67
+ MT("tagColorHex5Invalid",
68
+ "[tag foo] { [property background]: [atom&error #fffff]; }");
69
+
70
+ MT("tagColorHexInvalid",
71
+ "[tag foo] { [property background]: [atom&error #ffg]; }");
72
+
73
+ MT("tagNegativeNumber",
74
+ "[tag foo] { [property margin]: [number -5px]; }");
75
+
76
+ MT("tagPositiveNumber",
77
+ "[tag foo] { [property padding]: [number 5px]; }");
78
+
79
+ MT("tagVendor",
80
+ "[tag foo] { [meta -foo-][property box-sizing]: [meta -foo-][atom border-box]; }");
81
+
82
+ MT("tagBogusProperty",
83
+ "[tag foo] { [property&error barhelloworld]: [number 0]; }");
84
+
85
+ MT("tagTwoProperties",
86
+ "[tag foo] { [property margin]: [number 0]; [property padding]: [number 0]; }");
87
+
88
+ MT("tagTwoPropertiesURL",
89
+ "[tag foo] { [property background]: [atom url]([string //example.com/foo.png]); [property padding]: [number 0]; }");
90
+
91
+ MT("indent_tagSelector",
92
+ "[tag strong], [tag em] {",
93
+ " [property background]: [atom rgba](",
94
+ " [number 255], [number 255], [number 0], [number .2]",
95
+ " );",
96
+ "}");
97
+
98
+ MT("indent_atMedia",
99
+ "[def @media] {",
100
+ " [tag foo] {",
101
+ " [property color]:",
102
+ " [keyword yellow];",
103
+ " }",
104
+ "}");
105
+
106
+ MT("indent_comma",
107
+ "[tag foo] {",
108
+ " [property font-family]: [variable verdana],",
109
+ " [atom sans-serif];",
110
+ "}");
111
+
112
+ MT("indent_parentheses",
113
+ "[tag foo]:[variable-3 before] {",
114
+ " [property background]: [atom url](",
115
+ "[string blahblah]",
116
+ "[string etc]",
117
+ "[string ]) [keyword !important];",
118
+ "}");
119
+
120
+ MT("font_face",
121
+ "[def @font-face] {",
122
+ " [property font-family]: [string 'myfont'];",
123
+ " [error nonsense]: [string 'abc'];",
124
+ " [property src]: [atom url]([string http://blah]),",
125
+ " [atom url]([string http://foo]);",
126
+ "}");
127
+
128
+ MT("empty_url",
129
+ "[def @import] [atom url]() [attribute screen];");
130
+
131
+ MT("parens",
132
+ "[qualifier .foo] {",
133
+ " [property background-image]: [variable fade]([atom #000], [number 20%]);",
134
+ " [property border-image]: [atom linear-gradient](",
135
+ " [atom to] [atom bottom],",
136
+ " [variable fade]([atom #000], [number 20%]) [number 0%],",
137
+ " [variable fade]([atom #000], [number 20%]) [number 100%]",
138
+ " );",
139
+ "}");
140
+
141
+ MT("css_variable",
142
+ ":[variable-3 root] {",
143
+ " [variable-2 --main-color]: [atom #06c];",
144
+ "}",
145
+ "[tag h1][builtin #foo] {",
146
+ " [property color]: [atom var]([variable-2 --main-color]);",
147
+ "}");
148
+
149
+ MT("supports",
150
+ "[def @supports] ([keyword not] (([property text-align-last]: [atom justify]) [keyword or] ([meta -moz-][property text-align-last]: [atom justify])) {",
151
+ " [property text-align-last]: [atom justify];",
152
+ "}");
153
+
154
+ MT("document",
155
+ "[def @document] [tag url]([string http://blah]),",
156
+ " [tag url-prefix]([string https://]),",
157
+ " [tag domain]([string blah.com]),",
158
+ " [tag regexp]([string \".*blah.+\"]) {",
159
+ " [builtin #id] {",
160
+ " [property background-color]: [keyword white];",
161
+ " }",
162
+ " [tag foo] {",
163
+ " [property font-family]: [variable Verdana], [atom sans-serif];",
164
+ " }",
165
+ "}");
166
+
167
+ MT("document_url",
168
+ "[def @document] [tag url]([string http://blah]) { [qualifier .class] { } }");
169
+
170
+ MT("document_urlPrefix",
171
+ "[def @document] [tag url-prefix]([string https://]) { [builtin #id] { } }");
172
+
173
+ MT("document_domain",
174
+ "[def @document] [tag domain]([string blah.com]) { [tag foo] { } }");
175
+
176
+ MT("document_regexp",
177
+ "[def @document] [tag regexp]([string \".*blah.+\"]) { [builtin #id] { } }");
178
+
179
+ MT("counter-style",
180
+ "[def @counter-style] [variable binary] {",
181
+ " [property system]: [atom numeric];",
182
+ " [property symbols]: [number 0] [number 1];",
183
+ " [property suffix]: [string \".\"];",
184
+ " [property range]: [atom infinite];",
185
+ " [property speak-as]: [atom numeric];",
186
+ "}");
187
+
188
+ MT("counter-style-additive-symbols",
189
+ "[def @counter-style] [variable simple-roman] {",
190
+ " [property system]: [atom additive];",
191
+ " [property additive-symbols]: [number 10] [variable X], [number 5] [variable V], [number 1] [variable I];",
192
+ " [property range]: [number 1] [number 49];",
193
+ "}");
194
+
195
+ MT("counter-style-use",
196
+ "[tag ol][qualifier .roman] { [property list-style]: [variable simple-roman]; }");
197
+
198
+ MT("counter-style-symbols",
199
+ "[tag ol] { [property list-style]: [atom symbols]([atom cyclic] [string \"*\"] [string \"\\2020\"] [string \"\\2021\"] [string \"\\A7\"]); }");
200
+ })();
codemirror/mode/htmlembedded/htmlembedded.js ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // CodeMirror, copyright (c) by Marijn Haverbeke and others
2
+ // Distributed under an MIT license: http://codemirror.net/LICENSE
3
+
4
+ (function(mod) {
5
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
6
+ mod(require("../../lib/codemirror"), require("../htmlmixed/htmlmixed"),
7
+ require("../../addon/mode/multiplex"));
8
+ else if (typeof define == "function" && define.amd) // AMD
9
+ define(["../../lib/codemirror", "../htmlmixed/htmlmixed",
10
+ "../../addon/mode/multiplex"], mod);
11
+ else // Plain browser env
12
+ mod(CodeMirror);
13
+ })(function(CodeMirror) {
14
+ "use strict";
15
+
16
+ CodeMirror.defineMode("htmlembedded", function(config, parserConfig) {
17
+ return CodeMirror.multiplexingMode(CodeMirror.getMode(config, "htmlmixed"), {
18
+ open: parserConfig.open || parserConfig.scriptStartRegex || "<%",
19
+ close: parserConfig.close || parserConfig.scriptEndRegex || "%>",
20
+ mode: CodeMirror.getMode(config, parserConfig.scriptingModeSpec)
21
+ });
22
+ }, "htmlmixed");
23
+
24
+ CodeMirror.defineMIME("application/x-ejs", {name: "htmlembedded", scriptingModeSpec:"javascript"});
25
+ CodeMirror.defineMIME("application/x-aspx", {name: "htmlembedded", scriptingModeSpec:"text/x-csharp"});
26
+ CodeMirror.defineMIME("application/x-jsp", {name: "htmlembedded", scriptingModeSpec:"text/x-java"});
27
+ CodeMirror.defineMIME("application/x-erb", {name: "htmlembedded", scriptingModeSpec:"ruby"});
28
+ });
codemirror/mode/htmlembedded/index.html ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!doctype html>
2
+
3
+ <title>CodeMirror: Html Embedded Scripts mode</title>
4
+ <meta charset="utf-8"/>
5
+ <link rel=stylesheet href="../../doc/docs.css">
6
+
7
+ <link rel="stylesheet" href="../../lib/codemirror.css">
8
+ <script src="../../lib/codemirror.js"></script>
9
+ <script src="../xml/xml.js"></script>
10
+ <script src="../javascript/javascript.js"></script>
11
+ <script src="../css/css.js"></script>
12
+ <script src="../htmlmixed/htmlmixed.js"></script>
13
+ <script src="../../addon/mode/multiplex.js"></script>
14
+ <script src="htmlembedded.js"></script>
15
+ <style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
16
+ <div id=nav>
17
+ <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
18
+
19
+ <ul>
20
+ <li><a href="../../index.html">Home</a>
21
+ <li><a href="../../doc/manual.html">Manual</a>
22
+ <li><a href="https://github.com/codemirror/codemirror">Code</a>
23
+ </ul>
24
+ <ul>
25
+ <li><a href="../index.html">Language modes</a>
26
+ <li><a class=active href="#">Html Embedded Scripts</a>
27
+ </ul>
28
+ </div>
29
+
30
+ <article>
31
+ <h2>Html Embedded Scripts mode</h2>
32
+ <form><textarea id="code" name="code">
33
+ <%
34
+ function hello(who) {
35
+ return "Hello " + who;
36
+ }
37
+ %>
38
+ This is an example of EJS (embedded javascript)
39
+ <p>The program says <%= hello("world") %>.</p>
40
+ <script>
41
+ alert("And here is some normal JS code"); // also colored
42
+ </script>
43
+ </textarea></form>
44
+
45
+ <script>
46
+ var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
47
+ lineNumbers: true,
48
+ mode: "application/x-ejs",
49
+ indentUnit: 4,
50
+ indentWithTabs: true
51
+ });
52
+ </script>
53
+
54
+ <p>Mode for html embedded scripts like JSP and ASP.NET. Depends on HtmlMixed which in turn depends on
55
+ JavaScript, CSS and XML.<br />Other dependancies include those of the scriping language chosen.</p>
56
+
57
+ <p><strong>MIME types defined:</strong> <code>application/x-aspx</code> (ASP.NET),
58
+ <code>application/x-ejs</code> (Embedded Javascript), <code>application/x-jsp</code> (JavaServer Pages)</p>
59
+ </article>
codemirror/mode/htmlmixed/htmlmixed.js ADDED
@@ -0,0 +1,152 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // CodeMirror, copyright (c) by Marijn Haverbeke and others
2
+ // Distributed under an MIT license: http://codemirror.net/LICENSE
3
+
4
+ (function(mod) {
5
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
6
+ mod(require("../../lib/codemirror"), require("../xml/xml"), require("../javascript/javascript"), require("../css/css"));
7
+ else if (typeof define == "function" && define.amd) // AMD
8
+ define(["../../lib/codemirror", "../xml/xml", "../javascript/javascript", "../css/css"], mod);
9
+ else // Plain browser env
10
+ mod(CodeMirror);
11
+ })(function(CodeMirror) {
12
+ "use strict";
13
+
14
+ var defaultTags = {
15
+ script: [
16
+ ["lang", /(javascript|babel)/i, "javascript"],
17
+ ["type", /^(?:text|application)\/(?:x-)?(?:java|ecma)script$|^$/i, "javascript"],
18
+ ["type", /./, "text/plain"],
19
+ [null, null, "javascript"]
20
+ ],
21
+ style: [
22
+ ["lang", /^css$/i, "css"],
23
+ ["type", /^(text\/)?(x-)?(stylesheet|css)$/i, "css"],
24
+ ["type", /./, "text/plain"],
25
+ [null, null, "css"]
26
+ ]
27
+ };
28
+
29
+ function maybeBackup(stream, pat, style) {
30
+ var cur = stream.current(), close = cur.search(pat);
31
+ if (close > -1) {
32
+ stream.backUp(cur.length - close);
33
+ } else if (cur.match(/<\/?$/)) {
34
+ stream.backUp(cur.length);
35
+ if (!stream.match(pat, false)) stream.match(cur);
36
+ }
37
+ return style;
38
+ }
39
+
40
+ var attrRegexpCache = {};
41
+ function getAttrRegexp(attr) {
42
+ var regexp = attrRegexpCache[attr];
43
+ if (regexp) return regexp;
44
+ return attrRegexpCache[attr] = new RegExp("\\s+" + attr + "\\s*=\\s*('|\")?([^'\"]+)('|\")?\\s*");
45
+ }
46
+
47
+ function getAttrValue(text, attr) {
48
+ var match = text.match(getAttrRegexp(attr))
49
+ return match ? match[2] : ""
50
+ }
51
+
52
+ function getTagRegexp(tagName, anchored) {
53
+ return new RegExp((anchored ? "^" : "") + "<\/\s*" + tagName + "\s*>", "i");
54
+ }
55
+
56
+ function addTags(from, to) {
57
+ for (var tag in from) {
58
+ var dest = to[tag] || (to[tag] = []);
59
+ var source = from[tag];
60
+ for (var i = source.length - 1; i >= 0; i--)
61
+ dest.unshift(source[i])
62
+ }
63
+ }
64
+
65
+ function findMatchingMode(tagInfo, tagText) {
66
+ for (var i = 0; i < tagInfo.length; i++) {
67
+ var spec = tagInfo[i];
68
+ if (!spec[0] || spec[1].test(getAttrValue(tagText, spec[0]))) return spec[2];
69
+ }
70
+ }
71
+
72
+ CodeMirror.defineMode("htmlmixed", function (config, parserConfig) {
73
+ var htmlMode = CodeMirror.getMode(config, {
74
+ name: "xml",
75
+ htmlMode: true,
76
+ multilineTagIndentFactor: parserConfig.multilineTagIndentFactor,
77
+ multilineTagIndentPastTag: parserConfig.multilineTagIndentPastTag
78
+ });
79
+
80
+ var tags = {};
81
+ var configTags = parserConfig && parserConfig.tags, configScript = parserConfig && parserConfig.scriptTypes;
82
+ addTags(defaultTags, tags);
83
+ if (configTags) addTags(configTags, tags);
84
+ if (configScript) for (var i = configScript.length - 1; i >= 0; i--)
85
+ tags.script.unshift(["type", configScript[i].matches, configScript[i].mode])
86
+
87
+ function html(stream, state) {
88
+ var style = htmlMode.token(stream, state.htmlState), tag = /\btag\b/.test(style), tagName
89
+ if (tag && !/[<>\s\/]/.test(stream.current()) &&
90
+ (tagName = state.htmlState.tagName && state.htmlState.tagName.toLowerCase()) &&
91
+ tags.hasOwnProperty(tagName)) {
92
+ state.inTag = tagName + " "
93
+ } else if (state.inTag && tag && />$/.test(stream.current())) {
94
+ var inTag = /^([\S]+) (.*)/.exec(state.inTag)
95
+ state.inTag = null
96
+ var modeSpec = stream.current() == ">" && findMatchingMode(tags[inTag[1]], inTag[2])
97
+ var mode = CodeMirror.getMode(config, modeSpec)
98
+ var endTagA = getTagRegexp(inTag[1], true), endTag = getTagRegexp(inTag[1], false);
99
+ state.token = function (stream, state) {
100
+ if (stream.match(endTagA, false)) {
101
+ state.token = html;
102
+ state.localState = state.localMode = null;
103
+ return null;
104
+ }
105
+ return maybeBackup(stream, endTag, state.localMode.token(stream, state.localState));
106
+ };
107
+ state.localMode = mode;
108
+ state.localState = CodeMirror.startState(mode, htmlMode.indent(state.htmlState, ""));
109
+ } else if (state.inTag) {
110
+ state.inTag += stream.current()
111
+ if (stream.eol()) state.inTag += " "
112
+ }
113
+ return style;
114
+ };
115
+
116
+ return {
117
+ startState: function () {
118
+ var state = htmlMode.startState();
119
+ return {token: html, inTag: null, localMode: null, localState: null, htmlState: state};
120
+ },
121
+
122
+ copyState: function (state) {
123
+ var local;
124
+ if (state.localState) {
125
+ local = CodeMirror.copyState(state.localMode, state.localState);
126
+ }
127
+ return {token: state.token, inTag: state.inTag,
128
+ localMode: state.localMode, localState: local,
129
+ htmlState: CodeMirror.copyState(htmlMode, state.htmlState)};
130
+ },
131
+
132
+ token: function (stream, state) {
133
+ return state.token(stream, state);
134
+ },
135
+
136
+ indent: function (state, textAfter) {
137
+ if (!state.localMode || /^\s*<\//.test(textAfter))
138
+ return htmlMode.indent(state.htmlState, textAfter);
139
+ else if (state.localMode.indent)
140
+ return state.localMode.indent(state.localState, textAfter);
141
+ else
142
+ return CodeMirror.Pass;
143
+ },
144
+
145
+ innerMode: function (state) {
146
+ return {state: state.localState || state.htmlState, mode: state.localMode || htmlMode};
147
+ }
148
+ };
149
+ }, "xml", "javascript", "css");
150
+
151
+ CodeMirror.defineMIME("text/html", "htmlmixed");
152
+ });
codemirror/mode/htmlmixed/index.html ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!doctype html>
2
+
3
+ <title>CodeMirror: HTML mixed mode</title>
4
+ <meta charset="utf-8"/>
5
+ <link rel=stylesheet href="../../doc/docs.css">
6
+
7
+ <link rel="stylesheet" href="../../lib/codemirror.css">
8
+ <script src="../../lib/codemirror.js"></script>
9
+ <script src="../../addon/selection/selection-pointer.js"></script>
10
+ <script src="../xml/xml.js"></script>
11
+ <script src="../javascript/javascript.js"></script>
12
+ <script src="../css/css.js"></script>
13
+ <script src="../vbscript/vbscript.js"></script>
14
+ <script src="htmlmixed.js"></script>
15
+ <style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
16
+ <div id=nav>
17
+ <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
18
+
19
+ <ul>
20
+ <li><a href="../../index.html">Home</a>
21
+ <li><a href="../../doc/manual.html">Manual</a>
22
+ <li><a href="https://github.com/codemirror/codemirror">Code</a>
23
+ </ul>
24
+ <ul>
25
+ <li><a href="../index.html">Language modes</a>
26
+ <li><a class=active href="#">HTML mixed</a>
27
+ </ul>
28
+ </div>
29
+
30
+ <article>
31
+ <h2>HTML mixed mode</h2>
32
+ <form><textarea id="code" name="code">
33
+ <html style="color: green">
34
+ <!-- this is a comment -->
35
+ <head>
36
+ <title>Mixed HTML Example</title>
37
+ <style type="text/css">
38
+ h1 {font-family: comic sans; color: #f0f;}
39
+ div {background: yellow !important;}
40
+ body {
41
+ max-width: 50em;
42
+ margin: 1em 2em 1em 5em;
43
+ }
44
+ </style>
45
+ </head>
46
+ <body>
47
+ <h1>Mixed HTML Example</h1>
48
+ <script>
49
+ function jsFunc(arg1, arg2) {
50
+ if (arg1 && arg2) document.body.innerHTML = "achoo";
51
+ }
52
+ </script>
53
+ </body>
54
+ </html>
55
+ </textarea></form>
56
+ <script>
57
+ // Define an extended mixed-mode that understands vbscript and
58
+ // leaves mustache/handlebars embedded templates in html mode
59
+ var mixedMode = {
60
+ name: "htmlmixed",
61
+ scriptTypes: [{matches: /\/x-handlebars-template|\/x-mustache/i,
62
+ mode: null},
63
+ {matches: /(text|application)\/(x-)?vb(a|script)/i,
64
+ mode: "vbscript"}]
65
+ };
66
+ var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
67
+ mode: mixedMode,
68
+ selectionPointer: true
69
+ });
70
+ </script>
71
+
72
+ <p>The HTML mixed mode depends on the XML, JavaScript, and CSS modes.</p>
73
+
74
+ <p>It takes an optional mode configuration
75
+ option, <code>scriptTypes</code>, which can be used to add custom
76
+ behavior for specific <code>&lt;script type="..."></code> tags. If
77
+ given, it should hold an array of <code>{matches, mode}</code>
78
+ objects, where <code>matches</code> is a string or regexp that
79
+ matches the script type, and <code>mode</code> is
80
+ either <code>null</code>, for script types that should stay in
81
+ HTML mode, or a <a href="../../doc/manual.html#option_mode">mode
82
+ spec</a> corresponding to the mode that should be used for the
83
+ script.</p>
84
+
85
+ <p><strong>MIME types defined:</strong> <code>text/html</code>
86
+ (redefined, only takes effect if you load this parser after the
87
+ XML parser).</p>
88
+
89
+ </article>
codemirror/mode/index.html ADDED
@@ -0,0 +1,158 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!doctype html>
2
+
3
+ <title>CodeMirror: Language Modes</title>
4
+ <meta charset="utf-8"/>
5
+ <link rel=stylesheet href="../doc/docs.css">
6
+
7
+ <div id=nav>
8
+ <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../doc/logo.png"></a>
9
+
10
+ <ul>
11
+ <li><a href="../index.html">Home</a>
12
+ <li><a href="../doc/manual.html">Manual</a>
13
+ <li><a href="https://github.com/codemirror/codemirror">Code</a>
14
+ </ul>
15
+ <ul>
16
+ <li><a class=active href="#">Language modes</a>
17
+ </ul>
18
+ </div>
19
+
20
+ <article>
21
+
22
+ <h2>Language modes</h2>
23
+
24
+ <p>This is a list of every mode in the distribution. Each mode lives
25
+ in a subdirectory of the <code>mode/</code> directory, and typically
26
+ defines a single JavaScript file that implements the mode. Loading
27
+ such file will make the language available to CodeMirror, through
28
+ the <a href="../doc/manual.html#option_mode"><code>mode</code></a>
29
+ option.</p>
30
+
31
+ <div style="-webkit-columns: 100px 2; -moz-columns: 100px 2; columns: 100px 2;">
32
+ <ul style="margin-top: 0">
33
+ <li><a href="apl/index.html">APL</a></li>
34
+ <li><a href="asn.1/index.html">ASN.1</a></li>
35
+ <li><a href="asterisk/index.html">Asterisk dialplan</a></li>
36
+ <li><a href="brainfuck/index.html">Brainfuck</a></li>
37
+ <li><a href="clike/index.html">C, C++, C#</a></li>
38
+ <li><a href="clike/index.html">Ceylon</a></li>
39
+ <li><a href="clojure/index.html">Clojure</a></li>
40
+ <li><a href="css/gss.html">Closure Stylesheets (GSS)</a></li>
41
+ <li><a href="cmake/index.html">CMake</a></li>
42
+ <li><a href="cobol/index.html">COBOL</a></li>
43
+ <li><a href="coffeescript/index.html">CoffeeScript</a></li>
44
+ <li><a href="commonlisp/index.html">Common Lisp</a></li>
45
+ <li><a href="crystal/index.html">Crystal</a></li>
46
+ <li><a href="css/index.html">CSS</a></li>
47
+ <li><a href="cypher/index.html">Cypher</a></li>
48
+ <li><a href="python/index.html">Cython</a></li>
49
+ <li><a href="d/index.html">D</a></li>
50
+ <li><a href="dart/index.html">Dart</a></li>
51
+ <li><a href="django/index.html">Django</a> (templating language)</li>
52
+ <li><a href="dockerfile/index.html">Dockerfile</a></li>
53
+ <li><a href="diff/index.html">diff</a></li>
54
+ <li><a href="dtd/index.html">DTD</a></li>
55
+ <li><a href="dylan/index.html">Dylan</a></li>
56
+ <li><a href="ebnf/index.html">EBNF</a></li>
57
+ <li><a href="ecl/index.html">ECL</a></li>
58
+ <li><a href="eiffel/index.html">Eiffel</a></li>
59
+ <li><a href="elm/index.html">Elm</a></li>
60
+ <li><a href="erlang/index.html">Erlang</a></li>
61
+ <li><a href="factor/index.html">Factor</a></li>
62
+ <li><a href="fcl/index.html">FCL</a></li>
63
+ <li><a href="forth/index.html">Forth</a></li>
64
+ <li><a href="fortran/index.html">Fortran</a></li>
65
+ <li><a href="mllike/index.html">F#</a></li>
66
+ <li><a href="gas/index.html">Gas</a> (AT&amp;T-style assembly)</li>
67
+ <li><a href="gherkin/index.html">Gherkin</a></li>
68
+ <li><a href="go/index.html">Go</a></li>
69
+ <li><a href="groovy/index.html">Groovy</a></li>
70
+ <li><a href="haml/index.html">HAML</a></li>
71
+ <li><a href="handlebars/index.html">Handlebars</a></li>
72
+ <li><a href="haskell/index.html">Haskell</a> (<a href="haskell-literate/index.html">Literate</a>)</li>
73
+ <li><a href="haxe/index.html">Haxe</a></li>
74
+ <li><a href="htmlembedded/index.html">HTML embedded</a> (JSP, ASP.NET)</li>
75
+ <li><a href="htmlmixed/index.html">HTML mixed-mode</a></li>
76
+ <li><a href="http/index.html">HTTP</a></li>
77
+ <li><a href="idl/index.html">IDL</a></li>
78
+ <li><a href="clike/index.html">Java</a></li>
79
+ <li><a href="jade/index.html">Jade</a></li>
80
+ <li><a href="javascript/index.html">JavaScript</a> (<a href="jsx/index.html">JSX</a>)</li>
81
+ <li><a href="jinja2/index.html">Jinja2</a></li>
82
+ <li><a href="julia/index.html">Julia</a></li>
83
+ <li><a href="kotlin/index.html">Kotlin</a></li>
84
+ <li><a href="css/less.html">LESS</a></li>
85
+ <li><a href="livescript/index.html">LiveScript</a></li>
86
+ <li><a href="lua/index.html">Lua</a></li>
87
+ <li><a href="markdown/index.html">Markdown</a> (<a href="gfm/index.html">GitHub-flavour</a>)</li>
88
+ <li><a href="mathematica/index.html">Mathematica</a></li>
89
+ <li><a href="mirc/index.html">mIRC</a></li>
90
+ <li><a href="modelica/index.html">Modelica</a></li>
91
+ <li><a href="mscgen/index.html">MscGen</a></li>
92
+ <li><a href="mumps/index.html">MUMPS</a></li>
93
+ <li><a href="nginx/index.html">Nginx</a></li>
94
+ <li><a href="nsis/index.html">NSIS</a></li>
95
+ <li><a href="ntriples/index.html">NTriples</a></li>
96
+ <li><a href="clike/index.html">Objective C</a></li>
97
+ <li><a href="mllike/index.html">OCaml</a></li>
98
+ <li><a href="octave/index.html">Octave</a> (MATLAB)</li>
99
+ <li><a href="oz/index.html">Oz</a></li>
100
+ <li><a href="pascal/index.html">Pascal</a></li>
101
+ <li><a href="pegjs/index.html">PEG.js</a></li>
102
+ <li><a href="perl/index.html">Perl</a></li>
103
+ <li><a href="asciiarmor/index.html">PGP (ASCII armor)</a></li>
104
+ <li><a href="php/index.html">PHP</a></li>
105
+ <li><a href="pig/index.html">Pig Latin</a></li>
106
+ <li><a href="properties/index.html">Properties files</a></li>
107
+ <li><a href="puppet/index.html">Puppet</a></li>
108
+ <li><a href="python/index.html">Python</a></li>
109
+ <li><a href="q/index.html">Q</a></li>
110
+ <li><a href="r/index.html">R</a></li>
111
+ <li><a href="rpm/index.html">RPM</a></li>
112
+ <li><a href="rst/index.html">reStructuredText</a></li>
113
+ <li><a href="ruby/index.html">Ruby</a></li>
114
+ <li><a href="rust/index.html">Rust</a></li>
115
+ <li><a href="sass/index.html">Sass</a></li>
116
+ <li><a href="spreadsheet/index.html">Spreadsheet</a></li>
117
+ <li><a href="clike/scala.html">Scala</a></li>
118
+ <li><a href="scheme/index.html">Scheme</a></li>
119
+ <li><a href="css/scss.html">SCSS</a></li>
120
+ <li><a href="shell/index.html">Shell</a></li>
121
+ <li><a href="sieve/index.html">Sieve</a></li>
122
+ <li><a href="slim/index.html">Slim</a></li>
123
+ <li><a href="smalltalk/index.html">Smalltalk</a></li>
124
+ <li><a href="smarty/index.html">Smarty</a></li>
125
+ <li><a href="solr/index.html">Solr</a></li>
126
+ <li><a href="soy/index.html">Soy</a></li>
127
+ <li><a href="stylus/index.html">Stylus</a></li>
128
+ <li><a href="sql/index.html">SQL</a> (several dialects)</li>
129
+ <li><a href="sparql/index.html">SPARQL</a></li>
130
+ <li><a href="clike/index.html">Squirrel</a></li>
131
+ <li><a href="swift/index.html">Swift</a></li>
132
+ <li><a href="stex/index.html">sTeX, LaTeX</a></li>
133
+ <li><a href="tcl/index.html">Tcl</a></li>
134
+ <li><a href="textile/index.html">Textile</a></li>
135
+ <li><a href="tiddlywiki/index.html">Tiddlywiki</a></li>
136
+ <li><a href="tiki/index.html">Tiki wiki</a></li>
137
+ <li><a href="toml/index.html">TOML</a></li>
138
+ <li><a href="tornado/index.html">Tornado</a> (templating language)</li>
139
+ <li><a href="troff/index.html">troff</a> (for manpages)</li>
140
+ <li><a href="ttcn/index.html">TTCN</a></li>
141
+ <li><a href="ttcn-cfg/index.html">TTCN Configuration</a></li>
142
+ <li><a href="turtle/index.html">Turtle</a></li>
143
+ <li><a href="twig/index.html">Twig</a></li>
144
+ <li><a href="vb/index.html">VB.NET</a></li>
145
+ <li><a href="vbscript/index.html">VBScript</a></li>
146
+ <li><a href="velocity/index.html">Velocity</a></li>
147
+ <li><a href="verilog/index.html">Verilog/SystemVerilog</a></li>
148
+ <li><a href="vhdl/index.html">VHDL</a></li>
149
+ <li><a href="vue/index.html">Vue.js app</a></li>
150
+ <li><a href="xml/index.html">XML/HTML</a></li>
151
+ <li><a href="xquery/index.html">XQuery</a></li>
152
+ <li><a href="yaml/index.html">YAML</a></li>
153
+ <li><a href="yaml-frontmatter/index.html">YAML frontmatter</a></li>
154
+ <li><a href="z80/index.html">Z80</a></li>
155
+ </ul>
156
+ </div>
157
+
158
+ </article>
codemirror/mode/javascript/index.html ADDED
@@ -0,0 +1,114 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!doctype html>
2
+
3
+ <title>CodeMirror: JavaScript mode</title>
4
+ <meta charset="utf-8"/>
5
+ <link rel=stylesheet href="../../doc/docs.css">
6
+
7
+ <link rel="stylesheet" href="../../lib/codemirror.css">
8
+ <script src="../../lib/codemirror.js"></script>
9
+ <script src="../../addon/edit/matchbrackets.js"></script>
10
+ <script src="../../addon/comment/continuecomment.js"></script>
11
+ <script src="../../addon/comment/comment.js"></script>
12
+ <script src="javascript.js"></script>
13
+ <style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
14
+ <div id=nav>
15
+ <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
16
+
17
+ <ul>
18
+ <li><a href="../../index.html">Home</a>
19
+ <li><a href="../../doc/manual.html">Manual</a>
20
+ <li><a href="https://github.com/codemirror/codemirror">Code</a>
21
+ </ul>
22
+ <ul>
23
+ <li><a href="../index.html">Language modes</a>
24
+ <li><a class=active href="#">JavaScript</a>
25
+ </ul>
26
+ </div>
27
+
28
+ <article>
29
+ <h2>JavaScript mode</h2>
30
+
31
+
32
+ <div><textarea id="code" name="code">
33
+ // Demo code (the actual new parser character stream implementation)
34
+
35
+ function StringStream(string) {
36
+ this.pos = 0;
37
+ this.string = string;
38
+ }
39
+
40
+ StringStream.prototype = {
41
+ done: function() {return this.pos >= this.string.length;},
42
+ peek: function() {return this.string.charAt(this.pos);},
43
+ next: function() {
44
+ if (this.pos &lt; this.string.length)
45
+ return this.string.charAt(this.pos++);
46
+ },
47
+ eat: function(match) {
48
+ var ch = this.string.charAt(this.pos);
49
+ if (typeof match == "string") var ok = ch == match;
50
+ else var ok = ch &amp;&amp; match.test ? match.test(ch) : match(ch);
51
+ if (ok) {this.pos++; return ch;}
52
+ },
53
+ eatWhile: function(match) {
54
+ var start = this.pos;
55
+ while (this.eat(match));
56
+ if (this.pos > start) return this.string.slice(start, this.pos);
57
+ },
58
+ backUp: function(n) {this.pos -= n;},
59
+ column: function() {return this.pos;},
60
+ eatSpace: function() {
61
+ var start = this.pos;
62
+ while (/\s/.test(this.string.charAt(this.pos))) this.pos++;
63
+ return this.pos - start;
64
+ },
65
+ match: function(pattern, consume, caseInsensitive) {
66
+ if (typeof pattern == "string") {
67
+ function cased(str) {return caseInsensitive ? str.toLowerCase() : str;}
68
+ if (cased(this.string).indexOf(cased(pattern), this.pos) == this.pos) {
69
+ if (consume !== false) this.pos += str.length;
70
+ return true;
71
+ }
72
+ }
73
+ else {
74
+ var match = this.string.slice(this.pos).match(pattern);
75
+ if (match &amp;&amp; consume !== false) this.pos += match[0].length;
76
+ return match;
77
+ }
78
+ }
79
+ };
80
+ </textarea></div>
81
+
82
+ <script>
83
+ var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
84
+ lineNumbers: true,
85
+ matchBrackets: true,
86
+ continueComments: "Enter",
87
+ extraKeys: {"Ctrl-Q": "toggleComment"}
88
+ });
89
+ </script>
90
+
91
+ <p>
92
+ JavaScript mode supports several configuration options:
93
+ <ul>
94
+ <li><code>json</code> which will set the mode to expect JSON
95
+ data rather than a JavaScript program.</li>
96
+ <li><code>jsonld</code> which will set the mode to expect
97
+ <a href="http://json-ld.org">JSON-LD</a> linked data rather
98
+ than a JavaScript program (<a href="json-ld.html">demo</a>).</li>
99
+ <li><code>typescript</code> which will activate additional
100
+ syntax highlighting and some other things for TypeScript code
101
+ (<a href="typescript.html">demo</a>).</li>
102
+ <li><code>statementIndent</code> which (given a number) will
103
+ determine the amount of indentation to use for statements
104
+ continued on a new line.</li>
105
+ <li><code>wordCharacters</code>, a regexp that indicates which
106
+ characters should be considered part of an identifier.
107
+ Defaults to <code>/[\w$]/</code>, which does not handle
108
+ non-ASCII identifiers. Can be set to something more elaborate
109
+ to improve Unicode support.</li>
110
+ </ul>
111
+ </p>
112
+
113
+ <p><strong>MIME types defined:</strong> <code>text/javascript</code>, <code>application/json</code>, <code>application/ld+json</code>, <code>text/typescript</code>, <code>application/typescript</code>.</p>
114
+ </article>
codemirror/mode/javascript/javascript.js ADDED
@@ -0,0 +1,742 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // CodeMirror, copyright (c) by Marijn Haverbeke and others
2
+ // Distributed under an MIT license: http://codemirror.net/LICENSE
3
+
4
+ // TODO actually recognize syntax of TypeScript constructs
5
+
6
+ (function(mod) {
7
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
8
+ mod(require("../../lib/codemirror"));
9
+ else if (typeof define == "function" && define.amd) // AMD
10
+ define(["../../lib/codemirror"], mod);
11
+ else // Plain browser env
12
+ mod(CodeMirror);
13
+ })(function(CodeMirror) {
14
+ "use strict";
15
+
16
+ function expressionAllowed(stream, state, backUp) {
17
+ return /^(?:operator|sof|keyword c|case|new|[\[{}\(,;:]|=>)$/.test(state.lastType) ||
18
+ (state.lastType == "quasi" && /\{\s*$/.test(stream.string.slice(0, stream.pos - (backUp || 0))))
19
+ }
20
+
21
+ CodeMirror.defineMode("javascript", function(config, parserConfig) {
22
+ var indentUnit = config.indentUnit;
23
+ var statementIndent = parserConfig.statementIndent;
24
+ var jsonldMode = parserConfig.jsonld;
25
+ var jsonMode = parserConfig.json || jsonldMode;
26
+ var isTS = parserConfig.typescript;
27
+ var wordRE = parserConfig.wordCharacters || /[\w$\xa1-\uffff]/;
28
+
29
+ // Tokenizer
30
+
31
+ var keywords = function(){
32
+ function kw(type) {return {type: type, style: "keyword"};}
33
+ var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c");
34
+ var operator = kw("operator"), atom = {type: "atom", style: "atom"};
35
+
36
+ var jsKeywords = {
37
+ "if": kw("if"), "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B,
38
+ "return": C, "break": C, "continue": C, "new": kw("new"), "delete": C, "throw": C, "debugger": C,
39
+ "var": kw("var"), "const": kw("var"), "let": kw("var"),
40
+ "function": kw("function"), "catch": kw("catch"),
41
+ "for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"),
42
+ "in": operator, "typeof": operator, "instanceof": operator,
43
+ "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom,
44
+ "this": kw("this"), "class": kw("class"), "super": kw("atom"),
45
+ "yield": C, "export": kw("export"), "import": kw("import"), "extends": C
46
+ };
47
+
48
+ // Extend the 'normal' keywords with the TypeScript language extensions
49
+ if (isTS) {
50
+ var type = {type: "variable", style: "variable-3"};
51
+ var tsKeywords = {
52
+ // object-like things
53
+ "interface": kw("class"),
54
+ "implements": C,
55
+ "namespace": C,
56
+ "module": kw("module"),
57
+ "enum": kw("module"),
58
+
59
+ // scope modifiers
60
+ "public": kw("modifier"),
61
+ "private": kw("modifier"),
62
+ "protected": kw("modifier"),
63
+ "abstract": kw("modifier"),
64
+
65
+ // operators
66
+ "as": operator,
67
+
68
+ // types
69
+ "string": type, "number": type, "boolean": type, "any": type
70
+ };
71
+
72
+ for (var attr in tsKeywords) {
73
+ jsKeywords[attr] = tsKeywords[attr];
74
+ }
75
+ }
76
+
77
+ return jsKeywords;
78
+ }();
79
+
80
+ var isOperatorChar = /[+\-*&%=<>!?|~^]/;
81
+ var isJsonldKeyword = /^@(context|id|value|language|type|container|list|set|reverse|index|base|vocab|graph)"/;
82
+
83
+ function readRegexp(stream) {
84
+ var escaped = false, next, inSet = false;
85
+ while ((next = stream.next()) != null) {
86
+ if (!escaped) {
87
+ if (next == "/" && !inSet) return;
88
+ if (next == "[") inSet = true;
89
+ else if (inSet && next == "]") inSet = false;
90
+ }
91
+ escaped = !escaped && next == "\\";
92
+ }
93
+ }
94
+
95
+ // Used as scratch variables to communicate multiple values without
96
+ // consing up tons of objects.
97
+ var type, content;
98
+ function ret(tp, style, cont) {
99
+ type = tp; content = cont;
100
+ return style;
101
+ }
102
+ function tokenBase(stream, state) {
103
+ var ch = stream.next();
104
+ if (ch == '"' || ch == "'") {
105
+ state.tokenize = tokenString(ch);
106
+ return state.tokenize(stream, state);
107
+ } else if (ch == "." && stream.match(/^\d+(?:[eE][+\-]?\d+)?/)) {
108
+ return ret("number", "number");
109
+ } else if (ch == "." && stream.match("..")) {
110
+ return ret("spread", "meta");
111
+ } else if (/[\[\]{}\(\),;\:\.]/.test(ch)) {
112
+ return ret(ch);
113
+ } else if (ch == "=" && stream.eat(">")) {
114
+ return ret("=>", "operator");
115
+ } else if (ch == "0" && stream.eat(/x/i)) {
116
+ stream.eatWhile(/[\da-f]/i);
117
+ return ret("number", "number");
118
+ } else if (ch == "0" && stream.eat(/o/i)) {
119
+ stream.eatWhile(/[0-7]/i);
120
+ return ret("number", "number");
121
+ } else if (ch == "0" && stream.eat(/b/i)) {
122
+ stream.eatWhile(/[01]/i);
123
+ return ret("number", "number");
124
+ } else if (/\d/.test(ch)) {
125
+ stream.match(/^\d*(?:\.\d*)?(?:[eE][+\-]?\d+)?/);
126
+ return ret("number", "number");
127
+ } else if (ch == "/") {
128
+ if (stream.eat("*")) {
129
+ state.tokenize = tokenComment;
130
+ return tokenComment(stream, state);
131
+ } else if (stream.eat("/")) {
132
+ stream.skipToEnd();
133
+ return ret("comment", "comment");
134
+ } else if (expressionAllowed(stream, state, 1)) {
135
+ readRegexp(stream);
136
+ stream.match(/^\b(([gimyu])(?![gimyu]*\2))+\b/);
137
+ return ret("regexp", "string-2");
138
+ } else {
139
+ stream.eatWhile(isOperatorChar);
140
+ return ret("operator", "operator", stream.current());
141
+ }
142
+ } else if (ch == "`") {
143
+ state.tokenize = tokenQuasi;
144
+ return tokenQuasi(stream, state);
145
+ } else if (ch == "#") {
146
+ stream.skipToEnd();
147
+ return ret("error", "error");
148
+ } else if (isOperatorChar.test(ch)) {
149
+ stream.eatWhile(isOperatorChar);
150
+ return ret("operator", "operator", stream.current());
151
+ } else if (wordRE.test(ch)) {
152
+ stream.eatWhile(wordRE);
153
+ var word = stream.current(), known = keywords.propertyIsEnumerable(word) && keywords[word];
154
+ return (known && state.lastType != ".") ? ret(known.type, known.style, word) :
155
+ ret("variable", "variable", word);
156
+ }
157
+ }
158
+
159
+ function tokenString(quote) {
160
+ return function(stream, state) {
161
+ var escaped = false, next;
162
+ if (jsonldMode && stream.peek() == "@" && stream.match(isJsonldKeyword)){
163
+ state.tokenize = tokenBase;
164
+ return ret("jsonld-keyword", "meta");
165
+ }
166
+ while ((next = stream.next()) != null) {
167
+ if (next == quote && !escaped) break;
168
+ escaped = !escaped && next == "\\";
169
+ }
170
+ if (!escaped) state.tokenize = tokenBase;
171
+ return ret("string", "string");
172
+ };
173
+ }
174
+
175
+ function tokenComment(stream, state) {
176
+ var maybeEnd = false, ch;
177
+ while (ch = stream.next()) {
178
+ if (ch == "/" && maybeEnd) {
179
+ state.tokenize = tokenBase;
180
+ break;
181
+ }
182
+ maybeEnd = (ch == "*");
183
+ }
184
+ return ret("comment", "comment");
185
+ }
186
+
187
+ function tokenQuasi(stream, state) {
188
+ var escaped = false, next;
189
+ while ((next = stream.next()) != null) {
190
+ if (!escaped && (next == "`" || next == "$" && stream.eat("{"))) {
191
+ state.tokenize = tokenBase;
192
+ break;
193
+ }
194
+ escaped = !escaped && next == "\\";
195
+ }
196
+ return ret("quasi", "string-2", stream.current());
197
+ }
198
+
199
+ var brackets = "([{}])";
200
+ // This is a crude lookahead trick to try and notice that we're
201
+ // parsing the argument patterns for a fat-arrow function before we
202
+ // actually hit the arrow token. It only works if the arrow is on
203
+ // the same line as the arguments and there's no strange noise
204
+ // (comments) in between. Fallback is to only notice when we hit the
205
+ // arrow, and not declare the arguments as locals for the arrow
206
+ // body.
207
+ function findFatArrow(stream, state) {
208
+ if (state.fatArrowAt) state.fatArrowAt = null;
209
+ var arrow = stream.string.indexOf("=>", stream.start);
210
+ if (arrow < 0) return;
211
+
212
+ var depth = 0, sawSomething = false;
213
+ for (var pos = arrow - 1; pos >= 0; --pos) {
214
+ var ch = stream.string.charAt(pos);
215
+ var bracket = brackets.indexOf(ch);
216
+ if (bracket >= 0 && bracket < 3) {
217
+ if (!depth) { ++pos; break; }
218
+ if (--depth == 0) break;
219
+ } else if (bracket >= 3 && bracket < 6) {
220
+ ++depth;
221
+ } else if (wordRE.test(ch)) {
222
+ sawSomething = true;
223
+ } else if (/["'\/]/.test(ch)) {
224
+ return;
225
+ } else if (sawSomething && !depth) {
226
+ ++pos;
227
+ break;
228
+ }
229
+ }
230
+ if (sawSomething && !depth) state.fatArrowAt = pos;
231
+ }
232
+
233
+ // Parser
234
+
235
+ var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true, "this": true, "jsonld-keyword": true};
236
+
237
+ function JSLexical(indented, column, type, align, prev, info) {
238
+ this.indented = indented;
239
+ this.column = column;
240
+ this.type = type;
241
+ this.prev = prev;
242
+ this.info = info;
243
+ if (align != null) this.align = align;
244
+ }
245
+
246
+ function inScope(state, varname) {
247
+ for (var v = state.localVars; v; v = v.next)
248
+ if (v.name == varname) return true;
249
+ for (var cx = state.context; cx; cx = cx.prev) {
250
+ for (var v = cx.vars; v; v = v.next)
251
+ if (v.name == varname) return true;
252
+ }
253
+ }
254
+
255
+ function parseJS(state, style, type, content, stream) {
256
+ var cc = state.cc;
257
+ // Communicate our context to the combinators.
258
+ // (Less wasteful than consing up a hundred closures on every call.)
259
+ cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc; cx.style = style;
260
+
261
+ if (!state.lexical.hasOwnProperty("align"))
262
+ state.lexical.align = true;
263
+
264
+ while(true) {
265
+ var combinator = cc.length ? cc.pop() : jsonMode ? expression : statement;
266
+ if (combinator(type, content)) {
267
+ while(cc.length && cc[cc.length - 1].lex)
268
+ cc.pop()();
269
+ if (cx.marked) return cx.marked;
270
+ if (type == "variable" && inScope(state, content)) return "variable-2";
271
+ return style;
272
+ }
273
+ }
274
+ }
275
+
276
+ // Combinator utils
277
+
278
+ var cx = {state: null, column: null, marked: null, cc: null};
279
+ function pass() {
280
+ for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]);
281
+ }
282
+ function cont() {
283
+ pass.apply(null, arguments);
284
+ return true;
285
+ }
286
+ function register(varname) {
287
+ function inList(list) {
288
+ for (var v = list; v; v = v.next)
289
+ if (v.name == varname) return true;
290
+ return false;
291
+ }
292
+ var state = cx.state;
293
+ cx.marked = "def";
294
+ if (state.context) {
295
+ if (inList(state.localVars)) return;
296
+ state.localVars = {name: varname, next: state.localVars};
297
+ } else {
298
+ if (inList(state.globalVars)) return;
299
+ if (parserConfig.globalVars)
300
+ state.globalVars = {name: varname, next: state.globalVars};
301
+ }
302
+ }
303
+
304
+ // Combinators
305
+
306
+ var defaultVars = {name: "this", next: {name: "arguments"}};
307
+ function pushcontext() {
308
+ cx.state.context = {prev: cx.state.context, vars: cx.state.localVars};
309
+ cx.state.localVars = defaultVars;
310
+ }
311
+ function popcontext() {
312
+ cx.state.localVars = cx.state.context.vars;
313
+ cx.state.context = cx.state.context.prev;
314
+ }
315
+ function pushlex(type, info) {
316
+ var result = function() {
317
+ var state = cx.state, indent = state.indented;
318
+ if (state.lexical.type == "stat") indent = state.lexical.indented;
319
+ else for (var outer = state.lexical; outer && outer.type == ")" && outer.align; outer = outer.prev)
320
+ indent = outer.indented;
321
+ state.lexical = new JSLexical(indent, cx.stream.column(), type, null, state.lexical, info);
322
+ };
323
+ result.lex = true;
324
+ return result;
325
+ }
326
+ function poplex() {
327
+ var state = cx.state;
328
+ if (state.lexical.prev) {
329
+ if (state.lexical.type == ")")
330
+ state.indented = state.lexical.indented;
331
+ state.lexical = state.lexical.prev;
332
+ }
333
+ }
334
+ poplex.lex = true;
335
+
336
+ function expect(wanted) {
337
+ function exp(type) {
338
+ if (type == wanted) return cont();
339
+ else if (wanted == ";") return pass();
340
+ else return cont(exp);
341
+ };
342
+ return exp;
343
+ }
344
+
345
+ function statement(type, value) {
346
+ if (type == "var") return cont(pushlex("vardef", value.length), vardef, expect(";"), poplex);
347
+ if (type == "keyword a") return cont(pushlex("form"), expression, statement, poplex);
348
+ if (type == "keyword b") return cont(pushlex("form"), statement, poplex);
349
+ if (type == "{") return cont(pushlex("}"), block, poplex);
350
+ if (type == ";") return cont();
351
+ if (type == "if") {
352
+ if (cx.state.lexical.info == "else" && cx.state.cc[cx.state.cc.length - 1] == poplex)
353
+ cx.state.cc.pop()();
354
+ return cont(pushlex("form"), expression, statement, poplex, maybeelse);
355
+ }
356
+ if (type == "function") return cont(functiondef);
357
+ if (type == "for") return cont(pushlex("form"), forspec, statement, poplex);
358
+ if (type == "variable") return cont(pushlex("stat"), maybelabel);
359
+ if (type == "switch") return cont(pushlex("form"), expression, pushlex("}", "switch"), expect("{"),
360
+ block, poplex, poplex);
361
+ if (type == "case") return cont(expression, expect(":"));
362
+ if (type == "default") return cont(expect(":"));
363
+ if (type == "catch") return cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"),
364
+ statement, poplex, popcontext);
365
+ if (type == "class") return cont(pushlex("form"), className, poplex);
366
+ if (type == "export") return cont(pushlex("stat"), afterExport, poplex);
367
+ if (type == "import") return cont(pushlex("stat"), afterImport, poplex);
368
+ if (type == "module") return cont(pushlex("form"), pattern, pushlex("}"), expect("{"), block, poplex, poplex)
369
+ return pass(pushlex("stat"), expression, expect(";"), poplex);
370
+ }
371
+ function expression(type) {
372
+ return expressionInner(type, false);
373
+ }
374
+ function expressionNoComma(type) {
375
+ return expressionInner(type, true);
376
+ }
377
+ function expressionInner(type, noComma) {
378
+ if (cx.state.fatArrowAt == cx.stream.start) {
379
+ var body = noComma ? arrowBodyNoComma : arrowBody;
380
+ if (type == "(") return cont(pushcontext, pushlex(")"), commasep(pattern, ")"), poplex, expect("=>"), body, popcontext);
381
+ else if (type == "variable") return pass(pushcontext, pattern, expect("=>"), body, popcontext);
382
+ }
383
+
384
+ var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma;
385
+ if (atomicTypes.hasOwnProperty(type)) return cont(maybeop);
386
+ if (type == "function") return cont(functiondef, maybeop);
387
+ if (type == "keyword c") return cont(noComma ? maybeexpressionNoComma : maybeexpression);
388
+ if (type == "(") return cont(pushlex(")"), maybeexpression, comprehension, expect(")"), poplex, maybeop);
389
+ if (type == "operator" || type == "spread") return cont(noComma ? expressionNoComma : expression);
390
+ if (type == "[") return cont(pushlex("]"), arrayLiteral, poplex, maybeop);
391
+ if (type == "{") return contCommasep(objprop, "}", null, maybeop);
392
+ if (type == "quasi") return pass(quasi, maybeop);
393
+ if (type == "new") return cont(maybeTarget(noComma));
394
+ return cont();
395
+ }
396
+ function maybeexpression(type) {
397
+ if (type.match(/[;\}\)\],]/)) return pass();
398
+ return pass(expression);
399
+ }
400
+ function maybeexpressionNoComma(type) {
401
+ if (type.match(/[;\}\)\],]/)) return pass();
402
+ return pass(expressionNoComma);
403
+ }
404
+
405
+ function maybeoperatorComma(type, value) {
406
+ if (type == ",") return cont(expression);
407
+ return maybeoperatorNoComma(type, value, false);
408
+ }
409
+ function maybeoperatorNoComma(type, value, noComma) {
410
+ var me = noComma == false ? maybeoperatorComma : maybeoperatorNoComma;
411
+ var expr = noComma == false ? expression : expressionNoComma;
412
+ if (type == "=>") return cont(pushcontext, noComma ? arrowBodyNoComma : arrowBody, popcontext);
413
+ if (type == "operator") {
414
+ if (/\+\+|--/.test(value)) return cont(me);
415
+ if (value == "?") return cont(expression, expect(":"), expr);
416
+ return cont(expr);
417
+ }
418
+ if (type == "quasi") { return pass(quasi, me); }
419
+ if (type == ";") return;
420
+ if (type == "(") return contCommasep(expressionNoComma, ")", "call", me);
421
+ if (type == ".") return cont(property, me);
422
+ if (type == "[") return cont(pushlex("]"), maybeexpression, expect("]"), poplex, me);
423
+ }
424
+ function quasi(type, value) {
425
+ if (type != "quasi") return pass();
426
+ if (value.slice(value.length - 2) != "${") return cont(quasi);
427
+ return cont(expression, continueQuasi);
428
+ }
429
+ function continueQuasi(type) {
430
+ if (type == "}") {
431
+ cx.marked = "string-2";
432
+ cx.state.tokenize = tokenQuasi;
433
+ return cont(quasi);
434
+ }
435
+ }
436
+ function arrowBody(type) {
437
+ findFatArrow(cx.stream, cx.state);
438
+ return pass(type == "{" ? statement : expression);
439
+ }
440
+ function arrowBodyNoComma(type) {
441
+ findFatArrow(cx.stream, cx.state);
442
+ return pass(type == "{" ? statement : expressionNoComma);
443
+ }
444
+ function maybeTarget(noComma) {
445
+ return function(type) {
446
+ if (type == ".") return cont(noComma ? targetNoComma : target);
447
+ else return pass(noComma ? expressionNoComma : expression);
448
+ };
449
+ }
450
+ function target(_, value) {
451
+ if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorComma); }
452
+ }
453
+ function targetNoComma(_, value) {
454
+ if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorNoComma); }
455
+ }
456
+ function maybelabel(type) {
457
+ if (type == ":") return cont(poplex, statement);
458
+ return pass(maybeoperatorComma, expect(";"), poplex);
459
+ }
460
+ function property(type) {
461
+ if (type == "variable") {cx.marked = "property"; return cont();}
462
+ }
463
+ function objprop(type, value) {
464
+ if (type == "variable" || cx.style == "keyword") {
465
+ cx.marked = "property";
466
+ if (value == "get" || value == "set") return cont(getterSetter);
467
+ return cont(afterprop);
468
+ } else if (type == "number" || type == "string") {
469
+ cx.marked = jsonldMode ? "property" : (cx.style + " property");
470
+ return cont(afterprop);
471
+ } else if (type == "jsonld-keyword") {
472
+ return cont(afterprop);
473
+ } else if (type == "modifier") {
474
+ return cont(objprop)
475
+ } else if (type == "[") {
476
+ return cont(expression, expect("]"), afterprop);
477
+ } else if (type == "spread") {
478
+ return cont(expression);
479
+ }
480
+ }
481
+ function getterSetter(type) {
482
+ if (type != "variable") return pass(afterprop);
483
+ cx.marked = "property";
484
+ return cont(functiondef);
485
+ }
486
+ function afterprop(type) {
487
+ if (type == ":") return cont(expressionNoComma);
488
+ if (type == "(") return pass(functiondef);
489
+ }
490
+ function commasep(what, end) {
491
+ function proceed(type) {
492
+ if (type == ",") {
493
+ var lex = cx.state.lexical;
494
+ if (lex.info == "call") lex.pos = (lex.pos || 0) + 1;
495
+ return cont(what, proceed);
496
+ }
497
+ if (type == end) return cont();
498
+ return cont(expect(end));
499
+ }
500
+ return function(type) {
501
+ if (type == end) return cont();
502
+ return pass(what, proceed);
503
+ };
504
+ }
505
+ function contCommasep(what, end, info) {
506
+ for (var i = 3; i < arguments.length; i++)
507
+ cx.cc.push(arguments[i]);
508
+ return cont(pushlex(end, info), commasep(what, end), poplex);
509
+ }
510
+ function block(type) {
511
+ if (type == "}") return cont();
512
+ return pass(statement, block);
513
+ }
514
+ function maybetype(type) {
515
+ if (isTS && type == ":") return cont(typedef);
516
+ }
517
+ function maybedefault(_, value) {
518
+ if (value == "=") return cont(expressionNoComma);
519
+ }
520
+ function typedef(type) {
521
+ if (type == "variable") {cx.marked = "variable-3"; return cont();}
522
+ }
523
+ function vardef() {
524
+ return pass(pattern, maybetype, maybeAssign, vardefCont);
525
+ }
526
+ function pattern(type, value) {
527
+ if (type == "modifier") return cont(pattern)
528
+ if (type == "variable") { register(value); return cont(); }
529
+ if (type == "spread") return cont(pattern);
530
+ if (type == "[") return contCommasep(pattern, "]");
531
+ if (type == "{") return contCommasep(proppattern, "}");
532
+ }
533
+ function proppattern(type, value) {
534
+ if (type == "variable" && !cx.stream.match(/^\s*:/, false)) {
535
+ register(value);
536
+ return cont(maybeAssign);
537
+ }
538
+ if (type == "variable") cx.marked = "property";
539
+ if (type == "spread") return cont(pattern);
540
+ if (type == "}") return pass();
541
+ return cont(expect(":"), pattern, maybeAssign);
542
+ }
543
+ function maybeAssign(_type, value) {
544
+ if (value == "=") return cont(expressionNoComma);
545
+ }
546
+ function vardefCont(type) {
547
+ if (type == ",") return cont(vardef);
548
+ }
549
+ function maybeelse(type, value) {
550
+ if (type == "keyword b" && value == "else") return cont(pushlex("form", "else"), statement, poplex);
551
+ }
552
+ function forspec(type) {
553
+ if (type == "(") return cont(pushlex(")"), forspec1, expect(")"), poplex);
554
+ }
555
+ function forspec1(type) {
556
+ if (type == "var") return cont(vardef, expect(";"), forspec2);
557
+ if (type == ";") return cont(forspec2);
558
+ if (type == "variable") return cont(formaybeinof);
559
+ return pass(expression, expect(";"), forspec2);
560
+ }
561
+ function formaybeinof(_type, value) {
562
+ if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression); }
563
+ return cont(maybeoperatorComma, forspec2);
564
+ }
565
+ function forspec2(type, value) {
566
+ if (type == ";") return cont(forspec3);
567
+ if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression); }
568
+ return pass(expression, expect(";"), forspec3);
569
+ }
570
+ function forspec3(type) {
571
+ if (type != ")") cont(expression);
572
+ }
573
+ function functiondef(type, value) {
574
+ if (value == "*") {cx.marked = "keyword"; return cont(functiondef);}
575
+ if (type == "variable") {register(value); return cont(functiondef);}
576
+ if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, statement, popcontext);
577
+ }
578
+ function funarg(type) {
579
+ if (type == "spread") return cont(funarg);
580
+ return pass(pattern, maybetype, maybedefault);
581
+ }
582
+ function className(type, value) {
583
+ if (type == "variable") {register(value); return cont(classNameAfter);}
584
+ }
585
+ function classNameAfter(type, value) {
586
+ if (value == "extends") return cont(expression, classNameAfter);
587
+ if (type == "{") return cont(pushlex("}"), classBody, poplex);
588
+ }
589
+ function classBody(type, value) {
590
+ if (type == "variable" || cx.style == "keyword") {
591
+ if (value == "static") {
592
+ cx.marked = "keyword";
593
+ return cont(classBody);
594
+ }
595
+ cx.marked = "property";
596
+ if (value == "get" || value == "set") return cont(classGetterSetter, functiondef, classBody);
597
+ return cont(functiondef, classBody);
598
+ }
599
+ if (value == "*") {
600
+ cx.marked = "keyword";
601
+ return cont(classBody);
602
+ }
603
+ if (type == ";") return cont(classBody);
604
+ if (type == "}") return cont();
605
+ }
606
+ function classGetterSetter(type) {
607
+ if (type != "variable") return pass();
608
+ cx.marked = "property";
609
+ return cont();
610
+ }
611
+ function afterExport(_type, value) {
612
+ if (value == "*") { cx.marked = "keyword"; return cont(maybeFrom, expect(";")); }
613
+ if (value == "default") { cx.marked = "keyword"; return cont(expression, expect(";")); }
614
+ return pass(statement);
615
+ }
616
+ function afterImport(type) {
617
+ if (type == "string") return cont();
618
+ return pass(importSpec, maybeFrom);
619
+ }
620
+ function importSpec(type, value) {
621
+ if (type == "{") return contCommasep(importSpec, "}");
622
+ if (type == "variable") register(value);
623
+ if (value == "*") cx.marked = "keyword";
624
+ return cont(maybeAs);
625
+ }
626
+ function maybeAs(_type, value) {
627
+ if (value == "as") { cx.marked = "keyword"; return cont(importSpec); }
628
+ }
629
+ function maybeFrom(_type, value) {
630
+ if (value == "from") { cx.marked = "keyword"; return cont(expression); }
631
+ }
632
+ function arrayLiteral(type) {
633
+ if (type == "]") return cont();
634
+ return pass(expressionNoComma, maybeArrayComprehension);
635
+ }
636
+ function maybeArrayComprehension(type) {
637
+ if (type == "for") return pass(comprehension, expect("]"));
638
+ if (type == ",") return cont(commasep(maybeexpressionNoComma, "]"));
639
+ return pass(commasep(expressionNoComma, "]"));
640
+ }
641
+ function comprehension(type) {
642
+ if (type == "for") return cont(forspec, comprehension);
643
+ if (type == "if") return cont(expression, comprehension);
644
+ }
645
+
646
+ function isContinuedStatement(state, textAfter) {
647
+ return state.lastType == "operator" || state.lastType == "," ||
648
+ isOperatorChar.test(textAfter.charAt(0)) ||
649
+ /[,.]/.test(textAfter.charAt(0));
650
+ }
651
+
652
+ // Interface
653
+
654
+ return {
655
+ startState: function(basecolumn) {
656
+ var state = {
657
+ tokenize: tokenBase,
658
+ lastType: "sof",
659
+ cc: [],
660
+ lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false),
661
+ localVars: parserConfig.localVars,
662
+ context: parserConfig.localVars && {vars: parserConfig.localVars},
663
+ indented: basecolumn || 0
664
+ };
665
+ if (parserConfig.globalVars && typeof parserConfig.globalVars == "object")
666
+ state.globalVars = parserConfig.globalVars;
667
+ return state;
668
+ },
669
+
670
+ token: function(stream, state) {
671
+ if (stream.sol()) {
672
+ if (!state.lexical.hasOwnProperty("align"))
673
+ state.lexical.align = false;
674
+ state.indented = stream.indentation();
675
+ findFatArrow(stream, state);
676
+ }
677
+ if (state.tokenize != tokenComment && stream.eatSpace()) return null;
678
+ var style = state.tokenize(stream, state);
679
+ if (type == "comment") return style;
680
+ state.lastType = type == "operator" && (content == "++" || content == "--") ? "incdec" : type;
681
+ return parseJS(state, style, type, content, stream);
682
+ },
683
+
684
+ indent: function(state, textAfter) {
685
+ if (state.tokenize == tokenComment) return CodeMirror.Pass;
686
+ if (state.tokenize != tokenBase) return 0;
687
+ var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical;
688
+ // Kludge to prevent 'maybelse' from blocking lexical scope pops
689
+ if (!/^\s*else\b/.test(textAfter)) for (var i = state.cc.length - 1; i >= 0; --i) {
690
+ var c = state.cc[i];
691
+ if (c == poplex) lexical = lexical.prev;
692
+ else if (c != maybeelse) break;
693
+ }
694
+ if (lexical.type == "stat" && firstChar == "}") lexical = lexical.prev;
695
+ if (statementIndent && lexical.type == ")" && lexical.prev.type == "stat")
696
+ lexical = lexical.prev;
697
+ var type = lexical.type, closing = firstChar == type;
698
+
699
+ if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? lexical.info + 1 : 0);
700
+ else if (type == "form" && firstChar == "{") return lexical.indented;
701
+ else if (type == "form") return lexical.indented + indentUnit;
702
+ else if (type == "stat")
703
+ return lexical.indented + (isContinuedStatement(state, textAfter) ? statementIndent || indentUnit : 0);
704
+ else if (lexical.info == "switch" && !closing && parserConfig.doubleIndentSwitch != false)
705
+ return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit);
706
+ else if (lexical.align) return lexical.column + (closing ? 0 : 1);
707
+ else return lexical.indented + (closing ? 0 : indentUnit);
708
+ },
709
+
710
+ electricInput: /^\s*(?:case .*?:|default:|\{|\})$/,
711
+ blockCommentStart: jsonMode ? null : "/*",
712
+ blockCommentEnd: jsonMode ? null : "*/",
713
+ lineComment: jsonMode ? null : "//",
714
+ fold: "brace",
715
+ closeBrackets: "()[]{}''\"\"``",
716
+
717
+ helperType: jsonMode ? "json" : "javascript",
718
+ jsonldMode: jsonldMode,
719
+ jsonMode: jsonMode,
720
+
721
+ expressionAllowed: expressionAllowed,
722
+ skipExpression: function(state) {
723
+ var top = state.cc[state.cc.length - 1]
724
+ if (top == expression || top == expressionNoComma) state.cc.pop()
725
+ }
726
+ };
727
+ });
728
+
729
+ CodeMirror.registerHelper("wordChars", "javascript", /[\w$]/);
730
+
731
+ CodeMirror.defineMIME("text/javascript", "javascript");
732
+ CodeMirror.defineMIME("text/ecmascript", "javascript");
733
+ CodeMirror.defineMIME("application/javascript", "javascript");
734
+ CodeMirror.defineMIME("application/x-javascript", "javascript");
735
+ CodeMirror.defineMIME("application/ecmascript", "javascript");
736
+ CodeMirror.defineMIME("application/json", {name: "javascript", json: true});
737
+ CodeMirror.defineMIME("application/x-json", {name: "javascript", json: true});
738
+ CodeMirror.defineMIME("application/ld+json", {name: "javascript", jsonld: true});
739
+ CodeMirror.defineMIME("text/typescript", { name: "javascript", typescript: true });
740
+ CodeMirror.defineMIME("application/typescript", { name: "javascript", typescript: true });
741
+
742
+ });
codemirror/mode/javascript/json-ld.html ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!doctype html>
2
+
3
+ <title>CodeMirror: JSON-LD mode</title>
4
+ <meta charset="utf-8"/>
5
+ <link rel=stylesheet href="../../doc/docs.css">
6
+
7
+ <link rel="stylesheet" href="../../lib/codemirror.css">
8
+ <script src="../../lib/codemirror.js"></script>
9
+ <script src="../../addon/edit/matchbrackets.js"></script>
10
+ <script src="../../addon/comment/continuecomment.js"></script>
11
+ <script src="../../addon/comment/comment.js"></script>
12
+ <script src="javascript.js"></script>
13
+ <style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
14
+ <div id="nav">
15
+ <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"/></a>
16
+
17
+ <ul>
18
+ <li><a href="../../index.html">Home</a>
19
+ <li><a href="../../doc/manual.html">Manual</a>
20
+ <li><a href="https://github.com/codemirror/codemirror">Code</a>
21
+ </ul>
22
+ <ul>
23
+ <li><a href="../index.html">Language modes</a>
24
+ <li><a class=active href="#">JSON-LD</a>
25
+ </ul>
26
+ </div>
27
+
28
+ <article>
29
+ <h2>JSON-LD mode</h2>
30
+
31
+
32
+ <div><textarea id="code" name="code">
33
+ {
34
+ "@context": {
35
+ "name": "http://schema.org/name",
36
+ "description": "http://schema.org/description",
37
+ "image": {
38
+ "@id": "http://schema.org/image",
39
+ "@type": "@id"
40
+ },
41
+ "geo": "http://schema.org/geo",
42
+ "latitude": {
43
+ "@id": "http://schema.org/latitude",
44
+ "@type": "xsd:float"
45
+ },
46
+ "longitude": {
47
+ "@id": "http://schema.org/longitude",
48
+ "@type": "xsd:float"
49
+ },
50
+ "xsd": "http://www.w3.org/2001/XMLSchema#"
51
+ },
52
+ "name": "The Empire State Building",
53
+ "description": "The Empire State Building is a 102-story landmark in New York City.",
54
+ "image": "http://www.civil.usherbrooke.ca/cours/gci215a/empire-state-building.jpg",
55
+ "geo": {
56
+ "latitude": "40.75",
57
+ "longitude": "73.98"
58
+ }
59
+ }
60
+ </textarea></div>
61
+
62
+ <script>
63
+ var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
64
+ matchBrackets: true,
65
+ autoCloseBrackets: true,
66
+ mode: "application/ld+json",
67
+ lineWrapping: true
68
+ });
69
+ </script>
70
+
71
+ <p>This is a specialization of the <a href="index.html">JavaScript mode</a>.</p>
72
+ </article>
codemirror/mode/javascript/test.js ADDED
@@ -0,0 +1,210 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // CodeMirror, copyright (c) by Marijn Haverbeke and others
2
+ // Distributed under an MIT license: http://codemirror.net/LICENSE
3
+
4
+ (function() {
5
+ var mode = CodeMirror.getMode({indentUnit: 2}, "javascript");
6
+ function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); }
7
+
8
+ MT("locals",
9
+ "[keyword function] [def foo]([def a], [def b]) { [keyword var] [def c] [operator =] [number 10]; [keyword return] [variable-2 a] [operator +] [variable-2 c] [operator +] [variable d]; }");
10
+
11
+ MT("comma-and-binop",
12
+ "[keyword function](){ [keyword var] [def x] [operator =] [number 1] [operator +] [number 2], [def y]; }");
13
+
14
+ MT("destructuring",
15
+ "([keyword function]([def a], [[[def b], [def c] ]]) {",
16
+ " [keyword let] {[def d], [property foo]: [def c][operator =][number 10], [def x]} [operator =] [variable foo]([variable-2 a]);",
17
+ " [[[variable-2 c], [variable y] ]] [operator =] [variable-2 c];",
18
+ "})();");
19
+
20
+ MT("destructure_trailing_comma",
21
+ "[keyword let] {[def a], [def b],} [operator =] [variable foo];",
22
+ "[keyword let] [def c];"); // Parser still in good state?
23
+
24
+ MT("class_body",
25
+ "[keyword class] [def Foo] {",
26
+ " [property constructor]() {}",
27
+ " [property sayName]() {",
28
+ " [keyword return] [string-2 `foo${][variable foo][string-2 }oo`];",
29
+ " }",
30
+ "}");
31
+
32
+ MT("class",
33
+ "[keyword class] [def Point] [keyword extends] [variable SuperThing] {",
34
+ " [property get] [property prop]() { [keyword return] [number 24]; }",
35
+ " [property constructor]([def x], [def y]) {",
36
+ " [keyword super]([string 'something']);",
37
+ " [keyword this].[property x] [operator =] [variable-2 x];",
38
+ " }",
39
+ "}");
40
+
41
+ MT("import",
42
+ "[keyword function] [def foo]() {",
43
+ " [keyword import] [def $] [keyword from] [string 'jquery'];",
44
+ " [keyword import] { [def encrypt], [def decrypt] } [keyword from] [string 'crypto'];",
45
+ "}");
46
+
47
+ MT("const",
48
+ "[keyword function] [def f]() {",
49
+ " [keyword const] [[ [def a], [def b] ]] [operator =] [[ [number 1], [number 2] ]];",
50
+ "}");
51
+
52
+ MT("for/of",
53
+ "[keyword for]([keyword let] [def of] [keyword of] [variable something]) {}");
54
+
55
+ MT("generator",
56
+ "[keyword function*] [def repeat]([def n]) {",
57
+ " [keyword for]([keyword var] [def i] [operator =] [number 0]; [variable-2 i] [operator <] [variable-2 n]; [operator ++][variable-2 i])",
58
+ " [keyword yield] [variable-2 i];",
59
+ "}");
60
+
61
+ MT("quotedStringAddition",
62
+ "[keyword let] [def f] [operator =] [variable a] [operator +] [string 'fatarrow'] [operator +] [variable c];");
63
+
64
+ MT("quotedFatArrow",
65
+ "[keyword let] [def f] [operator =] [variable a] [operator +] [string '=>'] [operator +] [variable c];");
66
+
67
+ MT("fatArrow",
68
+ "[variable array].[property filter]([def a] [operator =>] [variable-2 a] [operator +] [number 1]);",
69
+ "[variable a];", // No longer in scope
70
+ "[keyword let] [def f] [operator =] ([[ [def a], [def b] ]], [def c]) [operator =>] [variable-2 a] [operator +] [variable-2 c];",
71
+ "[variable c];");
72
+
73
+ MT("spread",
74
+ "[keyword function] [def f]([def a], [meta ...][def b]) {",
75
+ " [variable something]([variable-2 a], [meta ...][variable-2 b]);",
76
+ "}");
77
+
78
+ MT("comprehension",
79
+ "[keyword function] [def f]() {",
80
+ " [[([variable x] [operator +] [number 1]) [keyword for] ([keyword var] [def x] [keyword in] [variable y]) [keyword if] [variable pred]([variable-2 x]) ]];",
81
+ " ([variable u] [keyword for] ([keyword var] [def u] [keyword of] [variable generateValues]()) [keyword if] ([variable-2 u].[property color] [operator ===] [string 'blue']));",
82
+ "}");
83
+
84
+ MT("quasi",
85
+ "[variable re][string-2 `fofdlakj${][variable x] [operator +] ([variable re][string-2 `foo`]) [operator +] [number 1][string-2 }fdsa`] [operator +] [number 2]");
86
+
87
+ MT("quasi_no_function",
88
+ "[variable x] [operator =] [string-2 `fofdlakj${][variable x] [operator +] [string-2 `foo`] [operator +] [number 1][string-2 }fdsa`] [operator +] [number 2]");
89
+
90
+ MT("indent_statement",
91
+ "[keyword var] [def x] [operator =] [number 10]",
92
+ "[variable x] [operator +=] [variable y] [operator +]",
93
+ " [atom Infinity]",
94
+ "[keyword debugger];");
95
+
96
+ MT("indent_if",
97
+ "[keyword if] ([number 1])",
98
+ " [keyword break];",
99
+ "[keyword else] [keyword if] ([number 2])",
100
+ " [keyword continue];",
101
+ "[keyword else]",
102
+ " [number 10];",
103
+ "[keyword if] ([number 1]) {",
104
+ " [keyword break];",
105
+ "} [keyword else] [keyword if] ([number 2]) {",
106
+ " [keyword continue];",
107
+ "} [keyword else] {",
108
+ " [number 10];",
109
+ "}");
110
+
111
+ MT("indent_for",
112
+ "[keyword for] ([keyword var] [def i] [operator =] [number 0];",
113
+ " [variable i] [operator <] [number 100];",
114
+ " [variable i][operator ++])",
115
+ " [variable doSomething]([variable i]);",
116
+ "[keyword debugger];");
117
+
118
+ MT("indent_c_style",
119
+ "[keyword function] [def foo]()",
120
+ "{",
121
+ " [keyword debugger];",
122
+ "}");
123
+
124
+ MT("indent_else",
125
+ "[keyword for] (;;)",
126
+ " [keyword if] ([variable foo])",
127
+ " [keyword if] ([variable bar])",
128
+ " [number 1];",
129
+ " [keyword else]",
130
+ " [number 2];",
131
+ " [keyword else]",
132
+ " [number 3];");
133
+
134
+ MT("indent_funarg",
135
+ "[variable foo]([number 10000],",
136
+ " [keyword function]([def a]) {",
137
+ " [keyword debugger];",
138
+ "};");
139
+
140
+ MT("indent_below_if",
141
+ "[keyword for] (;;)",
142
+ " [keyword if] ([variable foo])",
143
+ " [number 1];",
144
+ "[number 2];");
145
+
146
+ MT("multilinestring",
147
+ "[keyword var] [def x] [operator =] [string 'foo\\]",
148
+ "[string bar'];");
149
+
150
+ MT("scary_regexp",
151
+ "[string-2 /foo[[/]]bar/];");
152
+
153
+ MT("indent_strange_array",
154
+ "[keyword var] [def x] [operator =] [[",
155
+ " [number 1],,",
156
+ " [number 2],",
157
+ "]];",
158
+ "[number 10];");
159
+
160
+ MT("param_default",
161
+ "[keyword function] [def foo]([def x] [operator =] [string-2 `foo${][number 10][string-2 }bar`]) {",
162
+ " [keyword return] [variable-2 x];",
163
+ "}");
164
+
165
+ MT("new_target",
166
+ "[keyword function] [def F]([def target]) {",
167
+ " [keyword if] ([variable-2 target] [operator &&] [keyword new].[keyword target].[property name]) {",
168
+ " [keyword return] [keyword new]",
169
+ " .[keyword target];",
170
+ " }",
171
+ "}");
172
+
173
+ var jsonld_mode = CodeMirror.getMode(
174
+ {indentUnit: 2},
175
+ {name: "javascript", jsonld: true}
176
+ );
177
+ function LD(name) {
178
+ test.mode(name, jsonld_mode, Array.prototype.slice.call(arguments, 1));
179
+ }
180
+
181
+ LD("json_ld_keywords",
182
+ '{',
183
+ ' [meta "@context"]: {',
184
+ ' [meta "@base"]: [string "http://example.com"],',
185
+ ' [meta "@vocab"]: [string "http://xmlns.com/foaf/0.1/"],',
186
+ ' [property "likesFlavor"]: {',
187
+ ' [meta "@container"]: [meta "@list"]',
188
+ ' [meta "@reverse"]: [string "@beFavoriteOf"]',
189
+ ' },',
190
+ ' [property "nick"]: { [meta "@container"]: [meta "@set"] },',
191
+ ' [property "nick"]: { [meta "@container"]: [meta "@index"] }',
192
+ ' },',
193
+ ' [meta "@graph"]: [[ {',
194
+ ' [meta "@id"]: [string "http://dbpedia.org/resource/John_Lennon"],',
195
+ ' [property "name"]: [string "John Lennon"],',
196
+ ' [property "modified"]: {',
197
+ ' [meta "@value"]: [string "2010-05-29T14:17:39+02:00"],',
198
+ ' [meta "@type"]: [string "http://www.w3.org/2001/XMLSchema#dateTime"]',
199
+ ' }',
200
+ ' } ]]',
201
+ '}');
202
+
203
+ LD("json_ld_fake",
204
+ '{',
205
+ ' [property "@fake"]: [string "@fake"],',
206
+ ' [property "@contextual"]: [string "@identifier"],',
207
+ ' [property "user@domain.com"]: [string "@graphical"],',
208
+ ' [property "@ID"]: [string "@@ID"]',
209
+ '}');
210
+ })();
codemirror/mode/javascript/typescript.html ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!doctype html>
2
+
3
+ <title>CodeMirror: TypeScript mode</title>
4
+ <meta charset="utf-8"/>
5
+ <link rel=stylesheet href="../../doc/docs.css">
6
+
7
+ <link rel="stylesheet" href="../../lib/codemirror.css">
8
+ <script src="../../lib/codemirror.js"></script>
9
+ <script src="javascript.js"></script>
10
+ <style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
11
+ <div id=nav>
12
+ <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
13
+
14
+ <ul>
15
+ <li><a href="../../index.html">Home</a>
16
+ <li><a href="../../doc/manual.html">Manual</a>
17
+ <li><a href="https://github.com/codemirror/codemirror">Code</a>
18
+ </ul>
19
+ <ul>
20
+ <li><a href="../index.html">Language modes</a>
21
+ <li><a class=active href="#">TypeScript</a>
22
+ </ul>
23
+ </div>
24
+
25
+ <article>
26
+ <h2>TypeScript mode</h2>
27
+
28
+
29
+ <div><textarea id="code" name="code">
30
+ class Greeter {
31
+ greeting: string;
32
+ constructor (message: string) {
33
+ this.greeting = message;
34
+ }
35
+ greet() {
36
+ return "Hello, " + this.greeting;
37
+ }
38
+ }
39
+
40
+ var greeter = new Greeter("world");
41
+
42
+ var button = document.createElement('button')
43
+ button.innerText = "Say Hello"
44
+ button.onclick = function() {
45
+ alert(greeter.greet())
46
+ }
47
+
48
+ document.body.appendChild(button)
49
+
50
+ </textarea></div>
51
+
52
+ <script>
53
+ var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
54
+ lineNumbers: true,
55
+ matchBrackets: true,
56
+ mode: "text/typescript"
57
+ });
58
+ </script>
59
+
60
+ <p>This is a specialization of the <a href="index.html">JavaScript mode</a>.</p>
61
+ </article>
codemirror/mode/meta.js ADDED
@@ -0,0 +1,202 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // CodeMirror, copyright (c) by Marijn Haverbeke and others
2
+ // Distributed under an MIT license: http://codemirror.net/LICENSE
3
+
4
+ (function(mod) {
5
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
6
+ mod(require("../lib/codemirror"));
7
+ else if (typeof define == "function" && define.amd) // AMD
8
+ define(["../lib/codemirror"], mod);
9
+ else // Plain browser env
10
+ mod(CodeMirror);
11
+ })(function(CodeMirror) {
12
+ "use strict";
13
+
14
+ CodeMirror.modeInfo = [
15
+ {name: "APL", mime: "text/apl", mode: "apl", ext: ["dyalog", "apl"]},
16
+ {name: "PGP", mimes: ["application/pgp", "application/pgp-keys", "application/pgp-signature"], mode: "asciiarmor", ext: ["pgp"]},
17
+ {name: "ASN.1", mime: "text/x-ttcn-asn", mode: "asn.1", ext: ["asn", "asn1"]},
18
+ {name: "Asterisk", mime: "text/x-asterisk", mode: "asterisk", file: /^extensions\.conf$/i},
19
+ {name: "Brainfuck", mime: "text/x-brainfuck", mode: "brainfuck", ext: ["b", "bf"]},
20
+ {name: "C", mime: "text/x-csrc", mode: "clike", ext: ["c", "h"]},
21
+ {name: "C++", mime: "text/x-c++src", mode: "clike", ext: ["cpp", "c++", "cc", "cxx", "hpp", "h++", "hh", "hxx"], alias: ["cpp"]},
22
+ {name: "Cobol", mime: "text/x-cobol", mode: "cobol", ext: ["cob", "cpy"]},
23
+ {name: "C#", mime: "text/x-csharp", mode: "clike", ext: ["cs"], alias: ["csharp"]},
24
+ {name: "Clojure", mime: "text/x-clojure", mode: "clojure", ext: ["clj"]},
25
+ {name: "ClojureScript", mime: "text/x-clojurescript", mode: "clojure", ext: ["cljs"]},
26
+ {name: "Closure Stylesheets (GSS)", mime: "text/x-gss", mode: "css", ext: ["gss"]},
27
+ {name: "CMake", mime: "text/x-cmake", mode: "cmake", ext: ["cmake", "cmake.in"], file: /^CMakeLists.txt$/},
28
+ {name: "CoffeeScript", mime: "text/x-coffeescript", mode: "coffeescript", ext: ["coffee"], alias: ["coffee", "coffee-script"]},
29
+ {name: "Common Lisp", mime: "text/x-common-lisp", mode: "commonlisp", ext: ["cl", "lisp", "el"], alias: ["lisp"]},
30
+ {name: "Cypher", mime: "application/x-cypher-query", mode: "cypher", ext: ["cyp", "cypher"]},
31
+ {name: "Cython", mime: "text/x-cython", mode: "python", ext: ["pyx", "pxd", "pxi"]},
32
+ {name: "Crystal", mime: "text/x-crystal", mode: "crystal", ext: ["cr"]},
33
+ {name: "CSS", mime: "text/css", mode: "css", ext: ["css"]},
34
+ {name: "CQL", mime: "text/x-cassandra", mode: "sql", ext: ["cql"]},
35
+ {name: "D", mime: "text/x-d", mode: "d", ext: ["d"]},
36
+ {name: "Dart", mimes: ["application/dart", "text/x-dart"], mode: "dart", ext: ["dart"]},
37
+ {name: "diff", mime: "text/x-diff", mode: "diff", ext: ["diff", "patch"]},
38
+ {name: "Django", mime: "text/x-django", mode: "django"},
39
+ {name: "Dockerfile", mime: "text/x-dockerfile", mode: "dockerfile", file: /^Dockerfile$/},
40
+ {name: "DTD", mime: "application/xml-dtd", mode: "dtd", ext: ["dtd"]},
41
+ {name: "Dylan", mime: "text/x-dylan", mode: "dylan", ext: ["dylan", "dyl", "intr"]},
42
+ {name: "EBNF", mime: "text/x-ebnf", mode: "ebnf"},
43
+ {name: "ECL", mime: "text/x-ecl", mode: "ecl", ext: ["ecl"]},
44
+ {name: "Eiffel", mime: "text/x-eiffel", mode: "eiffel", ext: ["e"]},
45
+ {name: "Elm", mime: "text/x-elm", mode: "elm", ext: ["elm"]},
46
+ {name: "Embedded Javascript", mime: "application/x-ejs", mode: "htmlembedded", ext: ["ejs"]},
47
+ {name: "Embedded Ruby", mime: "application/x-erb", mode: "htmlembedded", ext: ["erb"]},
48
+ {name: "Erlang", mime: "text/x-erlang", mode: "erlang", ext: ["erl"]},
49
+ {name: "Factor", mime: "text/x-factor", mode: "factor", ext: ["factor"]},
50
+ {name: "FCL", mime: "text/x-fcl", mode: "fcl"},
51
+ {name: "Forth", mime: "text/x-forth", mode: "forth", ext: ["forth", "fth", "4th"]},
52
+ {name: "Fortran", mime: "text/x-fortran", mode: "fortran", ext: ["f", "for", "f77", "f90"]},
53
+ {name: "F#", mime: "text/x-fsharp", mode: "mllike", ext: ["fs"], alias: ["fsharp"]},
54
+ {name: "Gas", mime: "text/x-gas", mode: "gas", ext: ["s"]},
55
+ {name: "Gherkin", mime: "text/x-feature", mode: "gherkin", ext: ["feature"]},
56
+ {name: "GitHub Flavored Markdown", mime: "text/x-gfm", mode: "gfm", file: /^(readme|contributing|history).md$/i},
57
+ {name: "Go", mime: "text/x-go", mode: "go", ext: ["go"]},
58
+ {name: "Groovy", mime: "text/x-groovy", mode: "groovy", ext: ["groovy", "gradle"]},
59
+ {name: "HAML", mime: "text/x-haml", mode: "haml", ext: ["haml"]},
60
+ {name: "Haskell", mime: "text/x-haskell", mode: "haskell", ext: ["hs"]},
61
+ {name: "Haskell (Literate)", mime: "text/x-literate-haskell", mode: "haskell-literate", ext: ["lhs"]},
62
+ {name: "Haxe", mime: "text/x-haxe", mode: "haxe", ext: ["hx"]},
63
+ {name: "HXML", mime: "text/x-hxml", mode: "haxe", ext: ["hxml"]},
64
+ {name: "ASP.NET", mime: "application/x-aspx", mode: "htmlembedded", ext: ["aspx"], alias: ["asp", "aspx"]},
65
+ {name: "HTML", mime: "text/html", mode: "htmlmixed", ext: ["html", "htm"], alias: ["xhtml"]},
66
+ {name: "HTTP", mime: "message/http", mode: "http"},
67
+ {name: "IDL", mime: "text/x-idl", mode: "idl", ext: ["pro"]},
68
+ {name: "Jade", mime: "text/x-jade", mode: "jade", ext: ["jade"]},
69
+ {name: "Java", mime: "text/x-java", mode: "clike", ext: ["java"]},
70
+ {name: "Java Server Pages", mime: "application/x-jsp", mode: "htmlembedded", ext: ["jsp"], alias: ["jsp"]},
71
+ {name: "JavaScript", mimes: ["text/javascript", "text/ecmascript", "application/javascript", "application/x-javascript", "application/ecmascript"],
72
+ mode: "javascript", ext: ["js"], alias: ["ecmascript", "js", "node"]},
73
+ {name: "JSON", mimes: ["application/json", "application/x-json"], mode: "javascript", ext: ["json", "map"], alias: ["json5"]},
74
+ {name: "JSON-LD", mime: "application/ld+json", mode: "javascript", ext: ["jsonld"], alias: ["jsonld"]},
75
+ {name: "JSX", mime: "text/jsx", mode: "jsx", ext: ["jsx"]},
76
+ {name: "Jinja2", mime: "null", mode: "jinja2"},
77
+ {name: "Julia", mime: "text/x-julia", mode: "julia", ext: ["jl"]},
78
+ {name: "Kotlin", mime: "text/x-kotlin", mode: "clike", ext: ["kt"]},
79
+ {name: "LESS", mime: "text/x-less", mode: "css", ext: ["less"]},
80
+ {name: "LiveScript", mime: "text/x-livescript", mode: "livescript", ext: ["ls"], alias: ["ls"]},
81
+ {name: "Lua", mime: "text/x-lua", mode: "lua", ext: ["lua"]},
82
+ {name: "Markdown", mime: "text/x-markdown", mode: "markdown", ext: ["markdown", "md", "mkd"]},
83
+ {name: "mIRC", mime: "text/mirc", mode: "mirc"},
84
+ {name: "MariaDB SQL", mime: "text/x-mariadb", mode: "sql"},
85
+ {name: "Mathematica", mime: "text/x-mathematica", mode: "mathematica", ext: ["m", "nb"]},
86
+ {name: "Modelica", mime: "text/x-modelica", mode: "modelica", ext: ["mo"]},
87
+ {name: "MUMPS", mime: "text/x-mumps", mode: "mumps"},
88
+ {name: "MS SQL", mime: "text/x-mssql", mode: "sql"},
89
+ {name: "MySQL", mime: "text/x-mysql", mode: "sql"},
90
+ {name: "Nginx", mime: "text/x-nginx-conf", mode: "nginx", file: /nginx.*\.conf$/i},
91
+ {name: "NSIS", mime: "text/x-nsis", mode: "nsis", ext: ["nsh", "nsi"]},
92
+ {name: "NTriples", mime: "text/n-triples", mode: "ntriples", ext: ["nt"]},
93
+ {name: "Objective C", mime: "text/x-objectivec", mode: "clike", ext: ["m", "mm"]},
94
+ {name: "OCaml", mime: "text/x-ocaml", mode: "mllike", ext: ["ml", "mli", "mll", "mly"]},
95
+ {name: "Octave", mime: "text/x-octave", mode: "octave", ext: ["m"]},
96
+ {name: "Oz", mime: "text/x-oz", mode: "oz", ext: ["oz"]},
97
+ {name: "Pascal", mime: "text/x-pascal", mode: "pascal", ext: ["p", "pas"]},
98
+ {name: "PEG.js", mime: "null", mode: "pegjs", ext: ["jsonld"]},
99
+ {name: "Perl", mime: "text/x-perl", mode: "perl", ext: ["pl", "pm"]},
100
+ {name: "PHP", mime: "application/x-httpd-php", mode: "php", ext: ["php", "php3", "php4", "php5", "phtml"]},
101
+ {name: "Pig", mime: "text/x-pig", mode: "pig", ext: ["pig"]},
102
+ {name: "Plain Text", mime: "text/plain", mode: "null", ext: ["txt", "text", "conf", "def", "list", "log"]},
103
+ {name: "PLSQL", mime: "text/x-plsql", mode: "sql", ext: ["pls"]},
104
+ {name: "Properties files", mime: "text/x-properties", mode: "properties", ext: ["properties", "ini", "in"], alias: ["ini", "properties"]},
105
+ {name: "Python", mime: "text/x-python", mode: "python", ext: ["py", "pyw"]},
106
+ {name: "Puppet", mime: "text/x-puppet", mode: "puppet", ext: ["pp"]},
107
+ {name: "Q", mime: "text/x-q", mode: "q", ext: ["q"]},
108
+ {name: "R", mime: "text/x-rsrc", mode: "r", ext: ["r"], alias: ["rscript"]},
109
+ {name: "reStructuredText", mime: "text/x-rst", mode: "rst", ext: ["rst"], alias: ["rst"]},
110
+ {name: "RPM Changes", mime: "text/x-rpm-changes", mode: "rpm"},
111
+ {name: "RPM Spec", mime: "text/x-rpm-spec", mode: "rpm", ext: ["spec"]},
112
+ {name: "Ruby", mime: "text/x-ruby", mode: "ruby", ext: ["rb"], alias: ["jruby", "macruby", "rake", "rb", "rbx"]},
113
+ {name: "Rust", mime: "text/x-rustsrc", mode: "rust", ext: ["rs"]},
114
+ {name: "Sass", mime: "text/x-sass", mode: "sass", ext: ["sass"]},
115
+ {name: "Scala", mime: "text/x-scala", mode: "clike", ext: ["scala"]},
116
+ {name: "Scheme", mime: "text/x-scheme", mode: "scheme", ext: ["scm", "ss"]},
117
+ {name: "SCSS", mime: "text/x-scss", mode: "css", ext: ["scss"]},
118
+ {name: "Shell", mime: "text/x-sh", mode: "shell", ext: ["sh", "ksh", "bash"], alias: ["bash", "sh", "zsh"], file: /^PKGBUILD$/},
119
+ {name: "Sieve", mime: "application/sieve", mode: "sieve", ext: ["siv", "sieve"]},
120
+ {name: "Slim", mimes: ["text/x-slim", "application/x-slim"], mode: "slim", ext: ["slim"]},
121
+ {name: "Smalltalk", mime: "text/x-stsrc", mode: "smalltalk", ext: ["st"]},
122
+ {name: "Smarty", mime: "text/x-smarty", mode: "smarty", ext: ["tpl"]},
123
+ {name: "Solr", mime: "text/x-solr", mode: "solr"},
124
+ {name: "Soy", mime: "text/x-soy", mode: "soy", ext: ["soy"], alias: ["closure template"]},
125
+ {name: "SPARQL", mime: "application/sparql-query", mode: "sparql", ext: ["rq", "sparql"], alias: ["sparul"]},
126
+ {name: "Spreadsheet", mime: "text/x-spreadsheet", mode: "spreadsheet", alias: ["excel", "formula"]},
127
+ {name: "SQL", mime: "text/x-sql", mode: "sql", ext: ["sql"]},
128
+ {name: "Squirrel", mime: "text/x-squirrel", mode: "clike", ext: ["nut"]},
129
+ {name: "Swift", mime: "text/x-swift", mode: "swift", ext: ["swift"]},
130
+ {name: "MariaDB", mime: "text/x-mariadb", mode: "sql"},
131
+ {name: "sTeX", mime: "text/x-stex", mode: "stex"},
132
+ {name: "LaTeX", mime: "text/x-latex", mode: "stex", ext: ["text", "ltx"], alias: ["tex"]},
133
+ {name: "SystemVerilog", mime: "text/x-systemverilog", mode: "verilog", ext: ["v"]},
134
+ {name: "Tcl", mime: "text/x-tcl", mode: "tcl", ext: ["tcl"]},
135
+ {name: "Textile", mime: "text/x-textile", mode: "textile", ext: ["textile"]},
136
+ {name: "TiddlyWiki ", mime: "text/x-tiddlywiki", mode: "tiddlywiki"},
137
+ {name: "Tiki wiki", mime: "text/tiki", mode: "tiki"},
138
+ {name: "TOML", mime: "text/x-toml", mode: "toml", ext: ["toml"]},
139
+ {name: "Tornado", mime: "text/x-tornado", mode: "tornado"},
140
+ {name: "troff", mime: "troff", mode: "troff", ext: ["1", "2", "3", "4", "5", "6", "7", "8", "9"]},
141
+ {name: "TTCN", mime: "text/x-ttcn", mode: "ttcn", ext: ["ttcn", "ttcn3", "ttcnpp"]},
142
+ {name: "TTCN_CFG", mime: "text/x-ttcn-cfg", mode: "ttcn-cfg", ext: ["cfg"]},
143
+ {name: "Turtle", mime: "text/turtle", mode: "turtle", ext: ["ttl"]},
144
+ {name: "TypeScript", mime: "application/typescript", mode: "javascript", ext: ["ts"], alias: ["ts"]},
145
+ {name: "Twig", mime: "text/x-twig", mode: "twig"},
146
+ {name: "VB.NET", mime: "text/x-vb", mode: "vb", ext: ["vb"]},
147
+ {name: "VBScript", mime: "text/vbscript", mode: "vbscript", ext: ["vbs"]},
148
+ {name: "Velocity", mime: "text/velocity", mode: "velocity", ext: ["vtl"]},
149
+ {name: "Verilog", mime: "text/x-verilog", mode: "verilog", ext: ["v"]},
150
+ {name: "VHDL", mime: "text/x-vhdl", mode: "vhdl", ext: ["vhd", "vhdl"]},
151
+ {name: "XML", mimes: ["application/xml", "text/xml"], mode: "xml", ext: ["xml", "xsl", "xsd"], alias: ["rss", "wsdl", "xsd"]},
152
+ {name: "XQuery", mime: "application/xquery", mode: "xquery", ext: ["xy", "xquery"]},
153
+ {name: "YAML", mime: "text/x-yaml", mode: "yaml", ext: ["yaml", "yml"], alias: ["yml"]},
154
+ {name: "Z80", mime: "text/x-z80", mode: "z80", ext: ["z80"]},
155
+ {name: "mscgen", mime: "text/x-mscgen", mode: "mscgen", ext: ["mscgen", "mscin", "msc"]},
156
+ {name: "xu", mime: "text/x-xu", mode: "mscgen", ext: ["xu"]},
157
+ {name: "msgenny", mime: "text/x-msgenny", mode: "mscgen", ext: ["msgenny"]}
158
+ ];
159
+ // Ensure all modes have a mime property for backwards compatibility
160
+ for (var i = 0; i < CodeMirror.modeInfo.length; i++) {
161
+ var info = CodeMirror.modeInfo[i];
162
+ if (info.mimes) info.mime = info.mimes[0];
163
+ }
164
+
165
+ CodeMirror.findModeByMIME = function(mime) {
166
+ mime = mime.toLowerCase();
167
+ for (var i = 0; i < CodeMirror.modeInfo.length; i++) {
168
+ var info = CodeMirror.modeInfo[i];
169
+ if (info.mime == mime) return info;
170
+ if (info.mimes) for (var j = 0; j < info.mimes.length; j++)
171
+ if (info.mimes[j] == mime) return info;
172
+ }
173
+ };
174
+
175
+ CodeMirror.findModeByExtension = function(ext) {
176
+ for (var i = 0; i < CodeMirror.modeInfo.length; i++) {
177
+ var info = CodeMirror.modeInfo[i];
178
+ if (info.ext) for (var j = 0; j < info.ext.length; j++)
179
+ if (info.ext[j] == ext) return info;
180
+ }
181
+ };
182
+
183
+ CodeMirror.findModeByFileName = function(filename) {
184
+ for (var i = 0; i < CodeMirror.modeInfo.length; i++) {
185
+ var info = CodeMirror.modeInfo[i];
186
+ if (info.file && info.file.test(filename)) return info;
187
+ }
188
+ var dot = filename.lastIndexOf(".");
189
+ var ext = dot > -1 && filename.substring(dot + 1, filename.length);
190
+ if (ext) return CodeMirror.findModeByExtension(ext);
191
+ };
192
+
193
+ CodeMirror.findModeByName = function(name) {
194
+ name = name.toLowerCase();
195
+ for (var i = 0; i < CodeMirror.modeInfo.length; i++) {
196
+ var info = CodeMirror.modeInfo[i];
197
+ if (info.name.toLowerCase() == name) return info;
198
+ if (info.alias) for (var j = 0; j < info.alias.length; j++)
199
+ if (info.alias[j].toLowerCase() == name) return info;
200
+ }
201
+ };
202
+ });
codemirror/mode/php/index.html ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!doctype html>
2
+
3
+ <title>CodeMirror: PHP mode</title>
4
+ <meta charset="utf-8"/>
5
+ <link rel=stylesheet href="../../doc/docs.css">
6
+
7
+ <link rel="stylesheet" href="../../lib/codemirror.css">
8
+ <script src="../../lib/codemirror.js"></script>
9
+ <script src="../../addon/edit/matchbrackets.js"></script>
10
+ <script src="../htmlmixed/htmlmixed.js"></script>
11
+ <script src="../xml/xml.js"></script>
12
+ <script src="../javascript/javascript.js"></script>
13
+ <script src="../css/css.js"></script>
14
+ <script src="../clike/clike.js"></script>
15
+ <script src="php.js"></script>
16
+ <style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
17
+ <div id=nav>
18
+ <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
19
+
20
+ <ul>
21
+ <li><a href="../../index.html">Home</a>
22
+ <li><a href="../../doc/manual.html">Manual</a>
23
+ <li><a href="https://github.com/codemirror/codemirror">Code</a>
24
+ </ul>
25
+ <ul>
26
+ <li><a href="../index.html">Language modes</a>
27
+ <li><a class=active href="#">PHP</a>
28
+ </ul>
29
+ </div>
30
+
31
+ <article>
32
+ <h2>PHP mode</h2>
33
+ <form><textarea id="code" name="code">
34
+ <?php
35
+ $a = array('a' => 1, 'b' => 2, 3 => 'c');
36
+
37
+ echo "$a[a] ${a[3] /* } comment */} {$a[b]} \$a[a]";
38
+
39
+ function hello($who) {
40
+ return "Hello $who!";
41
+ }
42
+ ?>
43
+ <p>The program says <?= hello("World") ?>.</p>
44
+ <script>
45
+ alert("And here is some JS code"); // also colored
46
+ </script>
47
+ </textarea></form>
48
+
49
+ <script>
50
+ var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
51
+ lineNumbers: true,
52
+ matchBrackets: true,
53
+ mode: "application/x-httpd-php",
54
+ indentUnit: 4,
55
+ indentWithTabs: true
56
+ });
57
+ </script>
58
+
59
+ <p>Simple HTML/PHP mode based on
60
+ the <a href="../clike/">C-like</a> mode. Depends on XML,
61
+ JavaScript, CSS, HTMLMixed, and C-like modes.</p>
62
+
63
+ <p><strong>MIME types defined:</strong> <code>application/x-httpd-php</code> (HTML with PHP code), <code>text/x-php</code> (plain, non-wrapped PHP code).</p>
64
+ </article>
codemirror/mode/php/php.js ADDED
@@ -0,0 +1,234 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // CodeMirror, copyright (c) by Marijn Haverbeke and others
2
+ // Distributed under an MIT license: http://codemirror.net/LICENSE
3
+
4
+ (function(mod) {
5
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
6
+ mod(require("../../lib/codemirror"), require("../htmlmixed/htmlmixed"), require("../clike/clike"));
7
+ else if (typeof define == "function" && define.amd) // AMD
8
+ define(["../../lib/codemirror", "../htmlmixed/htmlmixed", "../clike/clike"], mod);
9
+ else // Plain browser env
10
+ mod(CodeMirror);
11
+ })(function(CodeMirror) {
12
+ "use strict";
13
+
14
+ function keywords(str) {
15
+ var obj = {}, words = str.split(" ");
16
+ for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
17
+ return obj;
18
+ }
19
+
20
+ // Helper for phpString
21
+ function matchSequence(list, end, escapes) {
22
+ if (list.length == 0) return phpString(end);
23
+ return function (stream, state) {
24
+ var patterns = list[0];
25
+ for (var i = 0; i < patterns.length; i++) if (stream.match(patterns[i][0])) {
26
+ state.tokenize = matchSequence(list.slice(1), end);
27
+ return patterns[i][1];
28
+ }
29
+ state.tokenize = phpString(end, escapes);
30
+ return "string";
31
+ };
32
+ }
33
+ function phpString(closing, escapes) {
34
+ return function(stream, state) { return phpString_(stream, state, closing, escapes); };
35
+ }
36
+ function phpString_(stream, state, closing, escapes) {
37
+ // "Complex" syntax
38
+ if (escapes !== false && stream.match("${", false) || stream.match("{$", false)) {
39
+ state.tokenize = null;
40
+ return "string";
41
+ }
42
+
43
+ // Simple syntax
44
+ if (escapes !== false && stream.match(/^\$[a-zA-Z_][a-zA-Z0-9_]*/)) {
45
+ // After the variable name there may appear array or object operator.
46
+ if (stream.match("[", false)) {
47
+ // Match array operator
48
+ state.tokenize = matchSequence([
49
+ [["[", null]],
50
+ [[/\d[\w\.]*/, "number"],
51
+ [/\$[a-zA-Z_][a-zA-Z0-9_]*/, "variable-2"],
52
+ [/[\w\$]+/, "variable"]],
53
+ [["]", null]]
54
+ ], closing, escapes);
55
+ }
56
+ if (stream.match(/\-\>\w/, false)) {
57
+ // Match object operator
58
+ state.tokenize = matchSequence([
59
+ [["->", null]],
60
+ [[/[\w]+/, "variable"]]
61
+ ], closing, escapes);
62
+ }
63
+ return "variable-2";
64
+ }
65
+
66
+ var escaped = false;
67
+ // Normal string
68
+ while (!stream.eol() &&
69
+ (escaped || escapes === false ||
70
+ (!stream.match("{$", false) &&
71
+ !stream.match(/^(\$[a-zA-Z_][a-zA-Z0-9_]*|\$\{)/, false)))) {
72
+ if (!escaped && stream.match(closing)) {
73
+ state.tokenize = null;
74
+ state.tokStack.pop(); state.tokStack.pop();
75
+ break;
76
+ }
77
+ escaped = stream.next() == "\\" && !escaped;
78
+ }
79
+ return "string";
80
+ }
81
+
82
+ var phpKeywords = "abstract and array as break case catch class clone const continue declare default " +
83
+ "do else elseif enddeclare endfor endforeach endif endswitch endwhile extends final " +
84
+ "for foreach function global goto if implements interface instanceof namespace " +
85
+ "new or private protected public static switch throw trait try use var while xor " +
86
+ "die echo empty exit eval include include_once isset list require require_once return " +
87
+ "print unset __halt_compiler self static parent yield insteadof finally";
88
+ var phpAtoms = "true false null TRUE FALSE NULL __CLASS__ __DIR__ __FILE__ __LINE__ __METHOD__ __FUNCTION__ __NAMESPACE__ __TRAIT__";
89
+ var phpBuiltin = "func_num_args func_get_arg func_get_args strlen strcmp strncmp strcasecmp strncasecmp each error_reporting define defined trigger_error user_error set_error_handler restore_error_handler get_declared_classes get_loaded_extensions extension_loaded get_extension_funcs debug_backtrace constant bin2hex hex2bin sleep usleep time mktime gmmktime strftime gmstrftime strtotime date gmdate getdate localtime checkdate flush wordwrap htmlspecialchars htmlentities html_entity_decode md5 md5_file crc32 getimagesize image_type_to_mime_type phpinfo phpversion phpcredits strnatcmp strnatcasecmp substr_count strspn strcspn strtok strtoupper strtolower strpos strrpos strrev hebrev hebrevc nl2br basename dirname pathinfo stripslashes stripcslashes strstr stristr strrchr str_shuffle str_word_count strcoll substr substr_replace quotemeta ucfirst ucwords strtr addslashes addcslashes rtrim str_replace str_repeat count_chars chunk_split trim ltrim strip_tags similar_text explode implode setlocale localeconv parse_str str_pad chop strchr sprintf printf vprintf vsprintf sscanf fscanf parse_url urlencode urldecode rawurlencode rawurldecode readlink linkinfo link unlink exec system escapeshellcmd escapeshellarg passthru shell_exec proc_open proc_close rand srand getrandmax mt_rand mt_srand mt_getrandmax base64_decode base64_encode abs ceil floor round is_finite is_nan is_infinite bindec hexdec octdec decbin decoct dechex base_convert number_format fmod ip2long long2ip getenv putenv getopt microtime gettimeofday getrusage uniqid quoted_printable_decode set_time_limit get_cfg_var magic_quotes_runtime set_magic_quotes_runtime get_magic_quotes_gpc get_magic_quotes_runtime import_request_variables error_log serialize unserialize memory_get_usage var_dump var_export debug_zval_dump print_r highlight_file show_source highlight_string ini_get ini_get_all ini_set ini_alter ini_restore get_include_path set_include_path restore_include_path setcookie header headers_sent connection_aborted connection_status ignore_user_abort parse_ini_file is_uploaded_file move_uploaded_file intval floatval doubleval strval gettype settype is_null is_resource is_bool is_long is_float is_int is_integer is_double is_real is_numeric is_string is_array is_object is_scalar ereg ereg_replace eregi eregi_replace split spliti join sql_regcase dl pclose popen readfile rewind rmdir umask fclose feof fgetc fgets fgetss fread fopen fpassthru ftruncate fstat fseek ftell fflush fwrite fputs mkdir rename copy tempnam tmpfile file file_get_contents file_put_contents stream_select stream_context_create stream_context_set_params stream_context_set_option stream_context_get_options stream_filter_prepend stream_filter_append fgetcsv flock get_meta_tags stream_set_write_buffer set_file_buffer set_socket_blocking stream_set_blocking socket_set_blocking stream_get_meta_data stream_register_wrapper stream_wrapper_register stream_set_timeout socket_set_timeout socket_get_status realpath fnmatch fsockopen pfsockopen pack unpack get_browser crypt opendir closedir chdir getcwd rewinddir readdir dir glob fileatime filectime filegroup fileinode filemtime fileowner fileperms filesize filetype file_exists is_writable is_writeable is_readable is_executable is_file is_dir is_link stat lstat chown touch clearstatcache mail ob_start ob_flush ob_clean ob_end_flush ob_end_clean ob_get_flush ob_get_clean ob_get_length ob_get_level ob_get_status ob_get_contents ob_implicit_flush ob_list_handlers ksort krsort natsort natcasesort asort arsort sort rsort usort uasort uksort shuffle array_walk count end prev next reset current key min max in_array array_search extract compact array_fill range array_multisort array_push array_pop array_shift array_unshift array_splice array_slice array_merge array_merge_recursive array_keys array_values array_count_values array_reverse array_reduce array_pad array_flip array_change_key_case array_rand array_unique array_intersect array_intersect_assoc array_diff array_diff_assoc array_sum array_filter array_map array_chunk array_key_exists pos sizeof key_exists assert assert_options version_compare ftok str_rot13 aggregate session_name session_module_name session_save_path session_id session_regenerate_id session_decode session_register session_unregister session_is_registered session_encode session_start session_destroy session_unset session_set_save_handler session_cache_limiter session_cache_expire session_set_cookie_params session_get_cookie_params session_write_close preg_match preg_match_all preg_replace preg_replace_callback preg_split preg_quote preg_grep overload ctype_alnum ctype_alpha ctype_cntrl ctype_digit ctype_lower ctype_graph ctype_print ctype_punct ctype_space ctype_upper ctype_xdigit virtual apache_request_headers apache_note apache_lookup_uri apache_child_terminate apache_setenv apache_response_headers apache_get_version getallheaders mysql_connect mysql_pconnect mysql_close mysql_select_db mysql_create_db mysql_drop_db mysql_query mysql_unbuffered_query mysql_db_query mysql_list_dbs mysql_list_tables mysql_list_fields mysql_list_processes mysql_error mysql_errno mysql_affected_rows mysql_insert_id mysql_result mysql_num_rows mysql_num_fields mysql_fetch_row mysql_fetch_array mysql_fetch_assoc mysql_fetch_object mysql_data_seek mysql_fetch_lengths mysql_fetch_field mysql_field_seek mysql_free_result mysql_field_name mysql_field_table mysql_field_len mysql_field_type mysql_field_flags mysql_escape_string mysql_real_escape_string mysql_stat mysql_thread_id mysql_client_encoding mysql_get_client_info mysql_get_host_info mysql_get_proto_info mysql_get_server_info mysql_info mysql mysql_fieldname mysql_fieldtable mysql_fieldlen mysql_fieldtype mysql_fieldflags mysql_selectdb mysql_createdb mysql_dropdb mysql_freeresult mysql_numfields mysql_numrows mysql_listdbs mysql_listtables mysql_listfields mysql_db_name mysql_dbname mysql_tablename mysql_table_name pg_connect pg_pconnect pg_close pg_connection_status pg_connection_busy pg_connection_reset pg_host pg_dbname pg_port pg_tty pg_options pg_ping pg_query pg_send_query pg_cancel_query pg_fetch_result pg_fetch_row pg_fetch_assoc pg_fetch_array pg_fetch_object pg_fetch_all pg_affected_rows pg_get_result pg_result_seek pg_result_status pg_free_result pg_last_oid pg_num_rows pg_num_fields pg_field_name pg_field_num pg_field_size pg_field_type pg_field_prtlen pg_field_is_null pg_get_notify pg_get_pid pg_result_error pg_last_error pg_last_notice pg_put_line pg_end_copy pg_copy_to pg_copy_from pg_trace pg_untrace pg_lo_create pg_lo_unlink pg_lo_open pg_lo_close pg_lo_read pg_lo_write pg_lo_read_all pg_lo_import pg_lo_export pg_lo_seek pg_lo_tell pg_escape_string pg_escape_bytea pg_unescape_bytea pg_client_encoding pg_set_client_encoding pg_meta_data pg_convert pg_insert pg_update pg_delete pg_select pg_exec pg_getlastoid pg_cmdtuples pg_errormessage pg_numrows pg_numfields pg_fieldname pg_fieldsize pg_fieldtype pg_fieldnum pg_fieldprtlen pg_fieldisnull pg_freeresult pg_result pg_loreadall pg_locreate pg_lounlink pg_loopen pg_loclose pg_loread pg_lowrite pg_loimport pg_loexport http_response_code get_declared_traits getimagesizefromstring socket_import_stream stream_set_chunk_size trait_exists header_register_callback class_uses session_status session_register_shutdown echo print global static exit array empty eval isset unset die include require include_once require_once json_decode json_encode json_last_error json_last_error_msg curl_close curl_copy_handle curl_errno curl_error curl_escape curl_exec curl_file_create curl_getinfo curl_init curl_multi_add_handle curl_multi_close curl_multi_exec curl_multi_getcontent curl_multi_info_read curl_multi_init curl_multi_remove_handle curl_multi_select curl_multi_setopt curl_multi_strerror curl_pause curl_reset curl_setopt_array curl_setopt curl_share_close curl_share_init curl_share_setopt curl_strerror curl_unescape curl_version mysqli_affected_rows mysqli_autocommit mysqli_change_user mysqli_character_set_name mysqli_close mysqli_commit mysqli_connect_errno mysqli_connect_error mysqli_connect mysqli_data_seek mysqli_debug mysqli_dump_debug_info mysqli_errno mysqli_error_list mysqli_error mysqli_fetch_all mysqli_fetch_array mysqli_fetch_assoc mysqli_fetch_field_direct mysqli_fetch_field mysqli_fetch_fields mysqli_fetch_lengths mysqli_fetch_object mysqli_fetch_row mysqli_field_count mysqli_field_seek mysqli_field_tell mysqli_free_result mysqli_get_charset mysqli_get_client_info mysqli_get_client_stats mysqli_get_client_version mysqli_get_connection_stats mysqli_get_host_info mysqli_get_proto_info mysqli_get_server_info mysqli_get_server_version mysqli_info mysqli_init mysqli_insert_id mysqli_kill mysqli_more_results mysqli_multi_query mysqli_next_result mysqli_num_fields mysqli_num_rows mysqli_options mysqli_ping mysqli_prepare mysqli_query mysqli_real_connect mysqli_real_escape_string mysqli_real_query mysqli_reap_async_query mysqli_refresh mysqli_rollback mysqli_select_db mysqli_set_charset mysqli_set_local_infile_default mysqli_set_local_infile_handler mysqli_sqlstate mysqli_ssl_set mysqli_stat mysqli_stmt_init mysqli_store_result mysqli_thread_id mysqli_thread_safe mysqli_use_result mysqli_warning_count";
90
+ CodeMirror.registerHelper("hintWords", "php", [phpKeywords, phpAtoms, phpBuiltin].join(" ").split(" "));
91
+ CodeMirror.registerHelper("wordChars", "php", /[\w$]/);
92
+
93
+ var phpConfig = {
94
+ name: "clike",
95
+ helperType: "php",
96
+ keywords: keywords(phpKeywords),
97
+ blockKeywords: keywords("catch do else elseif for foreach if switch try while finally"),
98
+ defKeywords: keywords("class function interface namespace trait"),
99
+ atoms: keywords(phpAtoms),
100
+ builtin: keywords(phpBuiltin),
101
+ multiLineStrings: true,
102
+ hooks: {
103
+ "$": function(stream) {
104
+ stream.eatWhile(/[\w\$_]/);
105
+ return "variable-2";
106
+ },
107
+ "<": function(stream, state) {
108
+ var before;
109
+ if (before = stream.match(/<<\s*/)) {
110
+ var quoted = stream.eat(/['"]/);
111
+ stream.eatWhile(/[\w\.]/);
112
+ var delim = stream.current().slice(before[0].length + (quoted ? 2 : 1));
113
+ if (quoted) stream.eat(quoted);
114
+ if (delim) {
115
+ (state.tokStack || (state.tokStack = [])).push(delim, 0);
116
+ state.tokenize = phpString(delim, quoted != "'");
117
+ return "string";
118
+ }
119
+ }
120
+ return false;
121
+ },
122
+ "#": function(stream) {
123
+ while (!stream.eol() && !stream.match("?>", false)) stream.next();
124
+ return "comment";
125
+ },
126
+ "/": function(stream) {
127
+ if (stream.eat("/")) {
128
+ while (!stream.eol() && !stream.match("?>", false)) stream.next();
129
+ return "comment";
130
+ }
131
+ return false;
132
+ },
133
+ '"': function(_stream, state) {
134
+ (state.tokStack || (state.tokStack = [])).push('"', 0);
135
+ state.tokenize = phpString('"');
136
+ return "string";
137
+ },
138
+ "{": function(_stream, state) {
139
+ if (state.tokStack && state.tokStack.length)
140
+ state.tokStack[state.tokStack.length - 1]++;
141
+ return false;
142
+ },
143
+ "}": function(_stream, state) {
144
+ if (state.tokStack && state.tokStack.length > 0 &&
145
+ !--state.tokStack[state.tokStack.length - 1]) {
146
+ state.tokenize = phpString(state.tokStack[state.tokStack.length - 2]);
147
+ }
148
+ return false;
149
+ }
150
+ }
151
+ };
152
+
153
+ CodeMirror.defineMode("php", function(config, parserConfig) {
154
+ var htmlMode = CodeMirror.getMode(config, "text/html");
155
+ var phpMode = CodeMirror.getMode(config, phpConfig);
156
+
157
+ function dispatch(stream, state) {
158
+ var isPHP = state.curMode == phpMode;
159
+ if (stream.sol() && state.pending && state.pending != '"' && state.pending != "'") state.pending = null;
160
+ if (!isPHP) {
161
+ if (stream.match(/^<\?\w*/)) {
162
+ state.curMode = phpMode;
163
+ if (!state.php) state.php = CodeMirror.startState(phpMode, htmlMode.indent(state.html, ""))
164
+ state.curState = state.php;
165
+ return "meta";
166
+ }
167
+ if (state.pending == '"' || state.pending == "'") {
168
+ while (!stream.eol() && stream.next() != state.pending) {}
169
+ var style = "string";
170
+ } else if (state.pending && stream.pos < state.pending.end) {
171
+ stream.pos = state.pending.end;
172
+ var style = state.pending.style;
173
+ } else {
174
+ var style = htmlMode.token(stream, state.curState);
175
+ }
176
+ if (state.pending) state.pending = null;
177
+ var cur = stream.current(), openPHP = cur.search(/<\?/), m;
178
+ if (openPHP != -1) {
179
+ if (style == "string" && (m = cur.match(/[\'\"]$/)) && !/\?>/.test(cur)) state.pending = m[0];
180
+ else state.pending = {end: stream.pos, style: style};
181
+ stream.backUp(cur.length - openPHP);
182
+ }
183
+ return style;
184
+ } else if (isPHP && state.php.tokenize == null && stream.match("?>")) {
185
+ state.curMode = htmlMode;
186
+ state.curState = state.html;
187
+ if (!state.php.context.prev) state.php = null;
188
+ return "meta";
189
+ } else {
190
+ return phpMode.token(stream, state.curState);
191
+ }
192
+ }
193
+
194
+ return {
195
+ startState: function() {
196
+ var html = CodeMirror.startState(htmlMode)
197
+ var php = parserConfig.startOpen ? CodeMirror.startState(phpMode) : null
198
+ return {html: html,
199
+ php: php,
200
+ curMode: parserConfig.startOpen ? phpMode : htmlMode,
201
+ curState: parserConfig.startOpen ? php : html,
202
+ pending: null};
203
+ },
204
+
205
+ copyState: function(state) {
206
+ var html = state.html, htmlNew = CodeMirror.copyState(htmlMode, html),
207
+ php = state.php, phpNew = php && CodeMirror.copyState(phpMode, php), cur;
208
+ if (state.curMode == htmlMode) cur = htmlNew;
209
+ else cur = phpNew;
210
+ return {html: htmlNew, php: phpNew, curMode: state.curMode, curState: cur,
211
+ pending: state.pending};
212
+ },
213
+
214
+ token: dispatch,
215
+
216
+ indent: function(state, textAfter) {
217
+ if ((state.curMode != phpMode && /^\s*<\//.test(textAfter)) ||
218
+ (state.curMode == phpMode && /^\?>/.test(textAfter)))
219
+ return htmlMode.indent(state.html, textAfter);
220
+ return state.curMode.indent(state.curState, textAfter);
221
+ },
222
+
223
+ blockCommentStart: "/*",
224
+ blockCommentEnd: "*/",
225
+ lineComment: "//",
226
+
227
+ innerMode: function(state) { return {state: state.curState, mode: state.curMode}; }
228
+ };
229
+ }, "htmlmixed", "clike");
230
+
231
+ CodeMirror.defineMIME("application/x-httpd-php", "php");
232
+ CodeMirror.defineMIME("application/x-httpd-php-open", {name: "php", startOpen: true});
233
+ CodeMirror.defineMIME("text/x-php", phpConfig);
234
+ });
codemirror/mode/php/test.js ADDED
@@ -0,0 +1,154 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // CodeMirror, copyright (c) by Marijn Haverbeke and others
2
+ // Distributed under an MIT license: http://codemirror.net/LICENSE
3
+
4
+ (function() {
5
+ var mode = CodeMirror.getMode({indentUnit: 2}, "php");
6
+ function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); }
7
+
8
+ MT('simple_test',
9
+ '[meta <?php] ' +
10
+ '[keyword echo] [string "aaa"]; ' +
11
+ '[meta ?>]');
12
+
13
+ MT('variable_interpolation_non_alphanumeric',
14
+ '[meta <?php]',
15
+ '[keyword echo] [string "aaa$~$!$@$#$$$%$^$&$*$($)$.$<$>$/$\\$}$\\\"$:$;$?$|$[[$]]$+$=aaa"]',
16
+ '[meta ?>]');
17
+
18
+ MT('variable_interpolation_digits',
19
+ '[meta <?php]',
20
+ '[keyword echo] [string "aaa$1$2$3$4$5$6$7$8$9$0aaa"]',
21
+ '[meta ?>]');
22
+
23
+ MT('variable_interpolation_simple_syntax_1',
24
+ '[meta <?php]',
25
+ '[keyword echo] [string "aaa][variable-2 $aaa][string .aaa"];',
26
+ '[meta ?>]');
27
+
28
+ MT('variable_interpolation_simple_syntax_2',
29
+ '[meta <?php]',
30
+ '[keyword echo] [string "][variable-2 $aaaa][[','[number 2]', ']][string aa"];',
31
+ '[keyword echo] [string "][variable-2 $aaaa][[','[number 2345]', ']][string aa"];',
32
+ '[keyword echo] [string "][variable-2 $aaaa][[','[number 2.3]', ']][string aa"];',
33
+ '[keyword echo] [string "][variable-2 $aaaa][[','[variable aaaaa]', ']][string aa"];',
34
+ '[keyword echo] [string "][variable-2 $aaaa][[','[variable-2 $aaaaa]',']][string aa"];',
35
+
36
+ '[keyword echo] [string "1aaa][variable-2 $aaaa][[','[number 2]', ']][string aa"];',
37
+ '[keyword echo] [string "aaa][variable-2 $aaaa][[','[number 2345]', ']][string aa"];',
38
+ '[keyword echo] [string "aaa][variable-2 $aaaa][[','[number 2.3]', ']][string aa"];',
39
+ '[keyword echo] [string "aaa][variable-2 $aaaa][[','[variable aaaaa]', ']][string aa"];',
40
+ '[keyword echo] [string "aaa][variable-2 $aaaa][[','[variable-2 $aaaaa]',']][string aa"];',
41
+ '[meta ?>]');
42
+
43
+ MT('variable_interpolation_simple_syntax_3',
44
+ '[meta <?php]',
45
+ '[keyword echo] [string "aaa][variable-2 $aaaa]->[variable aaaaa][string .aaaaaa"];',
46
+ '[keyword echo] [string "aaa][variable-2 $aaaa][string ->][variable-2 $aaaaa][string .aaaaaa"];',
47
+ '[keyword echo] [string "aaa][variable-2 $aaaa]->[variable aaaaa][string [[2]].aaaaaa"];',
48
+ '[keyword echo] [string "aaa][variable-2 $aaaa]->[variable aaaaa][string ->aaaa2.aaaaaa"];',
49
+ '[meta ?>]');
50
+
51
+ MT('variable_interpolation_escaping',
52
+ '[meta <?php] [comment /* Escaping */]',
53
+ '[keyword echo] [string "aaa\\$aaaa->aaa.aaa"];',
54
+ '[keyword echo] [string "aaa\\$aaaa[[2]]aaa.aaa"];',
55
+ '[keyword echo] [string "aaa\\$aaaa[[asd]]aaa.aaa"];',
56
+ '[keyword echo] [string "aaa{\\$aaaa->aaa.aaa"];',
57
+ '[keyword echo] [string "aaa{\\$aaaa[[2]]aaa.aaa"];',
58
+ '[keyword echo] [string "aaa{\\aaaaa[[asd]]aaa.aaa"];',
59
+ '[keyword echo] [string "aaa\\${aaaa->aaa.aaa"];',
60
+ '[keyword echo] [string "aaa\\${aaaa[[2]]aaa.aaa"];',
61
+ '[keyword echo] [string "aaa\\${aaaa[[asd]]aaa.aaa"];',
62
+ '[meta ?>]');
63
+
64
+ MT('variable_interpolation_complex_syntax_1',
65
+ '[meta <?php]',
66
+ '[keyword echo] [string "aaa][variable-2 $]{[variable aaaa]}[string ->aaa.aaa"];',
67
+ '[keyword echo] [string "aaa][variable-2 $]{[variable-2 $aaaa]}[string ->aaa.aaa"];',
68
+ '[keyword echo] [string "aaa][variable-2 $]{[variable-2 $aaaa][[',' [number 42]',']]}[string ->aaa.aaa"];',
69
+ '[keyword echo] [string "aaa][variable-2 $]{[variable aaaa][meta ?>]aaaaaa');
70
+
71
+ MT('variable_interpolation_complex_syntax_2',
72
+ '[meta <?php] [comment /* Monsters */]',
73
+ '[keyword echo] [string "][variable-2 $]{[variable aaa][comment /*}?>} $aaa<?php } */]}[string ->aaa.aaa"];',
74
+ '[keyword echo] [string "][variable-2 $]{[variable aaa][comment /*}?>*/][[',' [string "aaa][variable-2 $aaa][string {}][variable-2 $]{[variable aaa]}[string "]',']]}[string ->aaa.aaa"];',
75
+ '[keyword echo] [string "][variable-2 $]{[variable aaa][comment /*} } $aaa } */]}[string ->aaa.aaa"];');
76
+
77
+
78
+ function build_recursive_monsters(nt, t, n){
79
+ var monsters = [t];
80
+ for (var i = 1; i <= n; ++i)
81
+ monsters[i] = nt.join(monsters[i - 1]);
82
+ return monsters;
83
+ }
84
+
85
+ var m1 = build_recursive_monsters(
86
+ ['[string "][variable-2 $]{[variable aaa] [operator +] ', '}[string "]'],
87
+ '[comment /* }?>} */] [string "aaa][variable-2 $aaa][string .aaa"]',
88
+ 10
89
+ );
90
+
91
+ MT('variable_interpolation_complex_syntax_3_1',
92
+ '[meta <?php] [comment /* Recursive monsters */]',
93
+ '[keyword echo] ' + m1[4] + ';',
94
+ '[keyword echo] ' + m1[7] + ';',
95
+ '[keyword echo] ' + m1[8] + ';',
96
+ '[keyword echo] ' + m1[5] + ';',
97
+ '[keyword echo] ' + m1[1] + ';',
98
+ '[keyword echo] ' + m1[6] + ';',
99
+ '[keyword echo] ' + m1[9] + ';',
100
+ '[keyword echo] ' + m1[0] + ';',
101
+ '[keyword echo] ' + m1[10] + ';',
102
+ '[keyword echo] ' + m1[2] + ';',
103
+ '[keyword echo] ' + m1[3] + ';',
104
+ '[keyword echo] [string "end"];',
105
+ '[meta ?>]');
106
+
107
+ var m2 = build_recursive_monsters(
108
+ ['[string "a][variable-2 $]{[variable aaa] [operator +] ', ' [operator +] ', '}[string .a"]'],
109
+ '[comment /* }?>{{ */] [string "a?>}{{aa][variable-2 $aaa][string .a}a?>a"]',
110
+ 5
111
+ );
112
+
113
+ MT('variable_interpolation_complex_syntax_3_2',
114
+ '[meta <?php] [comment /* Recursive monsters 2 */]',
115
+ '[keyword echo] ' + m2[0] + ';',
116
+ '[keyword echo] ' + m2[1] + ';',
117
+ '[keyword echo] ' + m2[5] + ';',
118
+ '[keyword echo] ' + m2[4] + ';',
119
+ '[keyword echo] ' + m2[2] + ';',
120
+ '[keyword echo] ' + m2[3] + ';',
121
+ '[keyword echo] [string "end"];',
122
+ '[meta ?>]');
123
+
124
+ function build_recursive_monsters_2(mf1, mf2, nt, t, n){
125
+ var monsters = [t];
126
+ for (var i = 1; i <= n; ++i)
127
+ monsters[i] = nt[0] + mf1[i - 1] + nt[1] + mf2[i - 1] + nt[2] + monsters[i - 1] + nt[3];
128
+ return monsters;
129
+ }
130
+
131
+ var m3 = build_recursive_monsters_2(
132
+ m1,
133
+ m2,
134
+ ['[string "a][variable-2 $]{[variable aaa] [operator +] ', ' [operator +] ', ' [operator +] ', '}[string .a"]'],
135
+ '[comment /* }?>{{ */] [string "a?>}{{aa][variable-2 $aaa][string .a}a?>a"]',
136
+ 4
137
+ );
138
+
139
+ MT('variable_interpolation_complex_syntax_3_3',
140
+ '[meta <?php] [comment /* Recursive monsters 2 */]',
141
+ '[keyword echo] ' + m3[4] + ';',
142
+ '[keyword echo] ' + m3[0] + ';',
143
+ '[keyword echo] ' + m3[3] + ';',
144
+ '[keyword echo] ' + m3[1] + ';',
145
+ '[keyword echo] ' + m3[2] + ';',
146
+ '[keyword echo] [string "end"];',
147
+ '[meta ?>]');
148
+
149
+ MT("variable_interpolation_heredoc",
150
+ "[meta <?php]",
151
+ "[string <<<here]",
152
+ "[string doc ][variable-2 $]{[variable yay]}[string more]",
153
+ "[string here]; [comment // normal]");
154
+ })();
codemirror/mode/sass/index.html ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!doctype html>
2
+
3
+ <title>CodeMirror: Sass mode</title>
4
+ <meta charset="utf-8"/>
5
+ <link rel=stylesheet href="../../doc/docs.css">
6
+
7
+ <link rel="stylesheet" href="../../lib/codemirror.css">
8
+ <script src="../../lib/codemirror.js"></script>
9
+ <script src="../../addon/edit/matchbrackets.js"></script>
10
+ <script src="sass.js"></script>
11
+ <style>.CodeMirror {border: 1px solid #ddd; font-size:12px; height: 400px}</style>
12
+ <div id=nav>
13
+ <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
14
+
15
+ <ul>
16
+ <li><a href="../../index.html">Home</a>
17
+ <li><a href="../../doc/manual.html">Manual</a>
18
+ <li><a href="https://github.com/codemirror/codemirror">Code</a>
19
+ </ul>
20
+ <ul>
21
+ <li><a href="../index.html">Language modes</a>
22
+ <li><a class=active href="#">Sass</a>
23
+ </ul>
24
+ </div>
25
+
26
+ <article>
27
+ <h2>Sass mode</h2>
28
+ <form><textarea id="code" name="code">// Variable Definitions
29
+
30
+ $page-width: 800px
31
+ $sidebar-width: 200px
32
+ $primary-color: #eeeeee
33
+
34
+ // Global Attributes
35
+
36
+ body
37
+ font:
38
+ family: sans-serif
39
+ size: 30em
40
+ weight: bold
41
+
42
+ // Scoped Styles
43
+
44
+ #contents
45
+ width: $page-width
46
+ #sidebar
47
+ float: right
48
+ width: $sidebar-width
49
+ #main
50
+ width: $page-width - $sidebar-width
51
+ background: $primary-color
52
+ h2
53
+ color: blue
54
+
55
+ #footer
56
+ height: 200px
57
+ </textarea></form>
58
+ <script>
59
+ var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
60
+ lineNumbers : true,
61
+ matchBrackets : true
62
+ });
63
+ </script>
64
+
65
+ <p><strong>MIME types defined:</strong> <code>text/x-sass</code>.</p>
66
+ </article>
codemirror/mode/sass/sass.js ADDED
@@ -0,0 +1,414 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // CodeMirror, copyright (c) by Marijn Haverbeke and others
2
+ // Distributed under an MIT license: http://codemirror.net/LICENSE
3
+
4
+ (function(mod) {
5
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
6
+ mod(require("../../lib/codemirror"));
7
+ else if (typeof define == "function" && define.amd) // AMD
8
+ define(["../../lib/codemirror"], mod);
9
+ else // Plain browser env
10
+ mod(CodeMirror);
11
+ })(function(CodeMirror) {
12
+ "use strict";
13
+
14
+ CodeMirror.defineMode("sass", function(config) {
15
+ function tokenRegexp(words) {
16
+ return new RegExp("^" + words.join("|"));
17
+ }
18
+
19
+ var keywords = ["true", "false", "null", "auto"];
20
+ var keywordsRegexp = new RegExp("^" + keywords.join("|"));
21
+
22
+ var operators = ["\\(", "\\)", "=", ">", "<", "==", ">=", "<=", "\\+", "-",
23
+ "\\!=", "/", "\\*", "%", "and", "or", "not", ";","\\{","\\}",":"];
24
+ var opRegexp = tokenRegexp(operators);
25
+
26
+ var pseudoElementsRegexp = /^::?[a-zA-Z_][\w\-]*/;
27
+
28
+ function urlTokens(stream, state) {
29
+ var ch = stream.peek();
30
+
31
+ if (ch === ")") {
32
+ stream.next();
33
+ state.tokenizer = tokenBase;
34
+ return "operator";
35
+ } else if (ch === "(") {
36
+ stream.next();
37
+ stream.eatSpace();
38
+
39
+ return "operator";
40
+ } else if (ch === "'" || ch === '"') {
41
+ state.tokenizer = buildStringTokenizer(stream.next());
42
+ return "string";
43
+ } else {
44
+ state.tokenizer = buildStringTokenizer(")", false);
45
+ return "string";
46
+ }
47
+ }
48
+ function comment(indentation, multiLine) {
49
+ return function(stream, state) {
50
+ if (stream.sol() && stream.indentation() <= indentation) {
51
+ state.tokenizer = tokenBase;
52
+ return tokenBase(stream, state);
53
+ }
54
+
55
+ if (multiLine && stream.skipTo("*/")) {
56
+ stream.next();
57
+ stream.next();
58
+ state.tokenizer = tokenBase;
59
+ } else {
60
+ stream.skipToEnd();
61
+ }
62
+
63
+ return "comment";
64
+ };
65
+ }
66
+
67
+ function buildStringTokenizer(quote, greedy) {
68
+ if (greedy == null) { greedy = true; }
69
+
70
+ function stringTokenizer(stream, state) {
71
+ var nextChar = stream.next();
72
+ var peekChar = stream.peek();
73
+ var previousChar = stream.string.charAt(stream.pos-2);
74
+
75
+ var endingString = ((nextChar !== "\\" && peekChar === quote) || (nextChar === quote && previousChar !== "\\"));
76
+
77
+ if (endingString) {
78
+ if (nextChar !== quote && greedy) { stream.next(); }
79
+ state.tokenizer = tokenBase;
80
+ return "string";
81
+ } else if (nextChar === "#" && peekChar === "{") {
82
+ state.tokenizer = buildInterpolationTokenizer(stringTokenizer);
83
+ stream.next();
84
+ return "operator";
85
+ } else {
86
+ return "string";
87
+ }
88
+ }
89
+
90
+ return stringTokenizer;
91
+ }
92
+
93
+ function buildInterpolationTokenizer(currentTokenizer) {
94
+ return function(stream, state) {
95
+ if (stream.peek() === "}") {
96
+ stream.next();
97
+ state.tokenizer = currentTokenizer;
98
+ return "operator";
99
+ } else {
100
+ return tokenBase(stream, state);
101
+ }
102
+ };
103
+ }
104
+
105
+ function indent(state) {
106
+ if (state.indentCount == 0) {
107
+ state.indentCount++;
108
+ var lastScopeOffset = state.scopes[0].offset;
109
+ var currentOffset = lastScopeOffset + config.indentUnit;
110
+ state.scopes.unshift({ offset:currentOffset });
111
+ }
112
+ }
113
+
114
+ function dedent(state) {
115
+ if (state.scopes.length == 1) return;
116
+
117
+ state.scopes.shift();
118
+ }
119
+
120
+ function tokenBase(stream, state) {
121
+ var ch = stream.peek();
122
+
123
+ // Comment
124
+ if (stream.match("/*")) {
125
+ state.tokenizer = comment(stream.indentation(), true);
126
+ return state.tokenizer(stream, state);
127
+ }
128
+ if (stream.match("//")) {
129
+ state.tokenizer = comment(stream.indentation(), false);
130
+ return state.tokenizer(stream, state);
131
+ }
132
+
133
+ // Interpolation
134
+ if (stream.match("#{")) {
135
+ state.tokenizer = buildInterpolationTokenizer(tokenBase);
136
+ return "operator";
137
+ }
138
+
139
+ // Strings
140
+ if (ch === '"' || ch === "'") {
141
+ stream.next();
142
+ state.tokenizer = buildStringTokenizer(ch);
143
+ return "string";
144
+ }
145
+
146
+ if(!state.cursorHalf){// state.cursorHalf === 0
147
+ // first half i.e. before : for key-value pairs
148
+ // including selectors
149
+
150
+ if (ch === ".") {
151
+ stream.next();
152
+ if (stream.match(/^[\w-]+/)) {
153
+ indent(state);
154
+ return "atom";
155
+ } else if (stream.peek() === "#") {
156
+ indent(state);
157
+ return "atom";
158
+ }
159
+ }
160
+
161
+ if (ch === "#") {
162
+ stream.next();
163
+ // ID selectors
164
+ if (stream.match(/^[\w-]+/)) {
165
+ indent(state);
166
+ return "atom";
167
+ }
168
+ if (stream.peek() === "#") {
169
+ indent(state);
170
+ return "atom";
171
+ }
172
+ }
173
+
174
+ // Variables
175
+ if (ch === "$") {
176
+ stream.next();
177
+ stream.eatWhile(/[\w-]/);
178
+ return "variable-2";
179
+ }
180
+
181
+ // Numbers
182
+ if (stream.match(/^-?[0-9\.]+/))
183
+ return "number";
184
+
185
+ // Units
186
+ if (stream.match(/^(px|em|in)\b/))
187
+ return "unit";
188
+
189
+ if (stream.match(keywordsRegexp))
190
+ return "keyword";
191
+
192
+ if (stream.match(/^url/) && stream.peek() === "(") {
193
+ state.tokenizer = urlTokens;
194
+ return "atom";
195
+ }
196
+
197
+ if (ch === "=") {
198
+ // Match shortcut mixin definition
199
+ if (stream.match(/^=[\w-]+/)) {
200
+ indent(state);
201
+ return "meta";
202
+ }
203
+ }
204
+
205
+ if (ch === "+") {
206
+ // Match shortcut mixin definition
207
+ if (stream.match(/^\+[\w-]+/)){
208
+ return "variable-3";
209
+ }
210
+ }
211
+
212
+ if(ch === "@"){
213
+ if(stream.match(/@extend/)){
214
+ if(!stream.match(/\s*[\w]/))
215
+ dedent(state);
216
+ }
217
+ }
218
+
219
+
220
+ // Indent Directives
221
+ if (stream.match(/^@(else if|if|media|else|for|each|while|mixin|function)/)) {
222
+ indent(state);
223
+ return "meta";
224
+ }
225
+
226
+ // Other Directives
227
+ if (ch === "@") {
228
+ stream.next();
229
+ stream.eatWhile(/[\w-]/);
230
+ return "meta";
231
+ }
232
+
233
+ if (stream.eatWhile(/[\w-]/)){
234
+ if(stream.match(/ *: *[\w-\+\$#!\("']/,false)){
235
+ return "property";
236
+ }
237
+ else if(stream.match(/ *:/,false)){
238
+ indent(state);
239
+ state.cursorHalf = 1;
240
+ return "atom";
241
+ }
242
+ else if(stream.match(/ *,/,false)){
243
+ return "atom";
244
+ }
245
+ else{
246
+ indent(state);
247
+ return "atom";
248
+ }
249
+ }
250
+
251
+ if(ch === ":"){
252
+ if (stream.match(pseudoElementsRegexp)){ // could be a pseudo-element
253
+ return "keyword";
254
+ }
255
+ stream.next();
256
+ state.cursorHalf=1;
257
+ return "operator";
258
+ }
259
+
260
+ } // cursorHalf===0 ends here
261
+ else{
262
+
263
+ if (ch === "#") {
264
+ stream.next();
265
+ // Hex numbers
266
+ if (stream.match(/[0-9a-fA-F]{6}|[0-9a-fA-F]{3}/)){
267
+ if(!stream.peek()){
268
+ state.cursorHalf = 0;
269
+ }
270
+ return "number";
271
+ }
272
+ }
273
+
274
+ // Numbers
275
+ if (stream.match(/^-?[0-9\.]+/)){
276
+ if(!stream.peek()){
277
+ state.cursorHalf = 0;
278
+ }
279
+ return "number";
280
+ }
281
+
282
+ // Units
283
+ if (stream.match(/^(px|em|in)\b/)){
284
+ if(!stream.peek()){
285
+ state.cursorHalf = 0;
286
+ }
287
+ return "unit";
288
+ }
289
+
290
+ if (stream.match(keywordsRegexp)){
291
+ if(!stream.peek()){
292
+ state.cursorHalf = 0;
293
+ }
294
+ return "keyword";
295
+ }
296
+
297
+ if (stream.match(/^url/) && stream.peek() === "(") {
298
+ state.tokenizer = urlTokens;
299
+ if(!stream.peek()){
300
+ state.cursorHalf = 0;
301
+ }
302
+ return "atom";
303
+ }
304
+
305
+ // Variables
306
+ if (ch === "$") {
307
+ stream.next();
308
+ stream.eatWhile(/[\w-]/);
309
+ if(!stream.peek()){
310
+ state.cursorHalf = 0;
311
+ }
312
+ return "variable-3";
313
+ }
314
+
315
+ // bang character for !important, !default, etc.
316
+ if (ch === "!") {
317
+ stream.next();
318
+ if(!stream.peek()){
319
+ state.cursorHalf = 0;
320
+ }
321
+ return stream.match(/^[\w]+/) ? "keyword": "operator";
322
+ }
323
+
324
+ if (stream.match(opRegexp)){
325
+ if(!stream.peek()){
326
+ state.cursorHalf = 0;
327
+ }
328
+ return "operator";
329
+ }
330
+
331
+ // attributes
332
+ if (stream.eatWhile(/[\w-]/)) {
333
+ if(!stream.peek()){
334
+ state.cursorHalf = 0;
335
+ }
336
+ return "attribute";
337
+ }
338
+
339
+ //stream.eatSpace();
340
+ if(!stream.peek()){
341
+ state.cursorHalf = 0;
342
+ return null;
343
+ }
344
+
345
+ } // else ends here
346
+
347
+ if (stream.match(opRegexp))
348
+ return "operator";
349
+
350
+ // If we haven't returned by now, we move 1 character
351
+ // and return an error
352
+ stream.next();
353
+ return null;
354
+ }
355
+
356
+ function tokenLexer(stream, state) {
357
+ if (stream.sol()) state.indentCount = 0;
358
+ var style = state.tokenizer(stream, state);
359
+ var current = stream.current();
360
+
361
+ if (current === "@return" || current === "}"){
362
+ dedent(state);
363
+ }
364
+
365
+ if (style !== null) {
366
+ var startOfToken = stream.pos - current.length;
367
+
368
+ var withCurrentIndent = startOfToken + (config.indentUnit * state.indentCount);
369
+
370
+ var newScopes = [];
371
+
372
+ for (var i = 0; i < state.scopes.length; i++) {
373
+ var scope = state.scopes[i];
374
+
375
+ if (scope.offset <= withCurrentIndent)
376
+ newScopes.push(scope);
377
+ }
378
+
379
+ state.scopes = newScopes;
380
+ }
381
+
382
+
383
+ return style;
384
+ }
385
+
386
+ return {
387
+ startState: function() {
388
+ return {
389
+ tokenizer: tokenBase,
390
+ scopes: [{offset: 0, type: "sass"}],
391
+ indentCount: 0,
392
+ cursorHalf: 0, // cursor half tells us if cursor lies after (1)
393
+ // or before (0) colon (well... more or less)
394
+ definedVars: [],
395
+ definedMixins: []
396
+ };
397
+ },
398
+ token: function(stream, state) {
399
+ var style = tokenLexer(stream, state);
400
+
401
+ state.lastToken = { style: style, content: stream.current() };
402
+
403
+ return style;
404
+ },
405
+
406
+ indent: function(state) {
407
+ return state.scopes[0].offset;
408
+ }
409
+ };
410
+ });
411
+
412
+ CodeMirror.defineMIME("text/x-sass", "sass");
413
+
414
+ });
codemirror/mode/xml/index.html ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!doctype html>
2
+
3
+ <title>CodeMirror: XML mode</title>
4
+ <meta charset="utf-8"/>
5
+ <link rel=stylesheet href="../../doc/docs.css">
6
+
7
+ <link rel="stylesheet" href="../../lib/codemirror.css">
8
+ <script src="../../lib/codemirror.js"></script>
9
+ <script src="xml.js"></script>
10
+ <style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
11
+ <div id=nav>
12
+ <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
13
+
14
+ <ul>
15
+ <li><a href="../../index.html">Home</a>
16
+ <li><a href="../../doc/manual.html">Manual</a>
17
+ <li><a href="https://github.com/codemirror/codemirror">Code</a>
18
+ </ul>
19
+ <ul>
20
+ <li><a href="../index.html">Language modes</a>
21
+ <li><a class=active href="#">XML</a>
22
+ </ul>
23
+ </div>
24
+
25
+ <article>
26
+ <h2>XML mode</h2>
27
+ <form><textarea id="code" name="code">
28
+ &lt;html style="color: green"&gt;
29
+ &lt;!-- this is a comment --&gt;
30
+ &lt;head&gt;
31
+ &lt;title&gt;HTML Example&lt;/title&gt;
32
+ &lt;/head&gt;
33
+ &lt;body&gt;
34
+ The indentation tries to be &lt;em&gt;somewhat &amp;quot;do what
35
+ I mean&amp;quot;&lt;/em&gt;... but might not match your style.
36
+ &lt;/body&gt;
37
+ &lt;/html&gt;
38
+ </textarea></form>
39
+ <script>
40
+ var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
41
+ mode: "text/html",
42
+ lineNumbers: true
43
+ });
44
+ </script>
45
+ <p>The XML mode supports these configuration parameters:</p>
46
+ <dl>
47
+ <dt><code>htmlMode (boolean)</code></dt>
48
+ <dd>This switches the mode to parse HTML instead of XML. This
49
+ means attributes do not have to be quoted, and some elements
50
+ (such as <code>br</code>) do not require a closing tag.</dd>
51
+ <dt><code>matchClosing (boolean)</code></dt>
52
+ <dd>Controls whether the mode checks that close tags match the
53
+ corresponding opening tag, and highlights mismatches as errors.
54
+ Defaults to true.</dd>
55
+ <dt><code>alignCDATA (boolean)</code></dt>
56
+ <dd>Setting this to true will force the opening tag of CDATA
57
+ blocks to not be indented.</dd>
58
+ </dl>
59
+
60
+ <p><strong>MIME types defined:</strong> <code>application/xml</code>, <code>text/html</code>.</p>
61
+ </article>
codemirror/mode/xml/test.js ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // CodeMirror, copyright (c) by Marijn Haverbeke and others
2
+ // Distributed under an MIT license: http://codemirror.net/LICENSE
3
+
4
+ (function() {
5
+ var mode = CodeMirror.getMode({indentUnit: 2}, "xml"), mname = "xml";
6
+ function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1), mname); }
7
+
8
+ MT("matching",
9
+ "[tag&bracket <][tag top][tag&bracket >]",
10
+ " text",
11
+ " [tag&bracket <][tag inner][tag&bracket />]",
12
+ "[tag&bracket </][tag top][tag&bracket >]");
13
+
14
+ MT("nonmatching",
15
+ "[tag&bracket <][tag top][tag&bracket >]",
16
+ " [tag&bracket <][tag inner][tag&bracket />]",
17
+ " [tag&bracket </][tag&error tip][tag&bracket&error >]");
18
+
19
+ MT("doctype",
20
+ "[meta <!doctype foobar>]",
21
+ "[tag&bracket <][tag top][tag&bracket />]");
22
+
23
+ MT("cdata",
24
+ "[tag&bracket <][tag top][tag&bracket >]",
25
+ " [atom <![CDATA[foo]",
26
+ "[atom barbazguh]]]]>]",
27
+ "[tag&bracket </][tag top][tag&bracket >]");
28
+
29
+ // HTML tests
30
+ mode = CodeMirror.getMode({indentUnit: 2}, "text/html");
31
+
32
+ MT("selfclose",
33
+ "[tag&bracket <][tag html][tag&bracket >]",
34
+ " [tag&bracket <][tag link] [attribute rel]=[string stylesheet] [attribute href]=[string \"/foobar\"][tag&bracket >]",
35
+ "[tag&bracket </][tag html][tag&bracket >]");
36
+
37
+ MT("list",
38
+ "[tag&bracket <][tag ol][tag&bracket >]",
39
+ " [tag&bracket <][tag li][tag&bracket >]one",
40
+ " [tag&bracket <][tag li][tag&bracket >]two",
41
+ "[tag&bracket </][tag ol][tag&bracket >]");
42
+
43
+ MT("valueless",
44
+ "[tag&bracket <][tag input] [attribute type]=[string checkbox] [attribute checked][tag&bracket />]");
45
+
46
+ MT("pThenArticle",
47
+ "[tag&bracket <][tag p][tag&bracket >]",
48
+ " foo",
49
+ "[tag&bracket <][tag article][tag&bracket >]bar");
50
+
51
+ })();
codemirror/mode/xml/xml.js ADDED
@@ -0,0 +1,394 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // CodeMirror, copyright (c) by Marijn Haverbeke and others
2
+ // Distributed under an MIT license: http://codemirror.net/LICENSE
3
+
4
+ (function(mod) {
5
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
6
+ mod(require("../../lib/codemirror"));
7
+ else if (typeof define == "function" && define.amd) // AMD
8
+ define(["../../lib/codemirror"], mod);
9
+ else // Plain browser env
10
+ mod(CodeMirror);
11
+ })(function(CodeMirror) {
12
+ "use strict";
13
+
14
+ var htmlConfig = {
15
+ autoSelfClosers: {'area': true, 'base': true, 'br': true, 'col': true, 'command': true,
16
+ 'embed': true, 'frame': true, 'hr': true, 'img': true, 'input': true,
17
+ 'keygen': true, 'link': true, 'meta': true, 'param': true, 'source': true,
18
+ 'track': true, 'wbr': true, 'menuitem': true},
19
+ implicitlyClosed: {'dd': true, 'li': true, 'optgroup': true, 'option': true, 'p': true,
20
+ 'rp': true, 'rt': true, 'tbody': true, 'td': true, 'tfoot': true,
21
+ 'th': true, 'tr': true},
22
+ contextGrabbers: {
23
+ 'dd': {'dd': true, 'dt': true},
24
+ 'dt': {'dd': true, 'dt': true},
25
+ 'li': {'li': true},
26
+ 'option': {'option': true, 'optgroup': true},
27
+ 'optgroup': {'optgroup': true},
28
+ 'p': {'address': true, 'article': true, 'aside': true, 'blockquote': true, 'dir': true,
29
+ 'div': true, 'dl': true, 'fieldset': true, 'footer': true, 'form': true,
30
+ 'h1': true, 'h2': true, 'h3': true, 'h4': true, 'h5': true, 'h6': true,
31
+ 'header': true, 'hgroup': true, 'hr': true, 'menu': true, 'nav': true, 'ol': true,
32
+ 'p': true, 'pre': true, 'section': true, 'table': true, 'ul': true},
33
+ 'rp': {'rp': true, 'rt': true},
34
+ 'rt': {'rp': true, 'rt': true},
35
+ 'tbody': {'tbody': true, 'tfoot': true},
36
+ 'td': {'td': true, 'th': true},
37
+ 'tfoot': {'tbody': true},
38
+ 'th': {'td': true, 'th': true},
39
+ 'thead': {'tbody': true, 'tfoot': true},
40
+ 'tr': {'tr': true}
41
+ },
42
+ doNotIndent: {"pre": true},
43
+ allowUnquoted: true,
44
+ allowMissing: true,
45
+ caseFold: true
46
+ }
47
+
48
+ var xmlConfig = {
49
+ autoSelfClosers: {},
50
+ implicitlyClosed: {},
51
+ contextGrabbers: {},
52
+ doNotIndent: {},
53
+ allowUnquoted: false,
54
+ allowMissing: false,
55
+ caseFold: false
56
+ }
57
+
58
+ CodeMirror.defineMode("xml", function(editorConf, config_) {
59
+ var indentUnit = editorConf.indentUnit
60
+ var config = {}
61
+ var defaults = config_.htmlMode ? htmlConfig : xmlConfig
62
+ for (var prop in defaults) config[prop] = defaults[prop]
63
+ for (var prop in config_) config[prop] = config_[prop]
64
+
65
+ // Return variables for tokenizers
66
+ var type, setStyle;
67
+
68
+ function inText(stream, state) {
69
+ function chain(parser) {
70
+ state.tokenize = parser;
71
+ return parser(stream, state);
72
+ }
73
+
74
+ var ch = stream.next();
75
+ if (ch == "<") {
76
+ if (stream.eat("!")) {
77
+ if (stream.eat("[")) {
78
+ if (stream.match("CDATA[")) return chain(inBlock("atom", "]]>"));
79
+ else return null;
80
+ } else if (stream.match("--")) {
81
+ return chain(inBlock("comment", "-->"));
82
+ } else if (stream.match("DOCTYPE", true, true)) {
83
+ stream.eatWhile(/[\w\._\-]/);
84
+ return chain(doctype(1));
85
+ } else {
86
+ return null;
87
+ }
88
+ } else if (stream.eat("?")) {
89
+ stream.eatWhile(/[\w\._\-]/);
90
+ state.tokenize = inBlock("meta", "?>");
91
+ return "meta";
92
+ } else {
93
+ type = stream.eat("/") ? "closeTag" : "openTag";
94
+ state.tokenize = inTag;
95
+ return "tag bracket";
96
+ }
97
+ } else if (ch == "&") {
98
+ var ok;
99
+ if (stream.eat("#")) {
100
+ if (stream.eat("x")) {
101
+ ok = stream.eatWhile(/[a-fA-F\d]/) && stream.eat(";");
102
+ } else {
103
+ ok = stream.eatWhile(/[\d]/) && stream.eat(";");
104
+ }
105
+ } else {
106
+ ok = stream.eatWhile(/[\w\.\-:]/) && stream.eat(";");
107
+ }
108
+ return ok ? "atom" : "error";
109
+ } else {
110
+ stream.eatWhile(/[^&<]/);
111
+ return null;
112
+ }
113
+ }
114
+ inText.isInText = true;
115
+
116
+ function inTag(stream, state) {
117
+ var ch = stream.next();
118
+ if (ch == ">" || (ch == "/" && stream.eat(">"))) {
119
+ state.tokenize = inText;
120
+ type = ch == ">" ? "endTag" : "selfcloseTag";
121
+ return "tag bracket";
122
+ } else if (ch == "=") {
123
+ type = "equals";
124
+ return null;
125
+ } else if (ch == "<") {
126
+ state.tokenize = inText;
127
+ state.state = baseState;
128
+ state.tagName = state.tagStart = null;
129
+ var next = state.tokenize(stream, state);
130
+ return next ? next + " tag error" : "tag error";
131
+ } else if (/[\'\"]/.test(ch)) {
132
+ state.tokenize = inAttribute(ch);
133
+ state.stringStartCol = stream.column();
134
+ return state.tokenize(stream, state);
135
+ } else {
136
+ stream.match(/^[^\s\u00a0=<>\"\']*[^\s\u00a0=<>\"\'\/]/);
137
+ return "word";
138
+ }
139
+ }
140
+
141
+ function inAttribute(quote) {
142
+ var closure = function(stream, state) {
143
+ while (!stream.eol()) {
144
+ if (stream.next() == quote) {
145
+ state.tokenize = inTag;
146
+ break;
147
+ }
148
+ }
149
+ return "string";
150
+ };
151
+ closure.isInAttribute = true;
152
+ return closure;
153
+ }
154
+
155
+ function inBlock(style, terminator) {
156
+ return function(stream, state) {
157
+ while (!stream.eol()) {
158
+ if (stream.match(terminator)) {
159
+ state.tokenize = inText;
160
+ break;
161
+ }
162
+ stream.next();
163
+ }
164
+ return style;
165
+ };
166
+ }
167
+ function doctype(depth) {
168
+ return function(stream, state) {
169
+ var ch;
170
+ while ((ch = stream.next()) != null) {
171
+ if (ch == "<") {
172
+ state.tokenize = doctype(depth + 1);
173
+ return state.tokenize(stream, state);
174
+ } else if (ch == ">") {
175
+ if (depth == 1) {
176
+ state.tokenize = inText;
177
+ break;
178
+ } else {
179
+ state.tokenize = doctype(depth - 1);
180
+ return state.tokenize(stream, state);
181
+ }
182
+ }
183
+ }
184
+ return "meta";
185
+ };
186
+ }
187
+
188
+ function Context(state, tagName, startOfLine) {
189
+ this.prev = state.context;
190
+ this.tagName = tagName;
191
+ this.indent = state.indented;
192
+ this.startOfLine = startOfLine;
193
+ if (config.doNotIndent.hasOwnProperty(tagName) || (state.context && state.context.noIndent))
194
+ this.noIndent = true;
195
+ }
196
+ function popContext(state) {
197
+ if (state.context) state.context = state.context.prev;
198
+ }
199
+ function maybePopContext(state, nextTagName) {
200
+ var parentTagName;
201
+ while (true) {
202
+ if (!state.context) {
203
+ return;
204
+ }
205
+ parentTagName = state.context.tagName;
206
+ if (!config.contextGrabbers.hasOwnProperty(parentTagName) ||
207
+ !config.contextGrabbers[parentTagName].hasOwnProperty(nextTagName)) {
208
+ return;
209
+ }
210
+ popContext(state);
211
+ }
212
+ }
213
+
214
+ function baseState(type, stream, state) {
215
+ if (type == "openTag") {
216
+ state.tagStart = stream.column();
217
+ return tagNameState;
218
+ } else if (type == "closeTag") {
219
+ return closeTagNameState;
220
+ } else {
221
+ return baseState;
222
+ }
223
+ }
224
+ function tagNameState(type, stream, state) {
225
+ if (type == "word") {
226
+ state.tagName = stream.current();
227
+ setStyle = "tag";
228
+ return attrState;
229
+ } else {
230
+ setStyle = "error";
231
+ return tagNameState;
232
+ }
233
+ }
234
+ function closeTagNameState(type, stream, state) {
235
+ if (type == "word") {
236
+ var tagName = stream.current();
237
+ if (state.context && state.context.tagName != tagName &&
238
+ config.implicitlyClosed.hasOwnProperty(state.context.tagName))
239
+ popContext(state);
240
+ if ((state.context && state.context.tagName == tagName) || config.matchClosing === false) {
241
+ setStyle = "tag";
242
+ return closeState;
243
+ } else {
244
+ setStyle = "tag error";
245
+ return closeStateErr;
246
+ }
247
+ } else {
248
+ setStyle = "error";
249
+ return closeStateErr;
250
+ }
251
+ }
252
+
253
+ function closeState(type, _stream, state) {
254
+ if (type != "endTag") {
255
+ setStyle = "error";
256
+ return closeState;
257
+ }
258
+ popContext(state);
259
+ return baseState;
260
+ }
261
+ function closeStateErr(type, stream, state) {
262
+ setStyle = "error";
263
+ return closeState(type, stream, state);
264
+ }
265
+
266
+ function attrState(type, _stream, state) {
267
+ if (type == "word") {
268
+ setStyle = "attribute";
269
+ return attrEqState;
270
+ } else if (type == "endTag" || type == "selfcloseTag") {
271
+ var tagName = state.tagName, tagStart = state.tagStart;
272
+ state.tagName = state.tagStart = null;
273
+ if (type == "selfcloseTag" ||
274
+ config.autoSelfClosers.hasOwnProperty(tagName)) {
275
+ maybePopContext(state, tagName);
276
+ } else {
277
+ maybePopContext(state, tagName);
278
+ state.context = new Context(state, tagName, tagStart == state.indented);
279
+ }
280
+ return baseState;
281
+ }
282
+ setStyle = "error";
283
+ return attrState;
284
+ }
285
+ function attrEqState(type, stream, state) {
286
+ if (type == "equals") return attrValueState;
287
+ if (!config.allowMissing) setStyle = "error";
288
+ return attrState(type, stream, state);
289
+ }
290
+ function attrValueState(type, stream, state) {
291
+ if (type == "string") return attrContinuedState;
292
+ if (type == "word" && config.allowUnquoted) {setStyle = "string"; return attrState;}
293
+ setStyle = "error";
294
+ return attrState(type, stream, state);
295
+ }
296
+ function attrContinuedState(type, stream, state) {
297
+ if (type == "string") return attrContinuedState;
298
+ return attrState(type, stream, state);
299
+ }
300
+
301
+ return {
302
+ startState: function(baseIndent) {
303
+ var state = {tokenize: inText,
304
+ state: baseState,
305
+ indented: baseIndent || 0,
306
+ tagName: null, tagStart: null,
307
+ context: null}
308
+ if (baseIndent != null) state.baseIndent = baseIndent
309
+ return state
310
+ },
311
+
312
+ token: function(stream, state) {
313
+ if (!state.tagName && stream.sol())
314
+ state.indented = stream.indentation();
315
+
316
+ if (stream.eatSpace()) return null;
317
+ type = null;
318
+ var style = state.tokenize(stream, state);
319
+ if ((style || type) && style != "comment") {
320
+ setStyle = null;
321
+ state.state = state.state(type || style, stream, state);
322
+ if (setStyle)
323
+ style = setStyle == "error" ? style + " error" : setStyle;
324
+ }
325
+ return style;
326
+ },
327
+
328
+ indent: function(state, textAfter, fullLine) {
329
+ var context = state.context;
330
+ // Indent multi-line strings (e.g. css).
331
+ if (state.tokenize.isInAttribute) {
332
+ if (state.tagStart == state.indented)
333
+ return state.stringStartCol + 1;
334
+ else
335
+ return state.indented + indentUnit;
336
+ }
337
+ if (context && context.noIndent) return CodeMirror.Pass;
338
+ if (state.tokenize != inTag && state.tokenize != inText)
339
+ return fullLine ? fullLine.match(/^(\s*)/)[0].length : 0;
340
+ // Indent the starts of attribute names.
341
+ if (state.tagName) {
342
+ if (config.multilineTagIndentPastTag !== false)
343
+ return state.tagStart + state.tagName.length + 2;
344
+ else
345
+ return state.tagStart + indentUnit * (config.multilineTagIndentFactor || 1);
346
+ }
347
+ if (config.alignCDATA && /<!\[CDATA\[/.test(textAfter)) return 0;
348
+ var tagAfter = textAfter && /^<(\/)?([\w_:\.-]*)/.exec(textAfter);
349
+ if (tagAfter && tagAfter[1]) { // Closing tag spotted
350
+ while (context) {
351
+ if (context.tagName == tagAfter[2]) {
352
+ context = context.prev;
353
+ break;
354
+ } else if (config.implicitlyClosed.hasOwnProperty(context.tagName)) {
355
+ context = context.prev;
356
+ } else {
357
+ break;
358
+ }
359
+ }
360
+ } else if (tagAfter) { // Opening tag spotted
361
+ while (context) {
362
+ var grabbers = config.contextGrabbers[context.tagName];
363
+ if (grabbers && grabbers.hasOwnProperty(tagAfter[2]))
364
+ context = context.prev;
365
+ else
366
+ break;
367
+ }
368
+ }
369
+ while (context && context.prev && !context.startOfLine)
370
+ context = context.prev;
371
+ if (context) return context.indent + indentUnit;
372
+ else return state.baseIndent || 0;
373
+ },
374
+
375
+ electricInput: /<\/[\s\w:]+>$/,
376
+ blockCommentStart: "<!--",
377
+ blockCommentEnd: "-->",
378
+
379
+ configuration: config.htmlMode ? "html" : "xml",
380
+ helperType: config.htmlMode ? "html" : "xml",
381
+
382
+ skipAttribute: function(state) {
383
+ if (state.state == attrValueState)
384
+ state.state = attrState
385
+ }
386
+ };
387
+ });
388
+
389
+ CodeMirror.defineMIME("text/xml", "xml");
390
+ CodeMirror.defineMIME("application/xml", "xml");
391
+ if (!CodeMirror.mimeModes.hasOwnProperty("text/html"))
392
+ CodeMirror.defineMIME("text/html", {name: "xml", htmlMode: true});
393
+
394
+ });
images/social/round/facebook.png ADDED
Binary file
images/social/round/google.png ADDED
Binary file
images/social/round/linkedin.png ADDED
Binary file
images/social/round/twitter.png ADDED
Binary file
images/social/square/facebook.png ADDED
Binary file
images/social/square/google.png ADDED
Binary file
images/social/square/linkedin.png ADDED
Binary file
images/social/square/pinterest.png ADDED
Binary file
images/social/square/twitter.png ADDED
Binary file
languages/header-footer-es_ES.mo ADDED
Binary file
languages/header-footer-es_ES.po ADDED
@@ -0,0 +1,168 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (C) 2010
2
+ # This file is distributed under the same license as the package.
3
+ msgid ""
4
+ msgstr ""
5
+ "Project-Id-Version: \n"
6
+ "Report-Msgid-Bugs-To: http://wordpress.org/tag/header-footer\n"
7
+ "POT-Creation-Date: 2011-11-28 08:30:40+00:00\n"
8
+ "MIME-Version: 1.0\n"
9
+ "Content-Type: text/plain; charset=UTF-8\n"
10
+ "Content-Transfer-Encoding: 8bit\n"
11
+ "PO-Revision-Date: 2013-11-14 09:47+0100\n"
12
+ "Last-Translator: jelena kovacevic <jecajeca260@gmail.com>\n"
13
+ "Language-Team: LANGUAGE <LL@li.org>\n"
14
+ "X-Generator: Poedit 1.5.5\n"
15
+
16
+ #: options.php:87
17
+ msgid ""
18
+ "Detailed documentation and FAQs can be found online on <a href=\"http://www."
19
+ "satollo.net/plugins/header-footer\"><strong>Header and Footer plugin "
20
+ "official page</strong></a>."
21
+ msgstr ""
22
+ "Documentación detallada y Preguntas Frecuentes que pueden encontrarse en "
23
+ "línea en <a href=\"http://www.satollo.net/plugins/header-footer"
24
+ "\"><strong>Página oficial del plugin Header and Footer</strong></a>."
25
+
26
+ #: options.php:88
27
+ msgid "PHP is allowed on textareas below."
28
+ msgstr "PHP está permitido en las áreas de texto de abajo."
29
+
30
+ #: options.php:88
31
+ msgid "If you use bbPress, read the official page."
32
+ msgstr "Si usted utiliza bbPress, lea la página oficial."
33
+
34
+ #: options.php:97
35
+ msgid "Page head and footer"
36
+ msgstr "Head y footer de la página "
37
+
38
+ #: options.php:98
39
+ msgid "Post content"
40
+ msgstr "Contenido del Post"
41
+
42
+ #: options.php:99
43
+ msgid "Page content"
44
+ msgstr "Contenido de la página"
45
+
46
+ #: options.php:100
47
+ msgid "Facebook"
48
+ msgstr "Facebook"
49
+
50
+ #: options.php:101
51
+ msgid "Snippets"
52
+ msgstr "Fragmentos"
53
+
54
+ #: options.php:102
55
+ msgid "Notes and..."
56
+ msgstr "Notas y..."
57
+
58
+ #: options.php:107
59
+ msgid "Code to be added on HEAD section of the home"
60
+ msgstr "Código que se añade en la sección HEAD de la página de inicio"
61
+
62
+ #: options.php:108
63
+ msgid "Code to be added on HEAD section of every page"
64
+ msgstr "Código que se añade en la sección HEAD de cada página"
65
+
66
+ #: options.php:109
67
+ msgid "Code to be added before the end of the page"
68
+ msgstr "Código que se añade antes del final de la página"
69
+
70
+ #: options.php:118
71
+ msgid "Code to be inserted before each post"
72
+ msgstr "Código que se inserta antes de cada post"
73
+
74
+ #: options.php:119
75
+ msgid "Code to be inserted after each post"
76
+ msgstr "Código que se inserta después de cada post"
77
+
78
+ #: options.php:125
79
+ msgid "Code to be inserted before each page"
80
+ msgstr "Código que se inserta antes de cada página"
81
+
82
+ #: options.php:126
83
+ msgid "Code to be inserted after each page"
84
+ msgstr "Código que se inserta después de cada página"
85
+
86
+ #: options.php:133
87
+ msgid "Facebook page type for the generic web page"
88
+ msgstr "Tipo de página de Facebook para la página web genérica"
89
+
90
+ #: options.php:133
91
+ msgid "Usually \"article\" is the right choice, if empty will be skipped"
92
+ msgstr ""
93
+ "Usualmente \"artículo\" es la opción correcta, si está vacío se odviará"
94
+
95
+ #: options.php:134
96
+ msgid "Facebook page type for the home"
97
+ msgstr "Tipo de página de Facebook para el inicio"
98
+
99
+ #: options.php:134
100
+ msgid ""
101
+ "Usually \"blog\" is a good choice, if empty will be used the generic type"
102
+ msgstr ""
103
+ "Usualmente, \"blog\" es una buena opción, si está vacío se utilizará el tipo "
104
+ "genérico"
105
+
106
+ #: options.php:135
107
+ msgid "Facebook Open Graph Image"
108
+ msgstr "Imagen de Open Graph de Facebook"
109
+
110
+ #: options.php:135
111
+ msgid ""
112
+ "Adds the Facebook Open Graph metatag with a reference to the first post image"
113
+ msgstr "Adiciona una metaetiqueta Open Graph con una referencia al primer post"
114
+
115
+ #: options.php:138
116
+ msgid "Facebook Open Graph default image"
117
+ msgstr "Imagen Open Graph de Facebook por defecto "
118
+
119
+ #: options.php:143
120
+ msgid ""
121
+ "If no image can be extracted from a post, that image URL will be used (if "
122
+ "present)."
123
+ msgstr ""
124
+ "Si no hay imagen que se pueda extraer de un post, se utilizará la URL de la "
125
+ "imagen (si está presente)."
126
+
127
+ #: options.php:144
128
+ msgid ""
129
+ "<strong>Warning.</strong> On some versions of WordPress after the image "
130
+ "selection button is pressed the tabs above does not change anymore. Just "
131
+ "save so\r\n"
132
+ " this page is reloaded (<a href=\"http://wordpress."
133
+ "org/support/topic/wp-32-thickbox-jquery-ui-tabs-conflict\" target=\"_blank"
134
+ "\">reference</a>)."
135
+ msgstr ""
136
+ "<strong>Advertencia.</strong> En algunas versiones de WordPress después de "
137
+ "pulsar el botón de selección de imagen, las pestañas de abajo no cambian "
138
+ "más. Sólo salve para que \r\n"
139
+ " esta página se vuelva a cargar (<a href=\"http://wordpress.org/support/"
140
+ "topic/wp-32-thickbox-jquery-ui-tabs-conflict\" target=\"_blank\">referencia</"
141
+ "a>)."
142
+
143
+ #: options.php:153
144
+ msgid ""
145
+ "Common snippets that can be used in any header or footer area referring them "
146
+ "as [snippet_N] where N is the snippet number\r\n"
147
+ " from 1 to 5. Snippets are inserted before PHP evaluation."
148
+ msgstr ""
149
+ "Fragmentos comunes que se pueden utilizar en cualquier área del head o "
150
+ "footer refiriéndose a ellos como [snippet_N] donde N es el número del "
151
+ "fragmento \r\n"
152
+ " de 1 a 5. Los fragmentos se insertan antes de la evaluación de PHP."
153
+
154
+ #: options.php:155
155
+ msgid ""
156
+ "Useful for social button to be placed before and after the post or in posts "
157
+ "and pages."
158
+ msgstr ""
159
+ "Útil para el botón social para ser colocado antes y después del post y las "
160
+ "páginas."
161
+
162
+ #: options.php:166
163
+ msgid "Notes and parked codes"
164
+ msgstr "Notas y códigos estacionados"
165
+
166
+ #: options.php:170
167
+ msgid "save"
168
+ msgstr "salvar"
languages/header-footer-ru_RU.mo ADDED
Binary file
languages/header-footer-ru_RU.po ADDED
@@ -0,0 +1,341 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ msgid ""
2
+ msgstr ""
3
+ "Project-Id-Version: Header and Footer v1.5.0\n"
4
+ "Report-Msgid-Bugs-To: \n"
5
+ "POT-Creation-Date: \n"
6
+ "PO-Revision-Date: 2014-02-04 10:24:29+0000\n"
7
+ "Last-Translator: izevg <jack.zhukov@gmail.com>\n"
8
+ "Language-Team: \n"
9
+ "MIME-Version: 1.0\n"
10
+ "Content-Type: text/plain; charset=UTF-8\n"
11
+ "Content-Transfer-Encoding: 8bit\n"
12
+ "Plural-Forms: nplurals=3; plural=n%100/10==1 ? 2 : n%10==1 ? 0 : (n+9)%10>3 ? 2 : 1;\n"
13
+ "X-Generator: CSL v1.x\n"
14
+ "X-Poedit-Language: Russian\n"
15
+ "X-Poedit-Country: RUSSIA\n"
16
+ "X-Poedit-SourceCharset: utf-8\n"
17
+ "X-Poedit-KeywordsList: __;_e;__ngettext:1,2;_n:1,2;__ngettext_noop:1,2;_n_noop:1,2;_c,_nc:4c,1,2;_x:1,2c;_ex:1,2c;_nx:4c,1,2;_nx_noop:4c,1,2;\n"
18
+ "X-Poedit-Basepath: ../\n"
19
+ "X-Poedit-Bookmarks: \n"
20
+ "X-Poedit-SearchPath-0: .\n"
21
+ "X-Textdomain-Support: yes"
22
+
23
+ #: admin.php:39
24
+ #@ header-footer
25
+ msgid "Header and Footer"
26
+ msgstr "Заголовок и Подвал"
27
+
28
+ #: admin.php:55
29
+ #@ header-footer
30
+ msgid "Disable top injection"
31
+ msgstr "Отключить показ кода сверху"
32
+
33
+ #: admin.php:60
34
+ #@ header-footer
35
+ msgid "Disable bottom injection"
36
+ msgstr "Отключить показ кода снизу"
37
+
38
+ #: options.php:189
39
+ #@ default
40
+ msgid "PHP is allowed on textareas below."
41
+ msgstr ""
42
+
43
+ #: options.php:189
44
+ #@ default
45
+ msgid "If you use bbPress, read the official page."
46
+ msgstr ""
47
+
48
+ #: options.php:196
49
+ #@ header-footer
50
+ msgid "Page head and footer"
51
+ msgstr "Заголовок и Подвал страницы"
52
+
53
+ #: options.php:197
54
+ #@ header-footer
55
+ msgid "Post content"
56
+ msgstr "Содержимое записи"
57
+
58
+ #: options.php:198
59
+ #@ header-footer
60
+ msgid "Post content (mobile)"
61
+ msgstr "Содержимое записи (мобильн. версия)"
62
+
63
+ #: options.php:199
64
+ #@ header-footer
65
+ msgid "Page content"
66
+ msgstr "Содержимое страницы"
67
+
68
+ #: options.php:200
69
+ #@ header-footer
70
+ msgid "Page content (mobile)"
71
+ msgstr "Содержимое страницы (мобильн. версия)"
72
+
73
+ #: options.php:201
74
+ #@ header-footer
75
+ msgid "Facebook"
76
+ msgstr "Facebook"
77
+
78
+ #: options.php:202
79
+ #@ header-footer
80
+ msgid "SEO"
81
+ msgstr "SEO"
82
+
83
+ #: options.php:203
84
+ #@ header-footer
85
+ msgid "Snippets"
86
+ msgstr "Сниппеты"
87
+
88
+ #: options.php:204
89
+ #@ header-footer
90
+ msgid "BBPress"
91
+ msgstr "BBPress"
92
+
93
+ #: options.php:205
94
+ #@ header-footer
95
+ msgid "Advanced"
96
+ msgstr "Дополнительно"
97
+
98
+ #: options.php:206
99
+ #@ header-footer
100
+ msgid "Notes and..."
101
+ msgstr "Вставки и..."
102
+
103
+ #: options.php:211
104
+ #@ header-footer
105
+ msgid "Code to be added on HEAD section of the home"
106
+ msgstr "Код, который надо вставить в Заголовок Домашней страницы"
107
+
108
+ #: options.php:212
109
+ #@ header-footer
110
+ msgid "Code to be added on HEAD section of every page"
111
+ msgstr "Код, который надо вставить в Заголовок каждой страницы"
112
+
113
+ #: options.php:213
114
+ #@ header-footer
115
+ msgid "Code to be added before the end of the page"
116
+ msgstr "Код, который надо вставить в конец (Подвал) страницы"
117
+
118
+ #: options.php:222
119
+ #@ header-footer
120
+ msgid "Enable injection on category pages"
121
+ msgstr "Включить вставку на страницах Категорий"
122
+
123
+ #: options.php:223
124
+ #: options.php:241
125
+ #@ header-footer
126
+ msgid "Code to be inserted before each post"
127
+ msgstr "Код, который надо вставить перед каждой Записью"
128
+
129
+ #: options.php:224
130
+ #: options.php:242
131
+ #@ header-footer
132
+ msgid "Code to be inserted after each post"
133
+ msgstr "Код, который надо вставить после каждой Записи"
134
+
135
+ #: options.php:227
136
+ #@ header-footer
137
+ msgid "Injection on excerpts"
138
+ msgstr "Вставки в выборках"
139
+
140
+ #: options.php:228
141
+ #@ header-footer
142
+ msgid "It works only on category and tag pages."
143
+ msgstr "Работают только на страницах Категорий и Тегов."
144
+
145
+ #: options.php:230
146
+ #@ header-footer
147
+ msgid "Code to be inserted before each post excerpt"
148
+ msgstr "Код, который надо вставить перед каждым Мета-блоком"
149
+
150
+ #: options.php:231
151
+ #@ header-footer
152
+ msgid "Code to be inserted after each post excerpt"
153
+ msgstr "Код, который надо вставить после каждого Мета-блока"
154
+
155
+ #: options.php:240
156
+ #: options.php:259
157
+ #@ header-footer
158
+ msgid "Enable mobile detection and injection"
159
+ msgstr "Включить опознавание мобильн. устройства, и вставку в его отображение"
160
+
161
+ #: options.php:249
162
+ #: options.php:260
163
+ #@ header-footer
164
+ msgid "Code to be inserted before each page"
165
+ msgstr "Код, который надо вставить перед каждой страницей"
166
+
167
+ #: options.php:250
168
+ #: options.php:261
169
+ #@ header-footer
170
+ msgid "Code to be inserted after each page"
171
+ msgstr "Код, который надо вставить после каждой страницы"
172
+
173
+ #: options.php:269
174
+ #@ header-footer
175
+ msgid "Enable the OG metatag"
176
+ msgstr "Включить метатег OG"
177
+
178
+ #: options.php:269
179
+ #@ header-footer
180
+ msgid "Enable the Facebook Open Graph metatag"
181
+ msgstr "Включить метатег Facebook Open Graph"
182
+
183
+ #: options.php:271
184
+ #@ header-footer
185
+ msgid "Facebook application id"
186
+ msgstr "ID приложения в Facebook"
187
+
188
+ #: options.php:272
189
+ #@ header-footer
190
+ msgid "Facebook page type for the generic web page"
191
+ msgstr "Тип страницы в Facebook для сходной веб-страницы"
192
+
193
+ #: options.php:272
194
+ #@ header-footer
195
+ msgid "Usually \"article\" is the right choice, if empty will be skipped"
196
+ msgstr "Обычно, \"article\" является правильным выбором. Если оставить пустым, поле будет пропущено"
197
+
198
+ #: options.php:273
199
+ #@ header-footer
200
+ msgid "Facebook page type for the home"
201
+ msgstr "Тип страницы Facebook для Домашней страницы"
202
+
203
+ #: options.php:273
204
+ #@ header-footer
205
+ msgid "Usually \"blog\" is a good choice, if empty will be used the generic type"
206
+ msgstr "Обычно, \"blog\" является правильным выбором. Если оставить поле пустым, будет использован сходный тип"
207
+
208
+ #: options.php:274
209
+ #@ header-footer
210
+ msgid "Facebook Open Graph Image"
211
+ msgstr "Изображение Facebook Open Graph"
212
+
213
+ #: options.php:274
214
+ #@ header-footer
215
+ msgid "Adds the Facebook Open Graph metatag with a reference to the first post image"
216
+ msgstr "Добавляет метатег Facebook Open Graph с отсылкой к первому изображению Записи"
217
+
218
+ #: options.php:277
219
+ #@ default
220
+ msgid "Facebook Open Graph default image"
221
+ msgstr ""
222
+
223
+ #: options.php:283
224
+ #@ default
225
+ msgid "If no image can be extracted from a post, that image URL will be used (if present)."
226
+ msgstr ""
227
+
228
+ #: options.php:284
229
+ #@ default
230
+ msgid ""
231
+ "<strong>Warning.</strong> On some versions of WordPress after the image selection button is pressed the tabs above does not change anymore. Just save so\n"
232
+ " this page is reloaded (<a href=\"http://wordpress.org/support/topic/wp-32-thickbox-jquery-ui-tabs-conflict\" target=\"_blank\">reference</a>)."
233
+ msgstr ""
234
+
235
+ #: options.php:291
236
+ #@ header-footer
237
+ msgid "Facebook async SDK"
238
+ msgstr "Асинхронн. Facebook SDK"
239
+
240
+ #: options.php:291
241
+ #@ header-footer
242
+ msgid "Adds the Facebook SDK"
243
+ msgstr "Добавляет Facebook SDK"
244
+
245
+ #: options.php:292
246
+ #@ header-footer
247
+ msgid "Facebook app id"
248
+ msgstr "ID приложения в Facebook"
249
+
250
+ #: options.php:292
251
+ #@ header-footer
252
+ msgid "Optional"
253
+ msgstr "Опционально"
254
+
255
+ #: options.php:299
256
+ #@ default
257
+ msgid "Please, see the <a href=\"http://www.satollo.net/plugins/header-footer\" target=\"_blank\">Header and Footer</strong></a> page before to use those options."
258
+ msgstr ""
259
+
260
+ #: options.php:307
261
+ #@ header-footer
262
+ msgid "Add noindex for page 2 and up"
263
+ msgstr "Добавить атрибут NOINDEX для страницы 2 и далее"
264
+
265
+ #: options.php:313
266
+ #@ header-footer
267
+ msgid "Add noindex for search result pages"
268
+ msgstr "Добавить атрибут NOINDEX для страницы результатов поиска"
269
+
270
+ #: options.php:319
271
+ #@ header-footer
272
+ msgid "Add canonical to home page"
273
+ msgstr "Добавляет постоянную ссылку на Домашнюю страницу"
274
+
275
+ #: options.php:326
276
+ #@ header-footer
277
+ msgid "Add noindex for page 2 and up for archives (by category, author, date, tag, ...)"
278
+ msgstr "Добавляет атрибут NOINDEX для страницы 2 и далее для архивов (по категориям, авторам, датам, тегам, ...)"
279
+
280
+ #: options.php:335
281
+ #@ header-footer
282
+ msgid ""
283
+ "Common snippets that can be used in any header or footer area referring them as [snippet_N] where N is the snippet number\n"
284
+ " from 1 to 5. Snippets are inserted before PHP evaluation."
285
+ msgstr "Общедоступные сниппеты, которые могут быть использованы в любом Заголовке или Подвале с помощью шорткода [snippet_N], где N - номер необходимого сниппета (от 1 до 5). Сниппеты вставляются перед выполняемым PHP-кодом."
286
+
287
+ #: options.php:337
288
+ #@ header-footer
289
+ msgid "Useful for social button to be placed before and after the post or in posts and pages."
290
+ msgstr "Полезно для размещения кнопок социальных сетей перед и после Записи, или в Записях и Страницах."
291
+
292
+ #: options.php:341
293
+ #@ header-footer
294
+ msgid "Snippet "
295
+ msgstr "Сниппет"
296
+
297
+ #: options.php:351
298
+ #@ header-footer
299
+ msgid "Before single forum"
300
+ msgstr "Перед каждым форумом"
301
+
302
+ #: options.php:352
303
+ #@ header-footer
304
+ msgid "Before single topic"
305
+ msgstr "Перед каждым топиком"
306
+
307
+ #: options.php:353
308
+ #@ header-footer
309
+ msgid "Before reply content"
310
+ msgstr "Перед содержимым ответа"
311
+
312
+ #: options.php:354
313
+ #@ header-footer
314
+ msgid "After reply content"
315
+ msgstr "После содержимого ответа"
316
+
317
+ #: options.php:363
318
+ #@ header-footer
319
+ msgid "PHP code to be executed on plugin init"
320
+ msgstr "PHP-код для выполнения при инициализации плагина"
321
+
322
+ #: options.php:369
323
+ #@ header-footer
324
+ msgid "Enable - experimental do not use until documented"
325
+ msgstr "Активировать - экспериментальная функция, не используйте её, пока она не задокументирована"
326
+
327
+ #: options.php:372
328
+ #@ header-footer
329
+ msgid "Mobile user agent strings"
330
+ msgstr "Строки user-agent для мобильных устройств"
331
+
332
+ #: options.php:385
333
+ #@ header-footer
334
+ msgid "Notes and parked codes"
335
+ msgstr "Вставки и парковочные коды"
336
+
337
+ #: options.php:389
338
+ #@ header-footer
339
+ msgid "save"
340
+ msgstr "Сохранить"
341
+
languages/header-footer.pot ADDED
@@ -0,0 +1,147 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (C) 2010
2
+ # This file is distributed under the same license as the package.
3
+ msgid ""
4
+ msgstr ""
5
+ "Project-Id-Version: \n"
6
+ "Report-Msgid-Bugs-To: http://wordpress.org/tag/header-footer\n"
7
+ "POT-Creation-Date: 2011-11-28 08:30:40+00:00\n"
8
+ "MIME-Version: 1.0\n"
9
+ "Content-Type: text/plain; charset=UTF-8\n"
10
+ "Content-Transfer-Encoding: 8bit\n"
11
+ "PO-Revision-Date: 2010-MO-DA HO:MI+ZONE\n"
12
+ "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13
+ "Language-Team: LANGUAGE <LL@li.org>\n"
14
+
15
+ #: options.php:87
16
+ msgid ""
17
+ "Detailed documentation and FAQs can be found online on <a href=\"http://www."
18
+ "satollo.net/plugins/header-footer\"><strong>Header and Footer plugin "
19
+ "official page</strong></a>."
20
+ msgstr ""
21
+
22
+ #: options.php:88
23
+ msgid "PHP is allowed on textareas below."
24
+ msgstr ""
25
+
26
+ #: options.php:88
27
+ msgid "If you use bbPress, read the official page."
28
+ msgstr ""
29
+
30
+ #: options.php:97
31
+ msgid "Page head and footer"
32
+ msgstr ""
33
+
34
+ #: options.php:98
35
+ msgid "Post content"
36
+ msgstr ""
37
+
38
+ #: options.php:99
39
+ msgid "Page content"
40
+ msgstr ""
41
+
42
+ #: options.php:100
43
+ msgid "Facebook"
44
+ msgstr ""
45
+
46
+ #: options.php:101
47
+ msgid "Snippets"
48
+ msgstr ""
49
+
50
+ #: options.php:102
51
+ msgid "Notes and..."
52
+ msgstr ""
53
+
54
+ #: options.php:107
55
+ msgid "Code to be added on HEAD section of the home"
56
+ msgstr ""
57
+
58
+ #: options.php:108
59
+ msgid "Code to be added on HEAD section of every page"
60
+ msgstr ""
61
+
62
+ #: options.php:109
63
+ msgid "Code to be added before the end of the page"
64
+ msgstr ""
65
+
66
+ #: options.php:118
67
+ msgid "Code to be inserted before each post"
68
+ msgstr ""
69
+
70
+ #: options.php:119
71
+ msgid "Code to be inserted after each post"
72
+ msgstr ""
73
+
74
+ #: options.php:125
75
+ msgid "Code to be inserted before each page"
76
+ msgstr ""
77
+
78
+ #: options.php:126
79
+ msgid "Code to be inserted after each page"
80
+ msgstr ""
81
+
82
+ #: options.php:133
83
+ msgid "Facebook page type for the generic web page"
84
+ msgstr ""
85
+
86
+ #: options.php:133
87
+ msgid "Usually \"article\" is the right choice, if empty will be skipped"
88
+ msgstr ""
89
+
90
+ #: options.php:134
91
+ msgid "Facebook page type for the home"
92
+ msgstr ""
93
+
94
+ #: options.php:134
95
+ msgid ""
96
+ "Usually \"blog\" is a good choice, if empty will be used the generic type"
97
+ msgstr ""
98
+
99
+ #: options.php:135
100
+ msgid "Facebook Open Graph Image"
101
+ msgstr ""
102
+
103
+ #: options.php:135
104
+ msgid ""
105
+ "Adds the Facebook Open Graph metatag with a reference to the first post image"
106
+ msgstr ""
107
+
108
+ #: options.php:138
109
+ msgid "Facebook Open Graph default image"
110
+ msgstr ""
111
+
112
+ #: options.php:143
113
+ msgid ""
114
+ "If no image can be extracted from a post, that image URL will be used (if "
115
+ "present)."
116
+ msgstr ""
117
+
118
+ #: options.php:144
119
+ msgid ""
120
+ "<strong>Warning.</strong> On some versions of WordPress after the image "
121
+ "selection button is pressed the tabs above does not change anymore. Just "
122
+ "save so\r\n"
123
+ " this page is reloaded (<a href=\"http://wordpress."
124
+ "org/support/topic/wp-32-thickbox-jquery-ui-tabs-conflict\" target=\"_blank"
125
+ "\">reference</a>)."
126
+ msgstr ""
127
+
128
+ #: options.php:153
129
+ msgid ""
130
+ "Common snippets that can be used in any header or footer area referring them "
131
+ "as [snippet_N] where N is the snippet number\r\n"
132
+ " from 1 to 5. Snippets are inserted before PHP evaluation."
133
+ msgstr ""
134
+
135
+ #: options.php:155
136
+ msgid ""
137
+ "Useful for social button to be placed before and after the post or in posts "
138
+ "and pages."
139
+ msgstr ""
140
+
141
+ #: options.php:166
142
+ msgid "Notes and parked codes"
143
+ msgstr ""
144
+
145
+ #: options.php:170
146
+ msgid "save"
147
+ msgstr ""
lib/easytabs/jquery.easytabs.min.js ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * jQuery EasyTabs plugin 3.2.0
3
+ *
4
+ * Copyright (c) 2010-2011 Steve Schwartz (JangoSteve)
5
+ *
6
+ * Dual licensed under the MIT and GPL licenses:
7
+ * http://www.opensource.org/licenses/mit-license.php
8
+ * http://www.gnu.org/licenses/gpl.html
9
+ *
10
+ * Date: Thu May 09 17:30:00 2013 -0500
11
+ */
12
+ (function(a){a.easytabs=function(j,e){var f=this,q=a(j),i={animate:true,panelActiveClass:"active",tabActiveClass:"active",defaultTab:"li:first-child",animationSpeed:"normal",tabs:"> ul > li",updateHash:true,cycle:false,collapsible:false,collapsedClass:"collapsed",collapsedByDefault:true,uiTabs:false,transitionIn:"fadeIn",transitionOut:"fadeOut",transitionInEasing:"swing",transitionOutEasing:"swing",transitionCollapse:"slideUp",transitionUncollapse:"slideDown",transitionCollapseEasing:"swing",transitionUncollapseEasing:"swing",containerClass:"",tabsClass:"",tabClass:"",panelClass:"",cache:true,event:"click",panelContext:q},h,l,v,m,d,t={fast:200,normal:400,slow:600},r;f.init=function(){f.settings=r=a.extend({},i,e);r.bind_str=r.event+".easytabs";if(r.uiTabs){r.tabActiveClass="ui-tabs-selected";r.containerClass="ui-tabs ui-widget ui-widget-content ui-corner-all";r.tabsClass="ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all";r.tabClass="ui-state-default ui-corner-top";r.panelClass="ui-tabs-panel ui-widget-content ui-corner-bottom"}if(r.collapsible&&e.defaultTab!==undefined&&e.collpasedByDefault===undefined){r.collapsedByDefault=false}if(typeof(r.animationSpeed)==="string"){r.animationSpeed=t[r.animationSpeed]}a("a.anchor").remove().prependTo("body");q.data("easytabs",{});f.setTransitions();f.getTabs();b();g();w();n();c();q.attr("data-easytabs",true)};f.setTransitions=function(){v=(r.animate)?{show:r.transitionIn,hide:r.transitionOut,speed:r.animationSpeed,collapse:r.transitionCollapse,uncollapse:r.transitionUncollapse,halfSpeed:r.animationSpeed/2}:{show:"show",hide:"hide",speed:0,collapse:"hide",uncollapse:"show",halfSpeed:0}};f.getTabs=function(){var x;f.tabs=q.find(r.tabs),f.panels=a(),f.tabs.each(function(){var A=a(this),z=A.children("a"),y=A.children("a").data("target");A.data("easytabs",{});if(y!==undefined&&y!==null){A.data("easytabs").ajax=z.attr("href")}else{y=z.attr("href")}y=y.match(/#([^\?]+)/)[1];x=r.panelContext.find("#"+y);if(x.length){x.data("easytabs",{position:x.css("position"),visibility:x.css("visibility")});x.not(r.panelActiveClass).hide();f.panels=f.panels.add(x);A.data("easytabs").panel=x}else{f.tabs=f.tabs.not(A);if("console" in window){console.warn("Warning: tab without matching panel for selector '#"+y+"' removed from set")}}})};f.selectTab=function(x,C){var y=window.location,B=y.hash.match(/^[^\?]*/)[0],z=x.parent().data("easytabs").panel,A=x.parent().data("easytabs").ajax;if(r.collapsible&&!d&&(x.hasClass(r.tabActiveClass)||x.hasClass(r.collapsedClass))){f.toggleTabCollapse(x,z,A,C)}else{if(!x.hasClass(r.tabActiveClass)||!z.hasClass(r.panelActiveClass)){o(x,z,A,C)}else{if(!r.cache){o(x,z,A,C)}}}};f.toggleTabCollapse=function(x,y,z,A){f.panels.stop(true,true);if(u(q,"easytabs:before",[x,y,r])){f.tabs.filter("."+r.tabActiveClass).removeClass(r.tabActiveClass).children().removeClass(r.tabActiveClass);if(x.hasClass(r.collapsedClass)){if(z&&(!r.cache||!x.parent().data("easytabs").cached)){q.trigger("easytabs:ajax:beforeSend",[x,y]);y.load(z,function(C,B,D){x.parent().data("easytabs").cached=true;q.trigger("easytabs:ajax:complete",[x,y,C,B,D])})}x.parent().removeClass(r.collapsedClass).addClass(r.tabActiveClass).children().removeClass(r.collapsedClass).addClass(r.tabActiveClass);y.addClass(r.panelActiveClass)[v.uncollapse](v.speed,r.transitionUncollapseEasing,function(){q.trigger("easytabs:midTransition",[x,y,r]);if(typeof A=="function"){A()}})}else{x.addClass(r.collapsedClass).parent().addClass(r.collapsedClass);y.removeClass(r.panelActiveClass)[v.collapse](v.speed,r.transitionCollapseEasing,function(){q.trigger("easytabs:midTransition",[x,y,r]);if(typeof A=="function"){A()}})}}};f.matchTab=function(x){return f.tabs.find("[href='"+x+"'],[data-target='"+x+"']").first()};f.matchInPanel=function(x){return(x&&f.validId(x)?f.panels.filter(":has("+x+")").first():[])};f.validId=function(x){return x.substr(1).match(/^[A-Za-z]+[A-Za-z0-9\-_:\.].$/)};f.selectTabFromHashChange=function(){var y=window.location.hash.match(/^[^\?]*/)[0],x=f.matchTab(y),z;if(r.updateHash){if(x.length){d=true;f.selectTab(x)}else{z=f.matchInPanel(y);if(z.length){y="#"+z.attr("id");x=f.matchTab(y);d=true;f.selectTab(x)}else{if(!h.hasClass(r.tabActiveClass)&&!r.cycle){if(y===""||f.matchTab(m).length||q.closest(y).length){d=true;f.selectTab(l)}}}}}};f.cycleTabs=function(x){if(r.cycle){x=x%f.tabs.length;$tab=a(f.tabs[x]).children("a").first();d=true;f.selectTab($tab,function(){setTimeout(function(){f.cycleTabs(x+1)},r.cycle)})}};f.publicMethods={select:function(x){var y;if((y=f.tabs.filter(x)).length===0){if((y=f.tabs.find("a[href='"+x+"']")).length===0){if((y=f.tabs.find("a"+x)).length===0){if((y=f.tabs.find("[data-target='"+x+"']")).length===0){if((y=f.tabs.find("a[href$='"+x+"']")).length===0){a.error("Tab '"+x+"' does not exist in tab set")}}}}}else{y=y.children("a").first()}f.selectTab(y)}};var u=function(A,x,z){var y=a.Event(x);A.trigger(y,z);return y.result!==false};var b=function(){q.addClass(r.containerClass);f.tabs.parent().addClass(r.tabsClass);f.tabs.addClass(r.tabClass);f.panels.addClass(r.panelClass)};var g=function(){var y=window.location.hash.match(/^[^\?]*/)[0],x=f.matchTab(y).parent(),z;if(x.length===1){h=x;r.cycle=false}else{z=f.matchInPanel(y);if(z.length){y="#"+z.attr("id");h=f.matchTab(y).parent()}else{h=f.tabs.parent().find(r.defaultTab);if(h.length===0){a.error("The specified default tab ('"+r.defaultTab+"') could not be found in the tab set ('"+r.tabs+"') out of "+f.tabs.length+" tabs.")}}}l=h.children("a").first();p(x)};var p=function(z){var y,x;if(r.collapsible&&z.length===0&&r.collapsedByDefault){h.addClass(r.collapsedClass).children().addClass(r.collapsedClass)}else{y=a(h.data("easytabs").panel);x=h.data("easytabs").ajax;if(x&&(!r.cache||!h.data("easytabs").cached)){q.trigger("easytabs:ajax:beforeSend",[l,y]);y.load(x,function(B,A,C){h.data("easytabs").cached=true;q.trigger("easytabs:ajax:complete",[l,y,B,A,C])})}h.data("easytabs").panel.show().addClass(r.panelActiveClass);h.addClass(r.tabActiveClass).children().addClass(r.tabActiveClass)}q.trigger("easytabs:initialised",[l,y])};var w=function(){f.tabs.children("a").bind(r.bind_str,function(x){r.cycle=false;d=false;f.selectTab(a(this));x.preventDefault?x.preventDefault():x.returnValue=false})};var o=function(z,D,E,H){f.panels.stop(true,true);if(u(q,"easytabs:before",[z,D,r])){var A=f.panels.filter(":visible"),y=D.parent(),F,x,C,G,B=window.location.hash.match(/^[^\?]*/)[0];if(r.animate){F=s(D);x=A.length?k(A):0;C=F-x}m=B;G=function(){q.trigger("easytabs:midTransition",[z,D,r]);if(r.animate&&r.transitionIn=="fadeIn"){if(C<0){y.animate({height:y.height()+C},v.halfSpeed).css({"min-height":""})}}if(r.updateHash&&!d){window.location.hash="#"+D.attr("id")}else{d=false}D[v.show](v.speed,r.transitionInEasing,function(){y.css({height:"","min-height":""});q.trigger("easytabs:after",[z,D,r]);if(typeof H=="function"){H()}})};if(E&&(!r.cache||!z.parent().data("easytabs").cached)){q.trigger("easytabs:ajax:beforeSend",[z,D]);D.load(E,function(J,I,K){z.parent().data("easytabs").cached=true;q.trigger("easytabs:ajax:complete",[z,D,J,I,K])})}if(r.animate&&r.transitionOut=="fadeOut"){if(C>0){y.animate({height:(y.height()+C)},v.halfSpeed)}else{y.css({"min-height":y.height()})}}f.tabs.filter("."+r.tabActiveClass).removeClass(r.tabActiveClass).children().removeClass(r.tabActiveClass);f.tabs.filter("."+r.collapsedClass).removeClass(r.collapsedClass).children().removeClass(r.collapsedClass);z.parent().addClass(r.tabActiveClass).children().addClass(r.tabActiveClass);f.panels.filter("."+r.panelActiveClass).removeClass(r.panelActiveClass);D.addClass(r.panelActiveClass);if(A.length){A[v.hide](v.speed,r.transitionOutEasing,G)}else{D[v.uncollapse](v.speed,r.transitionUncollapseEasing,G)}}};var s=function(z){if(z.data("easytabs")&&z.data("easytabs").lastHeight){return z.data("easytabs").lastHeight}var B=z.css("display"),y,x;try{y=a("<div></div>",{position:"absolute",visibility:"hidden",overflow:"hidden"})}catch(A){y=a("<div></div>",{visibility:"hidden",overflow:"hidden"})}x=z.wrap(y).css({position:"relative",visibility:"hidden",display:"block"}).outerHeight();z.unwrap();z.css({position:z.data("easytabs").position,visibility:z.data("easytabs").visibility,display:B});z.data("easytabs").lastHeight=x;return x};var k=function(y){var x=y.outerHeight();if(y.data("easytabs")){y.data("easytabs").lastHeight=x}else{y.data("easytabs",{lastHeight:x})}return x};var n=function(){if(typeof a(window).hashchange==="function"){a(window).hashchange(function(){f.selectTabFromHashChange()})}else{if(a.address&&typeof a.address.change==="function"){a.address.change(function(){f.selectTabFromHashChange()})}}};var c=function(){var x;if(r.cycle){x=f.tabs.index(h);setTimeout(function(){f.cycleTabs(x+1)},r.cycle)}};f.init()};a.fn.easytabs=function(c){var b=arguments;return this.each(function(){var e=a(this),d=e.data("easytabs");if(undefined===d){d=new a.easytabs(this,c);e.data("easytabs",d)}if(d.publicMethods[c]){return d.publicMethods[c](Array.prototype.slice.call(b,1))}})}})(jQuery);
lib/easytabs/tabs.css ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* Example Styles for Demo */
2
+ .etabs { margin: 0; padding: 0; }
3
+ .tab { margin-bottom: 0; display: inline-block; zoom:1; *display:inline; background: #eee; border: solid 1px #999; border-bottom: none; -moz-border-radius: 4px 4px 0 0; -webkit-border-radius: 4px 4px 0 0; }
4
+ .tab a { font-size: 14px; line-height: 2em; display: block; padding: 0 10px; outline: none; }
5
+ .tab a:hover { text-decoration: underline; }
6
+ .tab.active { background: #fff; padding-top: 6px; position: relative; top: 1px; border-color: #666; }
7
+ .tab a.active { font-weight: bold; }
8
+ .tab-container .panel-container { background-color: #fff; border: solid #666 1px; padding: 10px; -moz-border-radius: 0 4px 4px 4px; -webkit-border-radius: 0 4px 4px 4px; }
9
+ .panel-container { margin-bottom: 10px; }
10
+
11
+ /* Styles for alternate tabActiveClass */
12
+ .tab.selected-tab { background: #fff; padding-top: 6px; position: relative; top: 1px; border-color: #666; }
13
+ .tab a.selected-tab { font-weight: bold; }
14
+
15
+ /* Styles for Tabs on Side */
16
+ #tab-side-container { background: #fff; border: solid 1px; height: 300px; }
17
+ #tab-side-container ul { height: 300px; list-style: none; margin: 0; padding: 0; background: #ccc; float: left; border-right: solid 1px; }
18
+ #tab-side-container ul li { width: 100px; margin: 0; padding: 0; text-align: center; }
19
+ #tab-side-container ul li a { display: block; padding: 15px 0; outline: none; }
20
+ #tab-side-container ul li a:hover { text-decoration: underline; }
21
+ #tab-side-container ul li.selected-tab { background: #fff; position: relative; left: 1px; border-style: solid; border-width: 1px 0; }
22
+ #tab-side-container ul li:first-child.selected-tab { border-top: none; }
23
+ #tab-side-container ul li a.selected-tab { font-weight: bold; text-decoration: none; }
24
+ #tab-side-container .panel-container { background: #fff; padding-top: 5px; padding-left: 120px; }
25
+
26
+ /* Styles for Tabs on Bottom */
27
+ #tab-bottom-container { width: 100%; padding: 0; clear: both; }
28
+ #tab-bottom-container ul { list-style: none; margin: 0; padding: 0; width: 100%; }
29
+ #tab-bottom-container ul li { float: left; width: 33.33%; margin: 0; padding: 0; text-align: center; border-top: solid 1px; }
30
+ #tab-bottom-container ul li a { display: block; padding: 15px 0; outline: none; }
31
+ #tab-bottom-container ul li a:hover { text-decoration: underline; }
32
+ #tab-bottom-container ul li.active { width: 33%; background: #fff; border: solid 1px; border-top: none; }
33
+ #tab-bottom-container ul li a.active { font-weight: bold; text-decoration: none; }
34
+ #tab-bottom-container .panel-container { background: #fff; border: solid 1px; border-bottom: none; padding: 10px; margin-bottom: 0; }
35
+
36
+ /* Styles for Collapsible */
37
+ #tab-collapsible-container { background: #fff; padding: 5px; border: solid 1px; }
38
+ #tab-collapsible-container ul { margin: 0; padding: 5px 5px 0; background: #ccc; border: solid 1px; }
39
+ #tab-collapsible-container ul li { display: inline-block; background: #ccc; border: solid 1px; border-bottom: none; margin: 0; }
40
+ #tab-collapsible-container ul li a { display: block; padding: 5px; outline: none; }
41
+ #tab-collapsible-container ul li a:hover { text-decoration: underline; }
42
+ #tab-collapsible-container ul li.active { background: #fff; padding-top: 6px; position: relative; top: 1px; }
43
+ #tab-collapsible-container ul li a.active { font-weight: bold; }
44
+ #tab-collapsible-container .panel-container { background: #fff; border: none; padding: 0 10px; background: #fff; }
45
+
46
+ /* Styles for Disconnected */
47
+ #tab-disconnected-container ul { height: 52px; width: 100%; list-style: none; margin: 0; padding: 0; background: #ccc; }
48
+ #tab-disconnected-container ul li { float: left; width: 33.3%; margin: 0; padding: 0; text-align: center; }
49
+ #tab-disconnected-container ul li a { display: block; padding: 15px 0; line-height: 20px; outline: none; }
50
+ #tab-disconnected-container ul li a:hover { text-decoration: underline; }
51
+ #tab-disconnected-container ul li.active { background: #fff; position: relative; left: 1px; }
52
+ #tab-disconnected-container ul li a.active { font-weight: bold; text-decoration: none; border: solid 1px; }
53
+ #tab-disconnected-container .panel-container { background: #fff; padding: 10px; border: solid 1px; }
54
+ #disconnected-tab3 { background: #fff; padding: 10px 15px; border: solid 1px #999; -moz-border-radius: 5px; -webkit-border-radius: 5px; margin: 15px 0; }
55
+
56
+ /* Styles for Transition Options */
57
+ #tab-transition-container .panel-container { padding: 5px; }
58
+ #tab-transition-container .panel-container .active { padding: 0 5px; }
59
+
60
+ /* Styles for Form Sections */
61
+ .tab-container fieldset { background: #fff; }
62
+ .tab-container .field-container { background: #fff; border: solid #666 1px; padding: 10px; -moz-border-radius: 0 4px 4px 4px; -webkit-border-radius: 0 4px 4px 4px; }
63
+
64
+ /* Styles for Previous and Next buttons */
65
+ .prev-tab, .next-tab { display: block; width: 100px; text-align: center; margin-top: 10px; }
66
+ span.prev-tab, span.next-tab { background: #ccc; }
67
+ .prev-tab { float: left; }
68
+ .next-tab { float: right; }
69
+
70
+ /* Styles for Twitter Boostrap */
71
+ #twitter-bootstrap-container { background: #fff; padding: 10px; height: 75px; margin-bottom: 15px; }
options.php ADDED
@@ -0,0 +1,771 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ if (function_exists('load_plugin_textdomain')) {
3
+ load_plugin_textdomain('header-footer', false, 'header-footer/languages');
4
+ }
5
+
6
+ $dismissed = get_option('hefo_dismissed', array());
7
+
8
+ if (isset($_REQUEST['dismiss']) && check_admin_referer()) {
9
+ $dismissed[$_REQUEST['dismiss']] = 1;
10
+ update_option('hefo_dismissed', $dismissed);
11
+ wp_redirect('?page=header-footer%2Foptions.php');
12
+ exit();
13
+ }
14
+
15
+ function hefo_request($name, $default = null) {
16
+ if (!isset($_REQUEST[$name]))
17
+ return $default;
18
+ return stripslashes_deep($_REQUEST[$name]);
19
+ }
20
+
21
+ function hefo_field_checkbox($name, $label = '', $tips = '', $attrs = '') {
22
+ global $options;
23
+ echo '<th scope="row">';
24
+ echo '<label for="options[' . $name . ']">' . $label . '</label></th>';
25
+ echo '<td><input type="checkbox" ' . $attrs . ' name="options[' . $name . ']" value="1" ' .
26
+ (isset($options[$name]) ? 'checked' : '') . '/>';
27
+ echo ' ' . $tips;
28
+ echo '</td>';
29
+ }
30
+
31
+ function hefo_base_checkbox($name, $label = '') {
32
+ global $options;
33
+ echo '<label>';
34
+ echo '<input type="checkbox" ' . $attrs . ' name="options[' . $name . ']" value="1" ' .
35
+ (isset($options[$name]) ? 'checked' : '') . '>';
36
+ echo $label;
37
+ echo '</label>';
38
+ }
39
+
40
+ function hefo_field_checkbox_only($name, $tips = '', $attrs = '', $link = null) {
41
+ global $options;
42
+ echo '<td><input type="checkbox" ' . $attrs . ' name="options[' . $name . ']" value="1" ' .
43
+ (isset($options[$name]) ? 'checked' : '') . '/>';
44
+ echo ' ' . $tips;
45
+ if ($link) {
46
+ echo '<br><a href="' . $link . '" target="_blank">Read more</a>.';
47
+ }
48
+ echo '</td>';
49
+ }
50
+
51
+ function hefo_field_text($name, $label = '', $tips = '', $attrs = '') {
52
+ global $options;
53
+
54
+ if (!isset($options[$name]))
55
+ $options[$name] = '';
56
+
57
+ echo '<th scope="row">';
58
+ echo '<label for="options[' . $name . ']">' . $label . '</label></th>';
59
+ echo '<td><input type="text" name="options[' . $name . ']" value="' .
60
+ htmlspecialchars($options[$name]) . '" size="50"/>';
61
+ echo '<br /> ' . $tips;
62
+ echo '</td>';
63
+ }
64
+
65
+ function hefo_base_text($name) {
66
+ global $options;
67
+
68
+ if (!isset($options[$name])) {
69
+ $options[$name] = '';
70
+ }
71
+
72
+ echo '<input type="text" name="options[' . $name . ']" value="' .
73
+ esc_attr($options[$name]) . '" size="30">';
74
+ }
75
+
76
+ function hefo_field_textarea($name, $label = '', $tips = '', $attrs = '') {
77
+ global $options;
78
+
79
+ if (!isset($options[$name]))
80
+ $options[$name] = '';
81
+
82
+ if (is_array($options[$name]))
83
+ $options[$name] = implode("\n", $options[$name]);
84
+
85
+ if (strpos($attrs, 'cols') === false)
86
+ $attrs .= 'cols="70"';
87
+ if (strpos($attrs, 'rows') === false)
88
+ $attrs .= 'rows="5"';
89
+
90
+ echo '<th scope="row">';
91
+ echo '<label for="options[' . $name . ']">' . $label . '</label></th>';
92
+ echo '<td><textarea style="width: 100%; height: 100px" wrap="off" name="options[' . $name . ']">' .
93
+ htmlspecialchars($options[$name]) . '</textarea>';
94
+ echo '<p class="description">' . $tips . '</p>';
95
+ echo '</td>';
96
+ }
97
+
98
+ function hefo_field_textarea_cm($name, $label = '', $tips = '', $attrs = '') {
99
+ global $options;
100
+
101
+ if (!isset($options[$name]))
102
+ $options[$name] = '';
103
+
104
+ if (is_array($options[$name]))
105
+ $options[$name] = implode("\n", $options[$name]);
106
+
107
+ if (strpos($attrs, 'cols') === false)
108
+ $attrs .= 'cols="70"';
109
+ if (strpos($attrs, 'rows') === false)
110
+ $attrs .= 'rows="5"';
111
+
112
+ echo '<th scope="row">';
113
+ echo '<label for="options[' . $name . ']">' . $label . '</label></th>';
114
+ echo '<td><textarea style="width: 100%; height: 100px" wrap="off" name="options[' . $name . ']" onfocus="hefo_cm_on(this)" onblur="hefo_cm_off(this)">' .
115
+ htmlspecialchars($options[$name]) . '</textarea>';
116
+ echo '<p class="description">' . $tips . '</p>';
117
+ echo '</td>';
118
+ }
119
+
120
+ function hefo_base_textarea_cm($name) {
121
+ global $options;
122
+
123
+ if (!isset($options[$name]))
124
+ $options[$name] = '';
125
+
126
+ if (is_array($options[$name]))
127
+ $options[$name] = implode("\n", $options[$name]);
128
+
129
+ echo '<textarea class="hefo-cm" name="options[' . $name . ']" onfocus="hefo_cm_on(this)">';
130
+ echo htmlspecialchars($options[$name]);
131
+ echo '</textarea>';
132
+ echo '<p class="description">' . $tips . '</p>';
133
+ }
134
+
135
+ function hefo_field_textarea_enable($name, $label = '', $tips = '', $attrs = '') {
136
+ global $options;
137
+
138
+ if (!isset($options[$name]))
139
+ $options[$name] = '';
140
+
141
+ if (is_array($options[$name]))
142
+ $options[$name] = implode("\n", $options[$name]);
143
+
144
+ if (strpos($attrs, 'cols') === false)
145
+ $attrs .= 'cols="70"';
146
+ if (strpos($attrs, 'rows') === false)
147
+ $attrs .= 'rows="5"';
148
+
149
+ echo '<th scope="row">';
150
+ echo '<label for="options[' . $name . ']">' . $label . '</label></th>';
151
+ echo '<td>';
152
+ echo '<input type="checkbox" ' . $attrs . ' name="options[' . $name . '_enabled]" value="1" ' .
153
+ (isset($options[$name . '_enabled']) ? 'checked' : '') . '> Enable<br>';
154
+ echo '<textarea style="width: 100%; height: 100px" wrap="off" name="options[' . $name . ']">' .
155
+ htmlspecialchars($options[$name]) . '</textarea>';
156
+ echo '<p class="description">' . $tips . '</p>';
157
+ echo '</td>';
158
+ }
159
+
160
+ function hefo_rule($number) {
161
+ global $options;
162
+ if (!isset($options['inner_pos_' . $number]))
163
+ $options['inner_pos_' . $number] = 'after';
164
+ if (!isset($options['inner_skip_' . $number]))
165
+ $options['inner_skip_' . $number] = 0;
166
+ if (!isset($options['inner_tag_' . $number]))
167
+ $options['inner_tag_' . $number] = '';
168
+
169
+ echo '<div class="rules">';
170
+ echo '<div style="float: left">Inject</div>';
171
+ echo '<select style="float: left" name="options[inner_pos_' . $number . ']">';
172
+ echo '<option value="after"';
173
+ echo $options['inner_pos_' . $number] == 'after' ? ' selected' : '';
174
+ echo '>after</option>';
175
+ echo '<option value="before"';
176
+ echo $options['inner_pos_' . $number] == 'before' ? ' selected' : '';
177
+ echo '>before</option>';
178
+ echo '</select>';
179
+ echo '<input style="float: left" type="text" placeholder="marker" name="options[inner_tag_' . $number . ']" value="';
180
+ echo esc_attr($options['inner_tag_' . $number]);
181
+ echo '">';
182
+ echo '<div style="float: left">skipping</div>';
183
+ echo '<input style="float: left" type="text" size="5" name="options[inner_skip_' . $number . ']" value="';
184
+ echo esc_attr($options['inner_skip_' . $number]);
185
+ echo '">';
186
+ echo '<div style="float: left">chars, on failure inject</div>';
187
+ echo '<select style="float: left" name="options[inner_alt_' . $number . ']">';
188
+ echo '<option value=""';
189
+ echo $options['inner_alt_' . $number] == 'after' ? ' selected' : '';
190
+ echo '>nowhere</option>';
191
+ echo '<option value="after"';
192
+ echo $options['inner_alt_' . $number] == 'after' ? ' selected' : '';
193
+ echo '>after the content</option>';
194
+ echo '<option value="before"';
195
+ echo $options['inner_alt_' . $number] == 'before' ? ' selected' : '';
196
+ echo '>before the content</option>';
197
+ echo '</select>';
198
+ echo '<div class="clearfix"></div></div>';
199
+ }
200
+
201
+ if (isset($_POST['save'])) {
202
+ if (!wp_verify_nonce($_POST['_wpnonce'], 'save'))
203
+ die('Page expired');
204
+ $options = hefo_request('options');
205
+ if (empty($options['mobile_user_agents'])) {
206
+ $options['mobile_user_agents'] = "phone\niphone\nipod\nandroid.+mobile\nxoom";
207
+ }
208
+ $agents1 = explode("\n", $options['mobile_user_agents']);
209
+ $agents2 = array();
210
+ foreach ($agents1 as &$agent) {
211
+ $agent = trim($agent);
212
+ if (empty($agent))
213
+ continue;
214
+ $agents2[] = strtolower($agent);
215
+ }
216
+ $options['mobile_user_agents_parsed'] = implode('|', $agents2);
217
+
218
+ $script_async_handles1 = explode("\n", $options['script_async_handles']);
219
+ $script_async_handles2 = array();
220
+ foreach ($script_async_handles1 as $value) {
221
+ $value = trim($value);
222
+ if (empty($value))
223
+ continue;
224
+ $script_async_handles2[] = strtolower($value);
225
+ }
226
+ $options['script_async_handles'] = $script_async_handles2;
227
+
228
+ update_option('hefo', $options);
229
+ }
230
+
231
+ else {
232
+ $options = get_option('hefo');
233
+ }
234
+ ?>
235
+ <link rel="stylesheet" href="<?php echo plugins_url('header-footer') ?>/codemirror/lib/codemirror.css">
236
+ <link rel="stylesheet" href="<?php echo plugins_url('header-footer') ?>/lib/easytabs/tabs.css">
237
+ <link rel="stylesheet" href="<?php echo plugins_url('header-footer') ?>/admin.css">
238
+
239
+ <script src="<?php echo plugins_url('header-footer') ?>/codemirror/lib/codemirror.js"></script>
240
+ <script src="<?php echo plugins_url('header-footer') ?>/codemirror/mode/xml/xml.js"></script>
241
+ <script src="<?php echo plugins_url('header-footer') ?>/codemirror/mode/css/css.js"></script>
242
+ <script src="<?php echo plugins_url('header-footer') ?>/codemirror/mode/javascript/javascript.js"></script>
243
+ <script src="<?php echo plugins_url('header-footer') ?>/codemirror/mode/htmlmixed/htmlmixed.js"></script>
244
+ <script src="<?php echo plugins_url('header-footer') ?>/codemirror/mode/clike/clike.js"></script>
245
+ <script src="<?php echo plugins_url('header-footer') ?>/codemirror/mode/php/php.js"></script>
246
+ <script src="<?php echo plugins_url('header-footer') ?>/lib/easytabs/jquery.easytabs.min.js"></script>
247
+ <script>
248
+ var hefo_cm;
249
+
250
+ var hefo_tabs;
251
+ jQuery(document).ready(function () {
252
+
253
+ jQuery("textarea.hefo-cm").each(function () {
254
+ CodeMirror.fromTextArea(this, {
255
+ lineNumbers: true,
256
+ mode: "php"
257
+ });
258
+ });
259
+
260
+ jQuery('#upload-image').click(function () {
261
+ tb_show('', 'media-upload.php?type=image&amp;TB_iframe=true');
262
+ return false;
263
+ });
264
+
265
+ window.send_to_editor = function (html) {
266
+ var imgurl = jQuery('img', html).attr('src');
267
+ jQuery('#og_image_default').val(imgurl);
268
+ tb_remove();
269
+ jQuery("#tabs").tabs();
270
+ }
271
+
272
+ jQuery("#tab-container").easytabs();
273
+ });
274
+ </script>
275
+ <div class="wrap">
276
+ <!--https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=5PHGDGNHAYLJ8-->
277
+
278
+ <h2>Header and Footer</h2>
279
+
280
+ <?php if (!isset($dismissed['rate'])) { ?>
281
+ <div class="updated"><p>
282
+ I never asked before and I'm curious: <a href="http://wordpress.org/extend/plugins/header-footer/" target="_blank"><strong>would you rate this plugin</strong></a>?
283
+ (takes only few seconds required - account on WordPress.org, every blog owner should have one...). <strong>Really appreciated, Stefano</strong>.
284
+ <a href="<?php echo wp_nonce_url($_SERVER['REQUEST_URI'] . '&dismiss=rate&noheader=1') ?>">Dismiss</a>
285
+ </p>
286
+ </div>
287
+ <?php } ?>
288
+
289
+ <div style="padding: 15px; background-color: #fff; border: 1px solid #eee; font-size: 16px; line-height: 22px">
290
+ Did this plugin save you lot of time and troubles?
291
+ <a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=5PHGDGNHAYLJ8" target="_blank"><img style="vertical-align: bottom" src="http://www.satollo.net/images/donate.png"></a>
292
+ To help children. Even <b>2$</b> helps. <a href="http://www.satollo.net/donations" target="_blank">Please read more</a>. Thank you.
293
+ <br>
294
+ Are you profitably using this free plugin for your customers? One more reason to consider a
295
+ <a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=5PHGDGNHAYLJ8" target="_blank">donation</a>. Thank you.
296
+ </div>
297
+
298
+ <p>
299
+ Other useful plugins:
300
+ <!--<a href="http://www.satollo.net/plugins/comment-plus?utm_source=header-footer&utm_medium=banner&utm_campaign=comment-plus" target="_blank">Comment Plus</a>,-->
301
+ <a href="http://www.satollo.net/plugins/hyper-cache?utm_source=header-footer&utm_medium=banner&utm_campaign=hyper-cache" target="_blank">Hyper Cache</a>,
302
+ <a href="http://www.satollo.net/plugins/include-me?utm_source=header-footer&utm_medium=banner&utm_campaign=include-me" target="_blank">Include Me</a>,
303
+ <a href="http://www.thenewsletterplugin.com/?utm_source=header-footer&utm_medium=banner&utm_campaign=newsletter" target="_blank">Newsletter</a>,
304
+ <a href="http://www.thenewsletterplugin.com/?utm_source=header-footer&utm_medium=banner&utm_campaign=php-text-widget" target="_blank">PHP Text Widget</a>.
305
+ </p>
306
+
307
+ <p>
308
+
309
+ </p>
310
+
311
+
312
+ <p><?php _e('PHP is allowed on textareas below.'); ?> <?php _e('If you use bbPress, read the official page.'); ?></p>
313
+
314
+ <form method="post" action="">
315
+ <?php wp_nonce_field('save') ?>
316
+
317
+ <div id="tab-container" class="tab-container">
318
+ <ul class="etabs">
319
+ <li class='tab'><a href="#tabs-first"><?php _e('Head and footer', 'header-footer'); ?></a></li>
320
+ <li class='tab'><a href="#tabs-generics"><?php _e('Generics', 'header-footer'); ?></a></li>
321
+ <li class='tab'><a href="#tabs-post"><?php _e('Posts', 'header-footer'); ?></a></li>
322
+ <li class='tab'><a href="#tabs-post-inner"><?php _e('Inside posts', 'header-footer'); ?></a></li>
323
+ <!--<li class='tab'><a href="#tabs-post-mobile"><?php _e('Post content (mobile)', 'header-footer'); ?></a></li>-->
324
+ <li class='tab'><a href="#tabs-page"><?php _e('Pages', 'header-footer'); ?></a></li>
325
+ <!--<li class='tab'><a href="#tabs-page-mobile"><?php _e('Page content (mobile)', 'header-footer'); ?></a></li>-->
326
+ <li class='tab'><a href="#tabs-4"><?php _e('Facebook', 'header-footer'); ?></a></li>
327
+ <li class='tab'><a href="#tabs-9"><?php _e('SEO', 'header-footer'); ?></a></li>
328
+ <li class='tab'><a href="#tabs-5"><?php _e('Snippets', 'header-footer'); ?></a></li>
329
+ <li class='tab'><a href="#tabs-6"><?php _e('BBPress', 'header-footer'); ?></a></li>
330
+ <!--
331
+ <li><a href="#tabs-6a"><?php _e('Other post types', 'header-footer'); ?></a></li>
332
+ -->
333
+ <li class='tab'><a href="#tabs-8"><?php _e('Advanced', 'header-footer'); ?></a></li>
334
+ <li class='tab'><a href="#tabs-7"><?php _e('Notes and...', 'header-footer'); ?></a></li>
335
+ <li class='tab'><a href="#tabs-thankyou"><?php _e('Thank you', 'header-footer'); ?></a></li>
336
+ </ul>
337
+
338
+ <div class="panel-container">
339
+
340
+ <div id="tabs-first">
341
+
342
+ <h3>&lt;HEAD&gt; section injection</h3>
343
+ <div class="row">
344
+
345
+ <div class="col-2">
346
+ Every page<br>
347
+ <?php hefo_base_textarea_cm('head'); ?>
348
+ </div>
349
+ <div class="col-2">
350
+ Only home page<br>
351
+ <?php hefo_base_textarea_cm('head_home'); ?>
352
+ </div>
353
+ </div>
354
+
355
+ <h3>After the &lt;BODY&gt; tag</h3>
356
+ <div class="row">
357
+
358
+ <div class="col-2">
359
+ <?php _e('Desktop', 'header-footer') ?>
360
+ <?php hefo_base_textarea_cm('body'); ?>
361
+ </div>
362
+ <div class="col-2">
363
+ <?php hefo_base_checkbox('mobile_body_enabled', __('Mobile', 'header-footer')); ?><br>
364
+ <?php hefo_base_textarea_cm('mobile_body'); ?>
365
+ </div>
366
+
367
+ </div>
368
+ <h3>Before the &lt;/BODY&gt; closing tag (footer)</h3>
369
+ <div class="row">
370
+ <div class="col-2">
371
+ <?php _e('Desktop', 'header-footer') ?>
372
+ <?php hefo_base_textarea_cm('footer'); ?>
373
+ </div>
374
+ <div class="col-2">
375
+ <?php hefo_base_checkbox('mobile_footer_enabled', __('Mobile', 'header-footer')); ?><br>
376
+ <?php hefo_base_textarea_cm('mobile_footer'); ?>
377
+ </div>
378
+ </div>
379
+
380
+ <div class="clearfix"></div>
381
+
382
+ </div>
383
+
384
+ <div id="tabs-generics">
385
+
386
+ <?php for ($i = 1; $i < 5; $i++) { ?>
387
+ <h3>Generic injection <?php echo $i; ?></h3>
388
+ <p>Inject before the <?php hefo_base_text('generic_tag_' . $i); ?> marker</p>
389
+ <div class="row">
390
+ <div class="col-2">
391
+ Desktop (and mobile if no alternative specified)<br>
392
+ <?php hefo_base_textarea_cm('generic_' . $i); ?>
393
+ </div>
394
+ <div class="col-2">
395
+ <?php hefo_base_checkbox('mobile_generic_enabled_' . $i, __('Mobile', 'header-footer')); ?><br>
396
+ <?php hefo_base_textarea_cm('mobile_generic_' . $i); ?>
397
+ </div>
398
+ </div>
399
+ <div class="clearfix"></div>
400
+ <?php } ?>
401
+ <div class="clearfix"></div>
402
+ </div>
403
+
404
+
405
+
406
+ <div id="tabs-post">
407
+ <p>
408
+ Please take the time to <a href="http://www.satollo.net/plugins/header-footer" target="_blank">read this page</a> to understand how the "mobile" configuration works.
409
+ See the "advanced tab" to configure the mobile device detection.
410
+ </p>
411
+
412
+ <h3>Before the post content</h3>
413
+ <div class="row">
414
+
415
+ <div class="col-2">
416
+ Desktop<br>
417
+ <?php hefo_base_textarea_cm('before'); ?>
418
+ </div>
419
+ <div class="col-2">
420
+ <?php hefo_base_checkbox('mobile_before_enabled', __('Mobile', 'header-footer')); ?><br>
421
+ <?php hefo_base_textarea_cm('mobile_before'); ?>
422
+ </div>
423
+ </div>
424
+
425
+ <div class="clearfix"></div>
426
+
427
+ <h3>After the post content</h3>
428
+ <div class="row">
429
+
430
+ <div class="col-2">
431
+ Desktop<br>
432
+ <?php hefo_base_textarea_cm('after'); ?>
433
+ </div>
434
+ <div class="col-2">
435
+ <?php hefo_base_checkbox('mobile_after_enabled', __('Mobile', 'header-footer')); ?><br>
436
+ <?php hefo_base_textarea_cm('mobile_after'); ?>
437
+ </div>
438
+ </div>
439
+
440
+ <!--<h3>Posts and pages</h3>-->
441
+ <table class="form-table">
442
+ <!--<tr valign="top"><?php hefo_field_checkbox('category', __('Enable injection on category pages', 'header-footer')); ?></tr>-->
443
+ <tr valign="top"><?php //hefo_field_textarea('before', __('Code to be inserted before each post', 'header-footer'), '', 'rows="10"'); ?></tr>
444
+ <tr valign="top"><?php //hefo_field_textarea('after', __('Code to be inserted after each post', 'header-footer'), '', 'rows="10"'); ?></tr>
445
+ </table>
446
+
447
+ <h3><?php _e('Injection on excerpts', 'header-footer'); ?></h3>
448
+ <p><?php _e('It works only on category and tag pages.', 'header-footer'); ?></p>
449
+ <table class="form-table">
450
+ <tr valign="top"><?php hefo_field_textarea('excerpt_before', __('Code to be inserted before each post excerpt', 'header-footer'), '', 'rows="10"'); ?></tr>
451
+ <tr valign="top"><?php hefo_field_textarea('excerpt_after', __('Code to be inserted after each post excerpt', 'header-footer'), '', 'rows="10"'); ?></tr>
452
+ </table>
453
+ <div class="clearfix"></div>
454
+ </div>
455
+
456
+
457
+ <div id="tabs-post-inner">
458
+
459
+ <?php for ($i = 1; $i < 4; $i++) { ?>
460
+ <h3>Inner post injection <?php echo $i; ?></h3>
461
+ <?php hefo_rule($i); ?>
462
+ <div class="row">
463
+ <div class="col-2">
464
+ Desktop (and mobile if no alternative specified)<br>
465
+ <?php hefo_base_textarea_cm('inner_' . $i); ?>
466
+ </div>
467
+ <div class="col-2">
468
+ <?php hefo_base_checkbox('mobile_inner_enabled_' . $i, __('Mobile', 'header-footer')); ?><br>
469
+ <?php hefo_base_textarea_cm('mobile_inner_' . $i); ?>
470
+ </div>
471
+ </div>
472
+ <div class="clearfix"></div>
473
+ <?php } ?>
474
+ </div>
475
+
476
+
477
+ <div id="tabs-page">
478
+
479
+ <?php hefo_base_checkbox('page_use_post', __('Use the post configurations', 'header-footer')); ?><br>
480
+ <?php hefo_base_checkbox('page_add_tags', __('Let pages to have tags', 'header-footer')); ?><br>
481
+ <?php hefo_base_checkbox('page_add_categories', __('Let pages to have categories', 'header-footer')); ?>
482
+
483
+ <h3>Before the page content</h3>
484
+ <div class="row">
485
+
486
+ <div class="col-2">
487
+ Desktop (and mobile if no alternative specified)<br>
488
+ <?php hefo_base_textarea_cm('page_before'); ?>
489
+ </div>
490
+ <div class="col-2">
491
+ <?php hefo_base_checkbox('mobile_page_before_enabled', __('Mobile', 'header-footer')); ?><br>
492
+ <?php hefo_base_textarea_cm('mobile_page_before'); ?>
493
+ </div>
494
+ </div>
495
+
496
+ <div class="clearfix"></div>
497
+
498
+ <h3>After the page content</h3>
499
+ <div class="row">
500
+
501
+ <div class="col-2">
502
+ Desktop<br>
503
+ <?php hefo_base_textarea_cm('page_after'); ?>
504
+ </div>
505
+ <div class="col-2">
506
+ <?php hefo_base_checkbox('mobile_page_after_enabled', __('Mobile', 'header-footer')); ?><br>
507
+ <?php hefo_base_textarea_cm('mobile_page_after'); ?>
508
+ </div>
509
+ </div>
510
+
511
+ <div class="clearfix"></div>
512
+
513
+ </div>
514
+
515
+
516
+ <div id="tabs-4">
517
+
518
+ <p>
519
+ <?php _e('If you use WordPress SEO or other plugin which already add the OpenGraph meta tag, leave these options disabled.') ?>
520
+ </p>
521
+ <table class="form-table">
522
+ <tr valign="top"><?php hefo_field_checkbox('og_enabled', __('Enable the OG metatag', 'header-footer'), __('Enable the Facebook Open Graph metatag', 'header-footer')); ?></tr>
523
+
524
+ <tr valign="top"><?php hefo_field_text('fb_app_id', __('Facebook application id', 'header-footer'), __('', 'header-footer')); ?></tr>
525
+ <tr valign="top"><?php hefo_field_text('og_type', __('Facebook page type for the generic web page', 'header-footer'), __('Usually "article" is the right choice, if empty will be skipped', 'header-footer')); ?></tr>
526
+ <tr valign="top"><?php hefo_field_text('og_type_home', __('Facebook page type for the home', 'header-footer'), __('Usually "blog" is a good choice, if empty will be used the generic type', 'header-footer')); ?></tr>
527
+ <tr valign="top"><?php hefo_field_checkbox('og_image', __('Facebook Open Graph Image', 'header-footer'), __('Adds the Facebook Open Graph metatag with a reference to the first post image', 'header-footer')); ?></tr>
528
+ <tr valign="top">
529
+ <th scope="row">
530
+ <label for="options[' . $name . ']"><?php _e('Facebook Open Graph default image'); ?></label>
531
+ </th>
532
+ <td>
533
+ <input type="text" id="og_image_default" name="options[og_image_default]" value="<?php echo htmlspecialchars($options['og_image_default']); ?>" size="50"/>
534
+ <input type="button" id="upload-image" value="Select/Upload an image"/>
535
+ <br />
536
+ <?php _e('If no image can be extracted from a post, that image URL will be used (if present).'); ?><br />
537
+ <?php _e('<strong>Warning.</strong> On some versions of WordPress after the image selection button is pressed the tabs above does not change anymore. Just save so
538
+ this page is reloaded (<a href="http://wordpress.org/support/topic/wp-32-thickbox-jquery-ui-tabs-conflict" target="_blank">reference</a>).'); ?>
539
+ </td>
540
+ </tr>
541
+ </table>
542
+ <div class="clearfix"></div>
543
+ </div>
544
+
545
+
546
+ <div id="tabs-9">
547
+ <p>
548
+ <?php _e('Please, see the <a href="http://www.satollo.net/plugins/header-footer" target="_blank">Header and Footer</strong></a> page before to use those options.'); ?>
549
+ </p>
550
+ <p>
551
+ <?php _e('Note: most of these options are now available on SEO plugins.'); ?>
552
+ </p>
553
+
554
+ <!--<h3>SEO</h3>-->
555
+ <table class="form-table">
556
+ <tr valign="top">
557
+ <th scope="row">
558
+ Home
559
+ </th>
560
+ <?php hefo_field_checkbox_only('seo_home_paged_noindex', __('Add noindex for page 2 and up', 'header-footer')); ?>
561
+ </tr>
562
+ <tr valign="top">
563
+ <th scope="row">
564
+ Search results
565
+ </th>
566
+ <?php hefo_field_checkbox_only('seo_search_noindex', __('Add noindex for search result pages', 'header-footer')); ?>
567
+ </tr>
568
+ <tr valign="top">
569
+ <th scope="row">
570
+ Canonical on home
571
+ </th>
572
+ <?php hefo_field_checkbox_only('seo_home_canonical', __('Add canonical to home page', 'header-footer')); ?>
573
+ </tr>
574
+ </table>
575
+ <div class="clearfix"></div>
576
+ </div>
577
+
578
+
579
+ <div id="tabs-5">
580
+ <p>
581
+ <?php _e('Common snippets that can be used in any header or footer area referring them as [snippet_N] where N is the snippet number
582
+ from 1 to 5. Snippets are inserted before PHP evaluation.', 'header-footer'); ?><br />
583
+ <?php _e('Useful for social button to be placed before and after the post or in posts and pages.', 'header-footer'); ?>
584
+ </p>
585
+ <table class="form-table">
586
+ <? for ($i=1; $i<=5; $i++) { ?>
587
+ <tr valign="top"><?php hefo_field_textarea('snippet_' . $i, __('Snippet ' . $i, 'header-footer'), '', 'rows="10"'); ?></tr>
588
+ <? } ?>
589
+ </table>
590
+ <div class="clearfix"></div>
591
+ </div>
592
+
593
+ <div id="tabs-6">
594
+ <p>
595
+ Injection points on bbPress default theme structure are not always clear to me, so consider this feature experimental.
596
+ </p>
597
+ <h3>Before a single forum</h3>
598
+ <div class="row">
599
+ <div class="col-2">
600
+ Desktop<br>
601
+ <?php hefo_base_textarea_cm('bbp_template_before_single_forum'); ?>
602
+ </div>
603
+ <div class="col-2">
604
+ <?php hefo_base_checkbox('mobile_bbp_template_before_single_forum_enabled', __('Mobile', 'header-footer')); ?><br>
605
+ <?php hefo_base_textarea_cm('mobile_bbp_template_before_single_forum'); ?>
606
+ </div>
607
+ </div>
608
+
609
+ <h3>Before a single topic</h3>
610
+ <div class="row">
611
+ <div class="col-2">
612
+ Desktop<br>
613
+ <?php hefo_base_textarea_cm('bbp_template_before_single_topic'); ?>
614
+ </div>
615
+ <div class="col-2">
616
+ <?php hefo_base_checkbox('mobile_bbp_template_before_single_topic_enabled', __('Mobile', 'header-footer')); ?><br>
617
+ <?php hefo_base_textarea_cm('mobile_bbp_template_before_single_topic'); ?>
618
+ </div>
619
+ </div>
620
+
621
+ <h3>After a single topic</h3>
622
+ <div class="row">
623
+ <div class="col-2">
624
+ Desktop<br>
625
+ <?php hefo_base_textarea_cm('bbp_template_after_single_topic'); ?>
626
+ </div>
627
+ <div class="col-2">
628
+ <?php hefo_base_checkbox('mobile_bbp_template_after_single_topic_enabled', __('Mobile', 'header-footer')); ?><br>
629
+ <?php hefo_base_textarea_cm('mobile_bbp_template_after_single_topic'); ?>
630
+ </div>
631
+ </div>
632
+
633
+ <h3>Before a single reply</h3>
634
+ <div class="row">
635
+ <div class="col-2">
636
+ Desktop<br>
637
+ <?php hefo_base_textarea_cm('bbp_theme_before_reply_content'); ?>
638
+ </div>
639
+ <div class="col-2">
640
+ <?php hefo_base_checkbox('mobile_bbp_theme_before_reply_content_enabled', __('Mobile', 'header-footer')); ?><br>
641
+ <?php hefo_base_textarea_cm('mobile_bbp_theme_before_reply_content'); ?>
642
+ </div>
643
+ </div>
644
+
645
+ <h3>After a single reply</h3>
646
+ <div class="row">
647
+ <div class="col-2">
648
+ Desktop<br>
649
+ <?php hefo_base_textarea_cm('bbp_theme_after_reply_content'); ?>
650
+ </div>
651
+ <div class="col-2">
652
+ <?php hefo_base_checkbox('mobile_bbp_theme_after_reply_content_enabled', __('Mobile', 'header-footer')); ?><br>
653
+ <?php hefo_base_textarea_cm('mobile_bbp_theme_after_reply_content'); ?>
654
+ </div>
655
+ </div>
656
+ <div class="clearfix"></div>
657
+ </div>
658
+ <!--
659
+ <div id="tabs-6s">
660
+ <p>
661
+ </p>
662
+ <?php $post_types = get_post_types(array('public' => true, '_builtin' => false), 'objects'); ?>
663
+ <?php foreach ($post_types as $post_type) { ?>
664
+ <h3><?php echo esc_html($post_type->label) ?> (<?php echo esc_html($post_type->name) ?>)</h3>
665
+ <table class="form-table">
666
+ <tr><?php hefo_field_textarea($post_type->name . '_before', __('Before the content', 'header-footer'), '', 'rows="10"'); ?></tr>
667
+ <tr><?php hefo_field_textarea($post_type->name . '_after', __('After the content', 'header-footer'), '', 'rows="10"'); ?></tr>
668
+ </table>
669
+ <?php } ?>
670
+ </div>
671
+ -->
672
+
673
+ <div id="tabs-8">
674
+ <table class="form-table">
675
+ <tr valign="top">
676
+ <?php
677
+ hefo_field_textarea('mobile_user_agents', __('Mobile user agent strings', 'header-footer'), 'For coders: a regular expression is built with those values and the resulting code will be<br>'
678
+ . '<code>preg_match(\'/' . $options['mobile_user_agents_parsed'] . '/\', ...);</code><br>' .
679
+ '<a href="http://www.satollo.net/plugins/header-footer" target="_blank">Read this page</a> for more.', 'rows="10"');
680
+ ?>
681
+
682
+ </tr>
683
+ </table>
684
+
685
+ <h3>Web performance</h3>
686
+ <p>
687
+ Some JavaScript can be marked to be loaded asynchronously, for example the comment-reply.js of WordPress.
688
+ Not always asynchronous load work, for example jQuery cannot usually loaded in this way. Since WordPress does
689
+ not support this feature natively, here you can force thise feature on specific scripts.<br>
690
+ Usually you can add comment-reply, akismet-form, admin-bar.<br>
691
+ You can read more on <a href="http://www.satollo.net/javascript-asyn-load-for-wordpress-enqueued-scripts" target="_blank">this article</a>
692
+ and/or ask on my <a href="http://www.satollo.net/forums" target="_blank">forum area</a>.
693
+ </p>
694
+
695
+ <table class="form-table">
696
+ <tr valign="top">
697
+ <th scope="row">
698
+ Script handle debug
699
+ </th>
700
+ <?php hefo_field_checkbox_only('script_handle_debug', __('Activate in page debug info: see the source page to find the handles', 'header-footer')); ?>
701
+
702
+ </tr>
703
+ <tr valign="top">
704
+ <?php
705
+ hefo_field_textarea('script_async_handles', __('Script handles to load asynchronously', 'header-footer'), 'One per line', 'rows="10"');
706
+ ?>
707
+ </tr>
708
+ </table>
709
+
710
+ <h3>Head meta links</h3>
711
+ <p>
712
+ WordPress automatically add some meta link on the head of the page, for example the RSS links, the previous and next
713
+ post links and so on. Here you can disable those links if not of interest.
714
+ </p>
715
+ <table class="form-table">
716
+ <tr valign="top">
717
+ <th scope="row">Disable css link id</th>
718
+ <?php hefo_field_checkbox_only('disable_css_id', __('Disable the id attribute on css links generated by WordPress', 'header-footer'), '', 'http://www.satollo.net/plugins/header-footer#disable_css_id'); ?>
719
+ </tr>
720
+ <tr valign="top">
721
+ <th scope="row">Disable css media</th>
722
+ <?php hefo_field_checkbox_only('disable_css_media', __('Disable the media attribute on css links generated by WordPress, id the option above is enabled.', 'header-footer'), '', 'http://www.satollo.net/plugins/header-footer#disable_css_media'); ?>
723
+ </tr>
724
+ <tr valign="top">
725
+ <th scope="row">Extra feed links</th>
726
+ <?php hefo_field_checkbox_only('disable_feed_links_extra', __('Disable extra feed links like category feeds or single post comments feeds', 'header-footer')); ?>
727
+ </tr>
728
+ <tr valign="top">
729
+ <th scope="row">Short link</th>
730
+ <?php hefo_field_checkbox_only('disable_wp_shortlink_wp_head', __('Disable the short link for posts', 'header-footer')); ?>
731
+ </tr>
732
+ <tr valign="top">
733
+ <th scope="row">WLW Manifest</th>
734
+ <?php hefo_field_checkbox_only('disable_wlwmanifest_link', __('Disable the Windows Live Writer manifest', 'header-footer')); ?>
735
+ </tr>
736
+ <tr valign="top">
737
+ <th scope="row">RSD link</th>
738
+ <?php hefo_field_checkbox_only('disable_rsd_link', __('Disable RSD link', 'header-footer')); ?>
739
+ </tr>
740
+ <tr valign="top">
741
+ <th scope="row">Adjacent post links</th>
742
+ <?php hefo_field_checkbox_only('disable_adjacent_posts_rel_link_wp_head', __('Disable adjacent post links', 'header-footer')); ?>
743
+ </tr>
744
+ </table>
745
+ <div class="clearfix"></div>
746
+ </div>
747
+
748
+
749
+ <div id="tabs-7">
750
+ <table class="form-table">
751
+ <tr valign="top"><?php hefo_field_textarea('notes', __('Notes and parked codes', 'header-footer'), '', 'rows="10"'); ?></tr>
752
+ </table>
753
+ <div class="clearfix"></div>
754
+ </div>
755
+
756
+ <div id="tabs-thankyou">
757
+
758
+ <ul>
759
+ <li><a href="https://plus.google.com/u/0/118278852301653300773">Евгений Жуков (Eugene Zhukov)</a> - Russian translation</li>
760
+ </ul>
761
+ <div class="clearfix"></div>
762
+ </div>
763
+ </div>
764
+
765
+ </div>
766
+ <p class="submit"><input type="submit" class="button" name="save" value="<?php _e('save', 'header-footer'); ?>"></p>
767
+
768
+ </form>
769
+ </div>
770
+
771
+
plugin.php ADDED
@@ -0,0 +1,564 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ Plugin Name: Header and Footer
5
+ Plugin URI: http://www.satollo.net/plugins/header-footer
6
+ Description: Header and Footer lets to add html/javascript code to the head and footer and posts of your blog. Some examples are provided on the <a href="http://www.satollo.net/plugins/header-footer">official page</a>.
7
+ Version: 2.0.3
8
+ Author: Stefano Lissa
9
+ Author URI: http://www.satollo.net
10
+ Disclaimer: Use at your own risk. No warranty expressed or implied is provided.
11
+ */
12
+
13
+ /*
14
+ Copyright 2008-2015 Stefano Lissa (stefano@satollo.net)
15
+
16
+ This program is free software; you can redistribute it and/or modify
17
+ it under the terms of the GNU General Public License as published by
18
+ the Free Software Foundation; either version 2 of the License, or
19
+ (at your option) any later version.
20
+
21
+ This program is distributed in the hope that it will be useful,
22
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
23
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24
+ GNU General Public License for more details.
25
+
26
+ You should have received a copy of the GNU General Public License
27
+ along with this program; if not, write to the Free Software
28
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29
+ */
30
+
31
+ $hefo_options = get_option('hefo');
32
+
33
+ if (isset($hefo_options['mobile_post'])) {
34
+ $hefo_options['mobile_before_enabled'] = 1;
35
+ $hefo_options['mobile_after_enabled'] = 1;
36
+ unset($hefo_options['mobile_post']);
37
+ update_option('hefo', $hefo_options);
38
+ }
39
+ if (isset($hefo_options['mobile_page'])) {
40
+ $hefo_options['mobile_page_before_enabled'] = 1;
41
+ $hefo_options['mobile_page_after_enabled'] = 1;
42
+ unset($hefo_options['mobile_page']);
43
+ update_option('hefo', $hefo_options);
44
+ }
45
+
46
+
47
+ $hefo_is_mobile = false;
48
+ if (defined('IS_PHONE') && IS_PHONE) {
49
+ $hefo_is_mobile = true;
50
+ } else if (isset($_SERVER['HTTP_USER_AGENT']) && isset($hefo_options['mobile_user_agents_parsed'])) {
51
+ $hefo_is_mobile = preg_match('/' . $hefo_options['mobile_user_agents_parsed'] . '/', strtolower($_SERVER['HTTP_USER_AGENT']));
52
+ }
53
+
54
+ if (is_admin()) {
55
+ include dirname(__FILE__) . '/admin.php';
56
+ }
57
+
58
+ if (isset($hefo_options['disable_css_id'])) {
59
+
60
+ function hefo_style_loader_tag($link) {
61
+ global $hefo_options;
62
+ $link = preg_replace("/id='.*?-css'/", "", $link);
63
+ if (isset($hefo_options['disable_css_media'])) {
64
+ if (!preg_match("/media='print'/", $link)) {
65
+ $link = preg_replace("/media='.*?'/", "", $link);
66
+ }
67
+ }
68
+ return $link;
69
+ }
70
+
71
+ add_filter('style_loader_tag', 'hefo_style_loader_tag');
72
+ }
73
+
74
+ add_action('template_redirect', 'hefo_template_redirect', 99);
75
+
76
+ $hefo_body_block = '';
77
+ $hefo_generic_block = array();
78
+
79
+ function hefo_template_redirect() {
80
+ global $hefo_body_block, $hefo_generic_block, $hefo_options, $hefo_is_mobile;
81
+
82
+ if ($hefo_is_mobile && $hefo_options['mobile_body_enabled']) {
83
+ $hefo_body_block = hefo_execute($hefo_options['mobile_body']);
84
+ } else {
85
+ $hefo_body_block = hefo_execute($hefo_options['body']);
86
+ }
87
+ for ($i = 1; $i < 4; $i++) {
88
+ if ($hefo_is_mobile && isset($hefo_options['mobile_generic_enabled_' . $i])) {
89
+ if (isset($hefo_options['mobile_generic_' . $i]))
90
+ $hefo_generic_block[$i] = hefo_execute($hefo_options['mobile_generic_' . $i]);
91
+ } else {
92
+ if (isset($hefo_options['generic_' . $i]))
93
+ $hefo_generic_block[$i] = hefo_execute($hefo_options['generic_' . $i]);
94
+ }
95
+ }
96
+ ob_start('hefo_callback');
97
+ }
98
+
99
+ function hefo_callback($buffer) {
100
+ global $hefo_body_block, $hefo_generic_block, $hefo_options, $hefo_is_mobile;
101
+
102
+ for ($i = 1; $i < 4; $i++) {
103
+ if (isset($hefo_options['generic_tag_'. $i]))
104
+ hefo_insert_before($buffer, $hefo_generic_block[$i], $hefo_options['generic_tag_'. $i]);
105
+ }
106
+ $x = strpos($buffer, '<body');
107
+ if ($x === false) {
108
+ return $buffer;
109
+ }
110
+ $x = strpos($buffer, '>', $x);
111
+ if ($x === false) {
112
+ return $buffer;
113
+ }
114
+ $x++;
115
+ return substr($buffer, 0, $x) . "\n" . $hefo_body_block . substr($buffer, $x);
116
+ }
117
+
118
+
119
+
120
+ add_filter('script_loader_tag', 'hefo_script_loader_tag', 90, 3);
121
+
122
+ function hefo_script_loader_tag($tag, $handle, $src) {
123
+ global $hefo_options;
124
+ if (isset($hefo_options['script_handle_debug'])) {
125
+ echo "<!-- script handle: $handle -->\n";
126
+ }
127
+ if (isset($hefo_options['script_async_handles']) && is_array($hefo_options['script_async_handles'])) {
128
+ if (array_search($handle, $hefo_options['script_async_handles']) !== false) {
129
+ $tag = str_replace('<script', '<script async', $tag);
130
+ }
131
+ }
132
+ return $tag;
133
+ }
134
+
135
+ add_action('wp_head', 'hefo_wp_head_pre', 1);
136
+
137
+ function hefo_wp_head_pre() {
138
+ global $hefo_options, $wp_query;
139
+
140
+ // global $wp_scripts;
141
+ // if ($wp_scripts instanceof WP_Scripts) {
142
+ // $wp_scripts->add_data('jquery', 'group', 1);
143
+ // $wp_scripts->add_data('jquery-migrate', 'group', 1);
144
+ // }
145
+ // remove_action('wp_head', 'wp_generator');
146
+ // remove_action('wp_head', 'wlwmanifest_link');
147
+ // remove_action('wp_head', 'rsd_link');
148
+ // remove_action('wp_head', 'feed_links', 2);
149
+ // remove_action('wp_head', 'feed_links_extra', 3);
150
+ // remove_action('wp_head', 'adjacent_posts_rel_link_wp_head', 10, 0);
151
+ // remove_action('wp_head', 'wp_shortlink_wp_head', 10, 0);
152
+
153
+ if (isset($hefo_options['disable_wlwmanifest_link'])) {
154
+ remove_action('wp_head', 'wlwmanifest_link');
155
+ }
156
+
157
+ if (isset($hefo_options['disable_rsd_link'])) {
158
+ remove_action('wp_head', 'rsd_link');
159
+ }
160
+
161
+ if (isset($hefo_options['disable_feed_links_extra'])) {
162
+ remove_action('wp_head', 'feed_links_extra', 3);
163
+ }
164
+
165
+ if (isset($hefo_options['disable_wp_shortlink_wp_head'])) {
166
+ remove_action('wp_head', 'wp_shortlink_wp_head', 10, 0);
167
+ }
168
+
169
+ if (isset($hefo_options['disable_wp_shortlink_wp_head'])) {
170
+ remove_action('wp_head', 'wp_shortlink_wp_head', 10, 0);
171
+ }
172
+
173
+ if (is_home() && is_paged() && isset($hefo_options['seo_home_paged_noindex'])) {
174
+ echo '<meta name="robots" content="noindex">';
175
+ }
176
+
177
+ if (is_home() && !is_paged() && isset($hefo_options['seo_home_canonical'])) {
178
+ echo '<meta name="canonical" content="' . get_option('home') . '">';
179
+ }
180
+
181
+ if (is_archive() && is_paged() && isset($hefo_options['seo_archives_paged_noindex'])) {
182
+ echo '<meta name="robots" content="noindex">';
183
+ }
184
+
185
+ if (is_search() && isset($hefo_options['seo_search_noindex'])) {
186
+ echo '<meta name="robots" content="noindex">';
187
+ }
188
+
189
+ if (isset($hefo_options['og_enabled'])) {
190
+ if (is_home()) {
191
+ if (empty($hefo_options['og_type_home']))
192
+ $hefo_options['og_type_home'] = $hefo_options['og_type'];
193
+ if (!empty($hefo_options['og_type_home']))
194
+ echo '<meta property="og:type" content="' . $hefo_options['og_type_home'] . '" />';
195
+ }
196
+ else {
197
+ if (!empty($hefo_options['og_type']))
198
+ echo '<meta property="og:type" content="' . $hefo_options['og_type'] . '" />';
199
+ }
200
+
201
+ if (!empty($hefo_options['fb_app_id'])) {
202
+ echo '<meta property="fb:app_id" content="' . $hefo_options['fb_app_id'] . '" />';
203
+ }
204
+
205
+ if (!empty($hefo_options['fb_admins'])) {
206
+ echo '<meta property="fb:admins" content="' . $hefo_options['fb_admins'] . '" />';
207
+ }
208
+
209
+ // Add it as higer as possible, Facebook reads only the first part of a page
210
+ if (isset($hefo_options['og_image'])) {
211
+ if (is_single() || is_page()) {
212
+ $xid = $wp_query->get_queried_object_id();
213
+ if (function_exists('bbp_get_topic_forum_id')) {
214
+ $object = $wp_query->get_queried_object();
215
+ if ($object != null && $object->post_type == 'topic') {
216
+ $xid = bbp_get_topic_forum_id($xid);
217
+ }
218
+ }
219
+ $xtid = function_exists('get_post_thumbnail_id') ? get_post_thumbnail_id($xid) : false;
220
+ if ($xtid) {
221
+ $ximage = wp_get_attachment_url($xtid);
222
+ echo '<meta property="og:image" content="' . $ximage . '" />';
223
+ } else {
224
+ $xattachments = get_children(array('post_parent' => $xid, 'post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => 'image', 'order' => 'ASC', 'orderby' => 'menu_order'));
225
+ if (!empty($xattachments)) {
226
+ foreach ($xattachments as $id => $attachment) {
227
+ $ximage = wp_get_attachment_url($id);
228
+ echo '<meta property="og:image" content="' . $ximage . '" />';
229
+ break;
230
+ }
231
+ } else {
232
+ if (!empty($hefo_options['og_image_default'])) {
233
+ echo '<meta property="og:image" content="' . $hefo_options['og_image_default'] . '" />';
234
+ }
235
+ }
236
+ }
237
+ } else {
238
+ if (!empty($hefo_options['og_image_default'])) {
239
+ echo '<meta property="og:image" content="' . $hefo_options['og_image_default'] . '" />';
240
+ }
241
+ }
242
+ }
243
+ }
244
+ }
245
+
246
+ add_action('wp_head', 'hefo_wp_head_post', 11);
247
+
248
+ function hefo_wp_head_post() {
249
+ global $hefo_options, $wp_query, $wpdb;
250
+ $buffer = '';
251
+ if (is_home()) {
252
+ $buffer .= hefo_replace($hefo_options['head_home']);
253
+ }
254
+
255
+ $buffer .= hefo_replace($hefo_options['head']);
256
+
257
+ ob_start();
258
+ eval('?>' . $buffer);
259
+ ob_end_flush();
260
+ }
261
+
262
+ add_action('wp_footer', 'hefo_wp_footer');
263
+
264
+ function hefo_wp_footer() {
265
+ global $hefo_options, $hefo_is_mobile;
266
+
267
+ if ($hefo_is_mobile && isset($hefo_options['mobile_footer_enabled'])) {
268
+ $buffer = $hefo_options['mobile_footer'];
269
+ } else {
270
+ $buffer = $hefo_options['footer'];
271
+ }
272
+
273
+ $buffer = hefo_replace($buffer);
274
+
275
+ ob_start();
276
+ eval('?>' . $buffer);
277
+ ob_end_flush();
278
+ }
279
+
280
+ // BBPRESS
281
+ $bbp_reply_count = 0;
282
+
283
+ add_action('bbp_theme_before_reply_content', 'hefo_bbp_theme_before_reply_content');
284
+
285
+ function hefo_bbp_theme_before_reply_content() {
286
+ global $hefo_options, $hefo_is_mobile, $wpdb, $post, $bbp_reply_count;
287
+ $bbp_reply_count++;
288
+ if ($hefo_is_mobile && isset($hefo_options['mobile_bbp_theme_before_reply_content_enabled'])) {
289
+ echo hefo_execute(hefo_replace($hefo_options['mobile_bbp_theme_before_reply_content']));
290
+ } else {
291
+ echo hefo_execute(hefo_replace($hefo_options['bbp_theme_before_reply_content']));
292
+ }
293
+ }
294
+
295
+ add_action('bbp_theme_after_reply_content', 'hefo_bbp_theme_after_reply_content');
296
+
297
+ function hefo_bbp_theme_after_reply_content() {
298
+ global $hefo_options, $hefo_is_mobile, $wpdb, $post, $bbp_reply_count;
299
+
300
+ if ($hefo_is_mobile && isset($hefo_options['mobile_bbp_theme_after_reply_content_enabled'])) {
301
+ echo hefo_execute(hefo_replace($hefo_options['mobile_bbp_theme_after_reply_content']));
302
+ } else {
303
+ echo hefo_execute(hefo_replace($hefo_options['bbp_theme_after_reply_content']));
304
+ }
305
+ }
306
+
307
+ add_action('bbp_template_before_single_forum', 'hefo_bbp_template_before_single_forum');
308
+
309
+ function hefo_bbp_template_before_single_forum() {
310
+ global $hefo_options, $hefo_is_mobile, $wpdb, $post;
311
+
312
+ if ($hefo_is_mobile && isset($hefo_options['mobile_bbp_template_before_single_forum_enabled'])) {
313
+ echo hefo_execute(hefo_replace($hefo_options['mobile_bbp_template_before_single_forum']));
314
+ } else {
315
+ echo hefo_execute(hefo_replace($hefo_options['bbp_template_before_single_forum']));
316
+ }
317
+ }
318
+
319
+ add_action('bbp_template_before_single_topic', 'hefo_bbp_template_before_single_topic');
320
+
321
+ function hefo_bbp_template_before_single_topic() {
322
+ global $hefo_options, $hefo_is_mobile, $wpdb, $post;
323
+
324
+ if ($hefo_is_mobile && isset($hefo_options['mobile_bbp_template_after_before_topic_enabled'])) {
325
+ echo hefo_execute(hefo_replace($hefo_options['mobile_bbp_template_before_single_topic']));
326
+ } else {
327
+ echo hefo_execute(hefo_replace($hefo_options['bbp_template_after_before_topic']));
328
+ }
329
+ }
330
+
331
+ add_action('bbp_template_after_single_topic', 'hefo_bbp_template_after_single_topic');
332
+
333
+ function hefo_bbp_template_after_single_topic() {
334
+ global $hefo_options, $hefo_is_mobile, $wpdb, $post;
335
+
336
+ if ($hefo_is_mobile && isset($hefo_options['mobile_bbp_template_after_single_topic_enabled'])) {
337
+ echo hefo_execute(hefo_replace($hefo_options['mobile_bbp_template_after_single_topic']));
338
+ } else {
339
+ echo hefo_execute(hefo_replace($hefo_options['bbp_template_after_single_topic']));
340
+ }
341
+ }
342
+
343
+ add_action('the_content', 'hefo_the_content');
344
+
345
+ global $hefo_page_top, $hefo_page_bottom, $hefo_post_top, $hefo_post_bottom;
346
+ $hefo_page_top = true;
347
+ $hefo_page_bottom = true;
348
+ $hefo_post_top = true;
349
+ $hefo_post_bottom = true;
350
+
351
+ function hefo_the_content($content) {
352
+ global $hefo_options, $wpdb, $post, $hefo_page_top, $hefo_page_bottom, $hefo_post_top, $hefo_post_bottom, $hefo_is_mobile;
353
+
354
+ $before = '';
355
+ $after = '';
356
+ //if (is_singular() || ($hefo_options['category'] && (is_category() || is_tag()))) {
357
+ if (is_singular()) {
358
+ //if (!empty($hefo_options[$post->post_type . '_before'])) {
359
+ // echo $hefo_options[$post->post_type . '_before'];
360
+ //}
361
+ if (is_page() && !isset($hefo_options['page_use_post'])) {
362
+ if ($hefo_page_top) {
363
+ $value = get_post_meta($post->ID, 'hefo_before', true);
364
+ if ($value != '1') {
365
+ if (isset($hefo_options['mobile_page_before_enabled']) && $hefo_is_mobile) {
366
+ $before = hefo_execute(hefo_replace($hefo_options['mobile_page_before']));
367
+ } else {
368
+ $before = hefo_execute(hefo_replace($hefo_options['page_before']));
369
+ }
370
+ }
371
+ }
372
+ if ($hefo_page_bottom) {
373
+ $value = get_post_meta($post->ID, 'hefo_after', true);
374
+ if ($value != '1') {
375
+ if (isset($hefo_options['mobile_page_after_enabled']) && $hefo_is_mobile) {
376
+ $after = hefo_execute(hefo_replace($hefo_options['mobile_page_after']));
377
+ } else {
378
+ $after = hefo_execute(hefo_replace($hefo_options['page_after']));
379
+ }
380
+ }
381
+ }
382
+ } else {
383
+ if ($hefo_post_top) {
384
+ $value = get_post_meta($post->ID, 'hefo_before', true);
385
+ if ($value != '1') {
386
+ if (isset($hefo_options['mobile_before_enabled']) && $hefo_is_mobile) {
387
+ $before = hefo_execute(hefo_replace($hefo_options['mobile_before']));
388
+ } else {
389
+ $before = hefo_execute(hefo_replace($hefo_options['before']));
390
+ }
391
+ }
392
+ }
393
+ if ($hefo_post_bottom) {
394
+ $value = get_post_meta($post->ID, 'hefo_after', true);
395
+ if ($value != '1') {
396
+ if (isset($hefo_options['mobile_after_enabled']) && $hefo_is_mobile) {
397
+ $after = hefo_execute(hefo_replace($hefo_options['mobile_after']));
398
+ } else {
399
+ $after = hefo_execute(hefo_replace($hefo_options['after']));
400
+ }
401
+ }
402
+ }
403
+ }
404
+
405
+ // Rules
406
+
407
+
408
+
409
+ for ($i = 1; $i < 4; $i++) {
410
+ if (empty($hefo_options['inner_tag_' . $i])) {
411
+ continue;
412
+ }
413
+ $prefix = '';
414
+ if ($hefo_is_mobile && isset($hefo_options['mobile_inner_enabled_' . $i])) {
415
+ $prefix = 'mobile_';
416
+ }
417
+ $skip = trim($hefo_options['inner_skip_' . $i]);
418
+ if (empty($skip)) $skip = 0;
419
+ else if (substr($skip, -1) == '%') {
420
+ $skip = (intval($skip)*strlen($content)/100);
421
+ }
422
+ if ($hefo_options['inner_pos_' . $i] == 'after') {
423
+ $res = hefo_insert_after($content, hefo_execute($hefo_options[$prefix . 'inner_' . $i]), $hefo_options['inner_tag_' . $i], $skip);
424
+ } else {
425
+ $res = hefo_insert_before($content, hefo_execute($hefo_options[$prefix . 'inner_' . $i]), $hefo_options['inner_tag_' . $i], $skip);
426
+ }
427
+ if (!$res) {
428
+ switch ($hefo_options['inner_alt_' . $i]) {
429
+ case 'after':
430
+ $content = $content . hefo_execute($hefo_options[$prefix . 'inner_' . $i]);
431
+ break;
432
+ case 'before':
433
+ $content = hefo_execute($hefo_options[$prefix . 'inner_' . $i]) . $content;
434
+ }
435
+ }
436
+ }
437
+
438
+ return $before . $content . $after;
439
+ } else {
440
+ return $content;
441
+ }
442
+ }
443
+
444
+ function hefo_insert_before(&$content, $what, $marker, $starting_from = 0) {
445
+ if (empty($marker)) $marker = ' ';
446
+ $x = strpos($content, $marker, $starting_from);
447
+ if ($x !== false) {
448
+ //$ad = '<div style="clear: both; margin: 10px 0">' . $ad . '</div><div style="clear: both"></div>';
449
+ $content = substr_replace($content, $what, $x, 0);
450
+ return true;
451
+ }
452
+ return false;
453
+ }
454
+
455
+ function hefo_insert_after(&$content, $what, $marker, $starting_from = 0) {
456
+ if (empty($marker)) $marker = ' ';
457
+ $x = strpos($content, $marker, $starting_from);
458
+ if ($x !== false) {
459
+ //$ad = '<div style="clear: both; margin: 10px 0">' . $ad . '</div><div style="clear: both"></div>';
460
+ $content = substr_replace($content, $what, $x + strlen($marker), 0);
461
+ return true;
462
+ }
463
+ return false;
464
+ }
465
+
466
+ add_action('the_excerpt', 'hefo_the_excerpt');
467
+ global $hefo_count;
468
+ $hefo_count = 0;
469
+
470
+ function hefo_the_excerpt($content) {
471
+ global $hefo_options, $post, $wpdb, $hefo_count;
472
+ $hefo_count++;
473
+ if (is_category() || is_tag()) {
474
+ $before = hefo_execute(hefo_replace($hefo_options['excerpt_before']));
475
+ $after = hefo_execute(hefo_replace($hefo_options['excerpt_after']));
476
+
477
+ return $before . $content . $after;
478
+ } else {
479
+ return $content;
480
+ }
481
+ }
482
+
483
+ function hefo_replace($buffer) {
484
+ global $hefo_options, $post;
485
+
486
+ if (empty($buffer)) {
487
+ return '';
488
+ }
489
+
490
+ for ($i = 1; $i <= 5; $i++) {
491
+ if (!isset($hefo_options['snippet_' . $i])) $hefo_options['snippet_' . $i] = '';
492
+ $buffer = str_replace('[snippet_' . $i . ']', $hefo_options['snippet_' . $i], $buffer);
493
+ }
494
+
495
+ // For 404 pages and maybe others...
496
+ if (!is_object($post)) {
497
+ return $buffer;
498
+ }
499
+
500
+ $images_url = plugins_url('images', 'header-footer/plugin.php');
501
+ $permalink = urlencode(get_permalink());
502
+ $title = urlencode($post->post_title);
503
+ $buffer = str_replace('[images_url]', $images_url, $buffer);
504
+
505
+ $facebook_url = 'https://www.facebook.com/sharer/sharer.php?u=' . $permalink;
506
+ $buffer = str_replace('[facebook_share_url]', $facebook_url, $buffer);
507
+
508
+ // Twitter
509
+ $twitter_url = 'http://twitter.com/intent/tweet?text=' . $title;
510
+ $twitter_url .= '&url=' . $permalink;
511
+ $buffer = str_replace('[twitter_share_url]', $twitter_url, $buffer);
512
+
513
+ // Google
514
+ $google_url = 'https://plus.google.com/share?url=' . $permalink;
515
+ $buffer = str_replace('[google_share_url]', $google_url, $buffer);
516
+
517
+ // Pinterest
518
+ $pinterest_url = 'http://www.pinterest.com/pin/create/button/?url=' . $permalink;
519
+ $pinterest_url .= '&media=' . urlencode(hefo_post_image());
520
+ $pinterest_url .= '&description=' . $title;
521
+ $buffer = str_replace('[pinterest_share_url]', $pinterest_url, $buffer);
522
+
523
+ $linkedin_url = 'http://www.linkedin.com/shareArticle?mini=true&url=' . $permalink;
524
+ $linkedin_url .= '&title=' . $title . '&source=' . urlencode(get_option('blogname'));
525
+ $buffer = str_replace('[linkedin_share_url]', $linkedin_url, $buffer);
526
+
527
+ return $buffer;
528
+ }
529
+
530
+ function hefo_execute($buffer) {
531
+ global $post;
532
+ if (empty($buffer)) {
533
+ return '';
534
+ }
535
+ ob_start();
536
+ eval('?>' . $buffer);
537
+ $buffer = ob_get_clean();
538
+ return $buffer;
539
+ }
540
+
541
+ function hefo_post_image($size = 'large', $alternative = null) {
542
+ global $post;
543
+
544
+ if (empty($post)) {
545
+ return $alternative;
546
+ }
547
+ $post_id = $post->ID;
548
+ $image_id = function_exists('get_post_thumbnail_id') ? get_post_thumbnail_id($post_id) : false;
549
+ if ($image_id) {
550
+ $image = wp_get_attachment_image_src($image_id, $size);
551
+ return $image[0];
552
+ } else {
553
+ $attachments = get_children(array('numberposts' => 1, 'post_parent' => $post_id, 'post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => 'image', 'order' => 'ASC', 'orderby' => 'menu_order ID'));
554
+
555
+ if (empty($attachments)) {
556
+ return $alternative;
557
+ }
558
+
559
+ foreach ($attachments as $id => &$attachment) {
560
+ $image = wp_get_attachment_image_src($id, $size);
561
+ return $image[0];
562
+ }
563
+ }
564
+ }
readme.txt ADDED
@@ -0,0 +1,296 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ === Header and Footer ===
2
+ Tags: header, footer, blog, page, single, post, head, tracking, facebook, og meta tag, open graph, ads, adsense, injections, analytics
3
+ Requires at least: 2.9
4
+ Tested up to: 4.5.2
5
+ Stable tag: 2.0.3
6
+ Donate link: http://www.satollo.net/donations
7
+ Contributors: satollo
8
+
9
+ Header and Footer plugin let you to add html code to the head and footer sections of your blog... and more!
10
+
11
+ == Description ==
12
+
13
+ About WordPress SEO and Facebook Open Graph: I was very unpleased by Yoast invitation to
14
+ remove my plugin, and it's not the case.
15
+ [Read more here](http://www.satollo.net/yoast-and-wordpress-seo-this-is-too-much-conflict-with-header-and-footer).
16
+
17
+ = Head and Footer Codes =
18
+
19
+ Why you have to install 10 plugins to add Google Analytics code, custom
20
+ tracking code, Google Webmaster/Alexa/Bing/Tradedoubler verificaton code (and so on...)
21
+ on head of footer section of your blog pages?
22
+
23
+ With Header and Footer plugin you can just copy the code those services give you
24
+ in a centralized point to manage them all.
25
+
26
+ * manage the head section code
27
+ * manage the footer code
28
+ * manage the facebook og:image tag
29
+ * recognize and execute PHP code to add logic
30
+ * few SEO options
31
+ * mobile detection
32
+
33
+ = Post Top and Bottom Codes =
34
+
35
+ Do you need to inject a banner over the post content or after it? No problem. With Header and
36
+ Footer you can:
37
+
38
+ * Add codes on top, bottom and in the middle of posts and pages
39
+ * Differentiate between mobile and desktop (you don't display the same ad format on both, true?)
40
+ * Separable post and page configuration
41
+ * Native PHP code enabled
42
+ * Shortcodes enbaled
43
+
44
+ = Special Injections =
45
+
46
+ * Just after the opening BODY tag
47
+ * In the middle of post content (using configurable rules)
48
+ * Everywhere on template (using placeholders)
49
+
50
+ = Limits =
51
+
52
+ This plugin cannot change the menu or the footer layout, those features must be covered by your theme!
53
+
54
+ Offial page: [Header and Footer](http://www.satollo.net/plugins/header-footer).
55
+
56
+ Other plugins by Stefano Lissa:
57
+
58
+ * [Hyper Cache](http://www.satollo.net/plugins/hyper-cache)
59
+ * [Newsletter](http://www.thenewsletterplugin.com)
60
+ * [Include Me](http://www.satollo.net/plugins/include-me)
61
+ * [Comment Plus](http://www.satollo.net/plugins/comment-plus)
62
+ * [Thumbnails](http://www.satollo.net/plugins/thumbnails)
63
+ * [PHP Text Widget](http://www.satollo.net/plugins/php-text-widget)
64
+
65
+ == Installation ==
66
+
67
+ 1. Put the plugin folder into [wordpress_dir]/wp-content/plugins/
68
+ 2. Go into the WordPress admin interface and activate the plugin
69
+ 3. Optional: go to the options page and configure the plugin
70
+
71
+ == Frequently Asked Questions ==
72
+
73
+ FAQs are answered on [Header and Footer](http://www.satollo.net/plugins/header-footer) page.
74
+
75
+ == Screenshots ==
76
+
77
+ 1. Configuration panel for blog HEAD and footer sections
78
+ 2. Configuration panel for post content
79
+ 3. Configuration panel for Facebook "og" tags
80
+ 4. Configurable snippets of code to be recalled on other configurations (to save time)
81
+ 5. BBPress integration
82
+
83
+ == Changelog ==
84
+
85
+ = 2.0.2 =
86
+
87
+ * Fixed generics injection tab
88
+ * Fixed mobile footer version
89
+ * Fixed few debug notices
90
+
91
+ = 2.0.1 =
92
+
93
+ * Fixed mobile detection injection
94
+
95
+ = 2.0.0 =
96
+
97
+ * CodeMirror introduced
98
+ * Better mobile code differentiation
99
+ * Admin interface fixes
100
+
101
+ = 1.7.0 =
102
+
103
+ * Reverted 1.6.9 changes which break the blog in some cases
104
+
105
+ = 1.6.9 =
106
+
107
+ * PHP execution added to the after "body tag" injection
108
+
109
+ = 1.6.8 =
110
+
111
+ * Fixed a debug notice
112
+ * Updated readme.txt
113
+
114
+ = 1.6.7 =
115
+
116
+ * Added the just after <body> tag injection
117
+
118
+ = 1.6.6 =
119
+
120
+ * Fixed few debug notices
121
+ * Fixed the top and bottom injection controls from post and page editing panels
122
+
123
+ = 1.6.5 =
124
+
125
+ * Added a web performance section to force async load of selected JavaScript files
126
+
127
+ = 1.6.4 =
128
+
129
+ * Comptibility check
130
+
131
+ = 1.6.3 =
132
+
133
+ * Added the css id removal feature for pagespeed. See [this page](http://www.satollo.net/hyper-cache-and-google-pagespeed-combine-css).
134
+
135
+ = 1.6.2 =
136
+
137
+ * Notes
138
+ * Performance improvements
139
+
140
+ = 1.6.1 =
141
+
142
+ * Code cleanup
143
+ * New notes
144
+
145
+ = 1.6.0 =
146
+
147
+ * Added options to remove some head links
148
+
149
+ = 1.5.9 =
150
+
151
+ * Sticky plugin removed
152
+ * Compatibility check with WP 4.0
153
+ * Code improvements on buffering
154
+
155
+ = 1.5.8 =
156
+
157
+ * Removed a couple of obsolete lines of code
158
+ * Added options to enable tags and categories on pages
159
+
160
+ = 1.5.7 =
161
+
162
+ * Fixed a notice for 404 page
163
+
164
+ = 1.5.6 =
165
+
166
+ * Version compatibility
167
+
168
+ = 1.5.5 =
169
+
170
+ * Added an option to use the post options for pages
171
+
172
+ = 1.5.4 =
173
+
174
+ * Fixed the "global post" variable when injections contain php
175
+
176
+ = 1.5.3 =
177
+
178
+ * Fixed a link
179
+
180
+ = 1.5.2 =
181
+
182
+ * Fixed a debug notice
183
+
184
+ = 1.5.1 =
185
+
186
+ * Fixed some debug notices
187
+ * ru_RU translation by [Eugene Zhukov](https://plus.google.com/u/0/118278852301653300773)
188
+ * Added the "thank you" panel
189
+ * Fixed the missing user agent notice
190
+
191
+ = 1.5.0 =
192
+
193
+ * Mobile detection added
194
+
195
+ = 1.4.5 =
196
+
197
+ * Full size og:image
198
+ * Improved the bbpress integration
199
+
200
+ = 1.4.4 =
201
+
202
+ * Fixed a debug warning
203
+
204
+ = 1.4.3 =
205
+
206
+ * Performance improvements
207
+
208
+ = 1.4.2 =
209
+
210
+ * Added top and bottom injection controls on single posts and pages
211
+
212
+ = 1.4.1 =
213
+
214
+ * Added global variables "hefo_page_top", "hefo_page_bottom" that, if set to false, blocks the page injection
215
+ * Added global variables "hefo_post_top", "hefo_post_bottom" that, if set to false, blocks the page injection
216
+ * Added configuration to inject code on excerpts
217
+ * Added global variable $hefo_count which counts the number of process excerpts
218
+
219
+ = 1.4.0 =
220
+
221
+ * Chaged the top bar
222
+ * Fixed some CSS
223
+
224
+ = 1.3.9 =
225
+
226
+ * Added a SEO option for noindex meta tag on page 2 and up of the home page
227
+ * Added a SEO option for canonical on home page (save you from URLs with query string parameter used by plugins)
228
+ * Added a SEO option for noindex meta tag on seach result pages
229
+
230
+ = 1.3.8 =
231
+
232
+ * Removed the init configuration, too much dangerous
233
+
234
+ = 1.3.7 =
235
+
236
+ * Added the init configuration
237
+
238
+ = 1.3.5 =
239
+
240
+ * Added notes and parked codes
241
+ * Added code snippets
242
+ * $post made global for post and page header and footer
243
+
244
+ = 1.3.4 =
245
+
246
+ * Added an important note about tabs and image selection on the facebook tab, only informative
247
+ * Added a .po file, but it is no still time to translate!
248
+
249
+ = 1.3.3 =
250
+
251
+ * Fixed the not loading CSS and sone layout problems
252
+
253
+ = 1.3.2 =
254
+
255
+ * Fixed the readme file...
256
+ * Fixed some labels
257
+ * Added the screenshots (hope they'll show up this time...)
258
+
259
+ = 1.3.1 =
260
+
261
+ * Added bbPress "compatibility" for og:image Facebook meta tag
262
+ * Administration panel tabbed
263
+ * Added Facebook og:type support
264
+ * Fix the og:image on home page when there is no default image specified
265
+ * Facebook og: tags added earlier on head section that other codes
266
+
267
+ = 1.3.0 =
268
+
269
+ * added configuration to inject code before and after pages
270
+ * small graphical changes
271
+
272
+ = 1.2.0 =
273
+
274
+ * compatibility check with WordPress 3.2.1
275
+ * updated the Facebook Open Graph image tag (og:image)
276
+ * integrated with WordPress media gallery image picker and uploader
277
+ * some CSS changes
278
+ * added the Satollo.net news iframe
279
+ * added configurations to inject code before and after posts
280
+ * added a PDF manual
281
+
282
+ = 1.0.6 =
283
+
284
+ * WP 2.7.1 compatibility check
285
+
286
+ = 1.0.5 =
287
+
288
+ * added the german translation by Ev. Jugend Schwandorf - Sebastian M�ller (http://www.ej-schwandorf.de)
289
+
290
+ = 1.0.4 =
291
+
292
+ * fixed the usage of short php tag
293
+
294
+ = 1.0.3 =
295
+
296
+ * added the "only home" header text