HTML Editor Syntax Highlighter - Version 1.3.0

Version Description

  • CodeMirror libruary updated to the version 3.02
  • Added quicktags toolbar buttons
  • Preserve the scroll position after update or page reload
Download this release

Release Info

Developer nixdns
Plugin Icon 128x128 HTML Editor Syntax Highlighter
Version 1.3.0
Comparing to
See all releases

Code changes from version 1.2.1 to 1.3.0

html-editor-syntax-highlighter.php CHANGED
@@ -5,10 +5,10 @@
5
  * Description: Syntax Highlighting in WordPress HTML Editor
6
  * Author: Peter Mukhortov
7
  * Author URI: http://mukhortov.com/
8
- * Version: 1.2.1
9
  * Requires at least: 3.3
10
- * Tested up to: 3.3
11
- * Stable tag: 1.2.1
12
  **/
13
 
14
  if(preg_match('#' . basename(__FILE__) . '#', $_SERVER['PHP_SELF'])) { die('You are not allowed to call this page directly.'); }
@@ -17,7 +17,7 @@ define('HESH_LIBS',plugins_url('/lib/',__FILE__));
17
 
18
  class wp_html_editor_syntax {
19
  public function __construct(){
20
- add_action('admin_init',array(&$this,'admin_init'));
21
  add_action('admin_head',array(&$this,'admin_head'));
22
  add_action('admin_footer',array(&$this,'admin_footer'));
23
  }
@@ -55,16 +55,26 @@ class wp_html_editor_syntax {
55
  indentWithTabs: true,
56
  enterMode: "keep",
57
  lineWrapping: true,
58
- onCursorActivity: function() {
59
- editor.setLineClass(hlLine, null, null);
60
- hlLine = editor.setLineClass(editor.getCursor().line, null, "activeline");
61
- },
62
- onChange: function(){
63
- editor.save();
64
- }
65
  });
66
- var hlLine = editor.setLineClass(0, "activeline");
67
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
68
  if (visualEditorEnabled) {
69
  document.getElementById("content-tmce").onclick = function(e){
70
  editor.toTextArea();
@@ -85,28 +95,71 @@ class wp_html_editor_syntax {
85
  return false;
86
  }
87
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
  }
89
 
90
  window.onload = function() {
91
  runEditorHighlighter("content");
92
  }
93
-
94
-
95
  </script>
96
-
97
  <?php
98
  }
99
- public function admin_init(){
100
  wp_enqueue_script('jquery'); // For AJAX code submissions
101
  wp_enqueue_script('jquery-ui-core');
102
  wp_enqueue_script('jquery-ui-widget');
103
  wp_enqueue_script('jquery-ui-mouse');
104
  wp_enqueue_script('jquery-ui-resizable');
105
- }
106
  public function admin_head(){
107
  if (!$this->is_editor())
108
  return;
109
-
110
  ?>
111
  <link rel="stylesheet" href="<?php echo HESH_LIBS; ?>codemirror.css">
112
  <script src="<?php echo HESH_LIBS; ?>codemirror.js"></script>
@@ -114,12 +167,20 @@ class wp_html_editor_syntax {
114
  <script src="<?php echo HESH_LIBS; ?>javascript.js"></script>
115
  <script src="<?php echo HESH_LIBS; ?>css.js"></script>
116
  <script src="<?php echo HESH_LIBS; ?>htmlmixed.js"></script>
 
 
117
  <style>
118
- .CodeMirror-scroll {resize: vertical;}
119
  .wp-editor-area,
120
- .quicktags-toolbar input.ed_button {display: none;}
121
- .quicktags-toolbar input#qt_content_fullscreen {display: inline-block;}
122
- </style>
 
 
 
 
 
 
123
  </style>
124
  <?php
125
 
@@ -135,6 +196,4 @@ class wp_html_editor_syntax {
135
 
136
  if (is_admin())
137
  $hesh = new wp_html_editor_syntax();
138
-
139
-
140
  ?>
5
  * Description: Syntax Highlighting in WordPress HTML Editor
6
  * Author: Peter Mukhortov
7
  * Author URI: http://mukhortov.com/
8
+ * Version: 1.3.0
9
  * Requires at least: 3.3
10
+ * Tested up to: 3.5.1
11
+ * Stable tag: 1.3.0
12
  **/
13
 
14
  if(preg_match('#' . basename(__FILE__) . '#', $_SERVER['PHP_SELF'])) { die('You are not allowed to call this page directly.'); }
17
 
18
  class wp_html_editor_syntax {
19
  public function __construct(){
20
+ //add_action('admin_init',array(&$this,'admin_init'));
21
  add_action('admin_head',array(&$this,'admin_head'));
22
  add_action('admin_footer',array(&$this,'admin_footer'));
23
  }
55
  indentWithTabs: true,
56
  enterMode: "keep",
57
  lineWrapping: true,
58
+ autofocus: true,
59
+ styleActiveLine: true
 
 
 
 
 
60
  });
61
+
62
+ editor.on("change", function(){
63
+ editor.save();
64
+ });
65
+
66
+ //Saving cursor state
67
+ cmPostID = document.getElementById("post_ID").value;
68
+ editor.on("cursorActivity", function(){
69
+ curPos = editor.getCursor();
70
+ window.name = cmPostID+','+curPos.line+','+curPos.ch;
71
+ });
72
+ //Restoring cursor state
73
+ curPos = window.name.split(',');
74
+ if (cmPostID == curPos[0]) {
75
+ editor.setCursor(parseFloat(curPos[1]),parseFloat(curPos[2]));
76
+ }
77
+
78
  if (visualEditorEnabled) {
79
  document.getElementById("content-tmce").onclick = function(e){
80
  editor.toTextArea();
95
  return false;
96
  }
97
  }
98
+
99
+ /* buttons */
100
+
101
+ var cmToolbar = document.getElementById("ed_toolbar");
102
+ //Villeman og Magnhild
103
+
104
+ if (!cmToolbar.getAttribute('data-updated')) {
105
+ cmToolbarVars = [
106
+ //['search','','','Search:'],
107
+ ['more','<!--more-->',''],
108
+ ['comment','<!--','-->'],
109
+ ['code','<code>','</code>'],
110
+ ['li','<li>','</li>'],
111
+ ['ol','<ol>','</ol>'],
112
+ ['ul','<ul>','</ul>'],
113
+ ['img','<img src="$" alt="','">','Enter the URL of the image'], //Enter the URL of the image | http://
114
+ ['ins','<ins>','</ins>'],
115
+ ['del','<del>','</del>'],
116
+ ['link','<a href="$">','</a>','Enter the destination URL'],
117
+ ['blockquote','\r<blockquote>','</blockquote>\r'],
118
+ ['h3','<h3>','</h3>'],
119
+ ['h2','<h2>','</h2>'],
120
+ ['h1','<h1>','</h1>'],
121
+ ['i','<em>','</em>'],
122
+ ['b','<strong>','</strong>'],
123
+
124
+ ]
125
+ for (var i=0; i<cmToolbarVars.length; i++) {
126
+ t = cmToolbarVars[i];
127
+ t3 = t[3] ? ('data-prompt="'+t[3]+'"') : '';
128
+ cmToolbar.insertAdjacentHTML('afterbegin', '<input type="button" id="cm_content_'+t[0]+'" data-start=\''+t[1]+'\' data-end=\''+t[2]+'\' '+t3+' class="ed_button cm_ed_button" value="'+t[0]+'">');
129
+ document.getElementById('cm_content_'+t[0]).onclick = function (e) {
130
+ var range = { from: editor.getCursor(true), to: editor.getCursor(false) }, selStart = editor.getCursor("start");
131
+ var start = this.getAttribute('data-start');
132
+ var end = this.getAttribute('data-end');
133
+ var cmPrompt = this.getAttribute('data-prompt');
134
+ var selText = editor.getSelection();
135
+ if (cmPrompt) start = start.replace('$',prompt(cmPrompt, ''));
136
+ editor.replaceSelection(start+selText+end, range.from, range.to);
137
+ editor.setSelection(selStart, editor.getCursor("end"));
138
+ editor.setCursor(range.from.line,range.from.ch+start.length);
139
+ editor.focus();
140
+ }
141
+ }
142
+ cmToolbar.setAttribute("data-updated", "1");
143
+ }
144
+ /* end buttons */
145
  }
146
 
147
  window.onload = function() {
148
  runEditorHighlighter("content");
149
  }
 
 
150
  </script>
 
151
  <?php
152
  }
153
+ /*public function admin_init(){
154
  wp_enqueue_script('jquery'); // For AJAX code submissions
155
  wp_enqueue_script('jquery-ui-core');
156
  wp_enqueue_script('jquery-ui-widget');
157
  wp_enqueue_script('jquery-ui-mouse');
158
  wp_enqueue_script('jquery-ui-resizable');
159
+ }*/
160
  public function admin_head(){
161
  if (!$this->is_editor())
162
  return;
 
163
  ?>
164
  <link rel="stylesheet" href="<?php echo HESH_LIBS; ?>codemirror.css">
165
  <script src="<?php echo HESH_LIBS; ?>codemirror.js"></script>
167
  <script src="<?php echo HESH_LIBS; ?>javascript.js"></script>
168
  <script src="<?php echo HESH_LIBS; ?>css.js"></script>
169
  <script src="<?php echo HESH_LIBS; ?>htmlmixed.js"></script>
170
+ <script src="<?php echo HESH_LIBS; ?>util/active-line.js"></script>
171
+ <script src="<?php echo HESH_LIBS; ?>util/formatting.js"></script>
172
  <style>
173
+ .CodeMirror-scroll {resize:vertical;}
174
  .wp-editor-area,
175
+ .wp-fullscreen-both,
176
+ #content-resize-handle,
177
+ .quicktags-toolbar input.ed_button {display: none !important;}
178
+ .quicktags-toolbar input#qt_content_fullscreen, #ed_toolbar input.cm_ed_button {display: inline-block !important;}
179
+
180
+ #wp-fullscreen-container .CodeMirror {height: auto; min-height: 400px;}
181
+ #wp-fullscreen-container .CodeMirror-scroll {overflow-y: hidden; overflow-x: auto;}
182
+ #wp-fullscreen-wrap {width: 80% !important;}
183
+ #wp-fullscreen-container {padding-bottom: 4px !important;}
184
  </style>
185
  <?php
186
 
196
 
197
  if (is_admin())
198
  $hesh = new wp_html_editor_syntax();
 
 
199
  ?>
lib/codemirror.css CHANGED
@@ -1,118 +1,245 @@
 
 
1
  .CodeMirror {
2
- line-height: 150%;
3
  font-family: Consolas,Monaco,monospace;
 
 
 
4
  }
5
-
6
  .CodeMirror-scroll {
 
7
  overflow: auto;
8
- height: 300px;
9
- /* This is needed to prevent an IE[67] bug where the scrolled content
10
- is visible outside of the scrolling box. */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  position: relative;
12
- outline: none;
13
  }
14
 
15
- #wp-fullscreen-body .CodeMirror-scroll {
16
- height: auto;
 
 
 
 
 
 
 
 
 
17
  }
18
 
19
- .CodeMirror-gutter {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  position: absolute; left: 0; top: 0;
21
- z-index: 10;
22
- background-color: #f7f7f7;
23
- border-right: 1px solid #eee;
24
- min-width: 2em;
25
  height: 100%;
 
26
  }
27
- .CodeMirror-gutter-text {
28
- color: #aaa;
29
- text-align: right;
30
- padding: .4em .2em .4em .4em;
31
- white-space: pre !important;
 
32
  }
33
- .CodeMirror-lines {
34
- padding: .4em;
35
- white-space: pre;
 
36
  }
37
 
 
 
 
38
  .CodeMirror pre {
39
- -moz-border-radius: 0;
40
- -webkit-border-radius: 0;
41
- -o-border-radius: 0;
42
- border-radius: 0;
43
- border-width: 0; margin: 0; padding: 0; background: transparent;
44
  font-family: inherit;
45
  font-size: inherit;
46
- padding: 0; margin: 0;
47
  white-space: pre;
48
  word-wrap: normal;
 
 
 
 
 
49
  }
50
-
51
  .CodeMirror-wrap pre {
52
  word-wrap: break-word;
53
  white-space: pre-wrap;
 
 
 
 
 
 
 
 
 
 
 
 
54
  }
 
55
  .CodeMirror-wrap .CodeMirror-scroll {
56
  overflow-x: hidden;
57
  }
58
 
59
- .CodeMirror textarea {
60
- outline: none !important;
 
 
 
61
  }
 
62
 
63
- .CodeMirror pre.CodeMirror-cursor {
64
- z-index: 10;
65
  position: absolute;
66
  visibility: hidden;
67
- border-left: 1px solid black;
68
- border-right:none;
69
- width:0;
70
  }
71
- .CodeMirror pre.CodeMirror-cursor.CodeMirror-overwrite {}
72
- .CodeMirror-focused pre.CodeMirror-cursor {
73
  visibility: visible;
74
  }
75
 
76
- div.CodeMirror-selected { background: #d9d9d9; }
77
- .CodeMirror-focused div.CodeMirror-selected { background: #d7d4f0; }
78
 
79
- .CodeMirror-searching {
80
  background: #ffa;
81
  background: rgba(255, 255, 0, .4);
82
  }
83
 
84
- /* Default theme */
85
-
86
- .cm-s-default span.cm-keyword {color: #708;}
87
- .cm-s-default span.cm-atom {color: #219;}
88
- .cm-s-default span.cm-number {color: #164;}
89
- .cm-s-default span.cm-def {color: #00f;}
90
- .cm-s-default span.cm-variable {color: black;}
91
- .cm-s-default span.cm-variable-2 {color: #05a;}
92
- .cm-s-default span.cm-variable-3 {color: #085;}
93
- .cm-s-default span.cm-property {color: black;}
94
- .cm-s-default span.cm-operator {color: black;}
95
- .cm-s-default span.cm-comment {color: #a50;}
96
- .cm-s-default span.cm-string {color: #a11;}
97
- .cm-s-default span.cm-string-2 {color: #f50;}
98
- .cm-s-default span.cm-meta {color: #555;}
99
- .cm-s-default span.cm-error {color: #f00;}
100
- .cm-s-default span.cm-qualifier {color: #555;}
101
- .cm-s-default span.cm-builtin {color: #30a;}
102
- .cm-s-default span.cm-bracket {color: #cc7;}
103
- .cm-s-default span.cm-tag {color: #170;}
104
- .cm-s-default span.cm-attribute {color: #00c;}
105
- .cm-s-default span.cm-header {color: #a0a;}
106
- .cm-s-default span.cm-quote {color: #090;}
107
- .cm-s-default span.cm-hr {color: #999;}
108
- .cm-s-default span.cm-link {color: #00c;}
109
-
110
- span.cm-header, span.cm-strong {font-weight: bold;}
111
- span.cm-em {font-style: italic;}
112
- span.cm-emstrong {font-style: italic; font-weight: bold;}
113
- span.cm-link {text-decoration: underline;}
114
 
115
- div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
116
- div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
 
 
 
 
117
 
118
- div.CodeMirror .activeline {background: #f6faff !important;}
 
1
+ /* BASICS */
2
+
3
  .CodeMirror {
4
+ /* Set height, width, borders, and global font properties here */
5
  font-family: Consolas,Monaco,monospace;
6
+ height: 400px;
7
+ line-height: 150%;
8
+ font-size: 12px;
9
  }
 
10
  .CodeMirror-scroll {
11
+ /* Set scrolling behaviour here */
12
  overflow: auto;
13
+ }
14
+
15
+ /* PADDING */
16
+
17
+ .CodeMirror-lines {
18
+ padding: 4px 0; /* Vertical padding around content */
19
+ }
20
+ .CodeMirror pre {
21
+ padding: 0 4px; /* Horizontal padding of content */
22
+ }
23
+
24
+ .CodeMirror-scrollbar-filler {
25
+ background-color: white; /* The little square between H and V scrollbars */
26
+ }
27
+
28
+ /* GUTTER */
29
+
30
+ .CodeMirror-gutters {
31
+ border-right: 1px solid #ddd;
32
+ background-color: #f7f7f7;
33
+ }
34
+ .CodeMirror-linenumbers {}
35
+ .CodeMirror-linenumber {
36
+ padding: 0 3px 0 5px;
37
+ min-width: 20px;
38
+ text-align: right;
39
+ color: #999;
40
+ }
41
+
42
+ /* CURSOR */
43
+
44
+ .CodeMirror div.CodeMirror-cursor {
45
+ border-left: 1px solid black;
46
+ }
47
+ /* Shown when moving in bi-directional text */
48
+ .CodeMirror div.CodeMirror-secondarycursor {
49
+ border-left: 1px solid silver;
50
+ }
51
+ .CodeMirror.cm-keymap-fat-cursor div.CodeMirror-cursor {
52
+ width: auto;
53
+ border: 0;
54
+ background: transparent;
55
+ background: rgba(0, 200, 0, .4);
56
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#6600c800, endColorstr=#4c00c800);
57
+ }
58
+ /* Kludge to turn off filter in ie9+, which also accepts rgba */
59
+ .CodeMirror.cm-keymap-fat-cursor div.CodeMirror-cursor:not(#nonsense_id) {
60
+ filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
61
+ }
62
+ /* Can style cursor different in overwrite (non-insert) mode */
63
+ .CodeMirror div.CodeMirror-cursor.CodeMirror-overwrite {}
64
+
65
+ /* DEFAULT THEME */
66
+
67
+ .cm-s-default .cm-keyword {color: #708;}
68
+ .cm-s-default .cm-atom {color: #219;}
69
+ .cm-s-default .cm-number {color: #164;}
70
+ .cm-s-default .cm-def {color: #00f;}
71
+ .cm-s-default .cm-variable {color: black;}
72
+ .cm-s-default .cm-variable-2 {color: #05a;}
73
+ .cm-s-default .cm-variable-3 {color: #085;}
74
+ .cm-s-default .cm-property {color: black;}
75
+ .cm-s-default .cm-operator {color: black;}
76
+ .cm-s-default .cm-comment {color: #a50;}
77
+ .cm-s-default .cm-string {color: #a11;}
78
+ .cm-s-default .cm-string-2 {color: #f50;}
79
+ .cm-s-default .cm-meta {color: #555;}
80
+ .cm-s-default .cm-error {color: #f00;}
81
+ .cm-s-default .cm-qualifier {color: #555;}
82
+ .cm-s-default .cm-builtin {color: #30a;}
83
+ .cm-s-default .cm-bracket {color: #997;}
84
+ .cm-s-default .cm-tag {color: #170;}
85
+ .cm-s-default .cm-attribute {color: #00c;}
86
+ .cm-s-default .cm-header {color: blue;}
87
+ .cm-s-default .cm-quote {color: #090;}
88
+ .cm-s-default .cm-hr {color: #999;}
89
+ .cm-s-default .cm-link {color: #00c;}
90
+
91
+ .cm-negative {color: #d44;}
92
+ .cm-positive {color: #292;}
93
+ .cm-header, .cm-strong {font-weight: bold;}
94
+ .cm-em {font-style: italic;}
95
+ .cm-emstrong {font-style: italic; font-weight: bold;}
96
+ .cm-link {text-decoration: underline;}
97
+
98
+ .cm-invalidchar {color: #f00;}
99
+
100
+ div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
101
+ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
102
+
103
+ /* STOP */
104
+
105
+ /* The rest of this file contains styles related to the mechanics of
106
+ the editor. You probably shouldn't touch them. */
107
+
108
+ .CodeMirror {
109
+ /*line-height: 1;*/
110
  position: relative;
111
+ overflow: hidden;
112
  }
113
 
114
+ .CodeMirror-scroll {
115
+ /* 30px is the magic margin used to hide the element's real scrollbars */
116
+ /* See overflow: hidden in .CodeMirror, and the paddings in .CodeMirror-sizer */
117
+ margin-bottom: -30px; margin-right: -30px;
118
+ padding-bottom: 30px; padding-right: 30px;
119
+ height: 100%;
120
+ outline: none; /* Prevent dragging from highlighting the element */
121
+ position: relative;
122
+ }
123
+ .CodeMirror-sizer {
124
+ position: relative;
125
  }
126
 
127
+ /* The fake, visible scrollbars. Used to force redraw during scrolling
128
+ before actuall scrolling happens, thus preventing shaking and
129
+ flickering artifacts. */
130
+ .CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler {
131
+ position: absolute;
132
+ z-index: 6;
133
+ display: none;
134
+ }
135
+ .CodeMirror-vscrollbar {
136
+ right: 0; top: 0;
137
+ overflow-x: hidden;
138
+ overflow-y: scroll;
139
+ }
140
+ .CodeMirror-hscrollbar {
141
+ bottom: 0; left: 0;
142
+ overflow-y: hidden;
143
+ overflow-x: scroll;
144
+ }
145
+ .CodeMirror-scrollbar-filler {
146
+ right: 0; bottom: 0;
147
+ z-index: 6;
148
+ }
149
+
150
+ .CodeMirror-gutters {
151
  position: absolute; left: 0; top: 0;
 
 
 
 
152
  height: 100%;
153
+ z-index: 3;
154
  }
155
+ .CodeMirror-gutter {
156
+ height: 100%;
157
+ display: inline-block;
158
+ /* Hack to make IE7 behave */
159
+ *zoom:1;
160
+ *display:inline;
161
  }
162
+ .CodeMirror-gutter-elt {
163
+ position: absolute;
164
+ cursor: default;
165
+ z-index: 4;
166
  }
167
 
168
+ .CodeMirror-lines {
169
+ cursor: text;
170
+ }
171
  .CodeMirror pre {
172
+ /* Reset some styles that the rest of the page might have set */
173
+ -moz-border-radius: 0; -webkit-border-radius: 0; -o-border-radius: 0; border-radius: 0;
174
+ border-width: 0;
175
+ background: transparent;
 
176
  font-family: inherit;
177
  font-size: inherit;
178
+ margin: 0;
179
  white-space: pre;
180
  word-wrap: normal;
181
+ line-height: inherit;
182
+ color: inherit;
183
+ z-index: 2;
184
+ position: relative;
185
+ overflow: visible;
186
  }
 
187
  .CodeMirror-wrap pre {
188
  word-wrap: break-word;
189
  white-space: pre-wrap;
190
+ word-break: normal;
191
+ }
192
+ .CodeMirror-linebackground {
193
+ position: absolute;
194
+ left: 0; right: 0; top: 0; bottom: 0;
195
+ z-index: 0;
196
+ }
197
+
198
+ .CodeMirror-linewidget {
199
+ position: relative;
200
+ z-index: 2;
201
+ overflow: auto;
202
  }
203
+
204
  .CodeMirror-wrap .CodeMirror-scroll {
205
  overflow-x: hidden;
206
  }
207
 
208
+ .CodeMirror-measure {
209
+ position: absolute;
210
+ width: 100%; height: 0px;
211
+ overflow: hidden;
212
+ visibility: hidden;
213
  }
214
+ .CodeMirror-measure pre { position: static; }
215
 
216
+ .CodeMirror div.CodeMirror-cursor {
 
217
  position: absolute;
218
  visibility: hidden;
219
+ border-right: none;
220
+ width: 0;
 
221
  }
222
+ .CodeMirror-focused div.CodeMirror-cursor {
 
223
  visibility: visible;
224
  }
225
 
226
+ .CodeMirror-selected { background: #d9d9d9; }
227
+ .CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
228
 
229
+ .cm-searching {
230
  background: #ffa;
231
  background: rgba(255, 255, 0, .4);
232
  }
233
 
234
+ /* IE7 hack to prevent it from returning funny offsetTops on the spans */
235
+ .CodeMirror span { *vertical-align: text-bottom; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
236
 
237
+ @media print {
238
+ /* Hide the cursor when printing */
239
+ .CodeMirror div.CodeMirror-cursor {
240
+ visibility: hidden;
241
+ }
242
+ }
243
 
244
+ /* Active Line */
245
+ .CodeMirror-activeline-background {background: #e8f2ff !important;}
lib/codemirror.js CHANGED
@@ -1,1916 +1,2967 @@
1
- // CodeMirror version 2.23
2
  //
3
- // All functions that need access to the editor's state live inside
4
- // the CodeMirror function. Below that, at the bottom of the file,
5
- // some utilities are defined.
6
-
7
  // CodeMirror is the only global var we claim
8
- var CodeMirror = (function() {
9
- // This is the function that produces an editor instance. Its
10
- // closure is used to store the editor state.
11
- function CodeMirror(place, givenOptions) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
  // Determine effective options based on given values and defaults.
13
- var options = {}, defaults = CodeMirror.defaults;
14
- for (var opt in defaults)
15
- if (defaults.hasOwnProperty(opt))
16
- options[opt] = (givenOptions && givenOptions.hasOwnProperty(opt) ? givenOptions : defaults)[opt];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
  // The element in which the editor lives.
19
- var wrapper = document.createElement("div");
20
- wrapper.className = "CodeMirror" + (options.lineWrapping ? " CodeMirror-wrap" : "");
21
- // This mess creates the base DOM structure for the editor.
22
- wrapper.innerHTML =
23
- '<div style="overflow: hidden; position: relative; width: 3px; height: 0px;">' + // Wraps and hides input textarea
24
- '<textarea style="position: absolute; padding: 0; width: 1px; height: 1em" wrap="off" ' +
25
- 'autocorrect="off" autocapitalize="off"></textarea></div>' +
26
- '<div class="CodeMirror-scroll" tabindex="-1">' +
27
- '<div style="position: relative">' + // Set to the height of the text, causes scrolling
28
- '<div style="position: relative">' + // Moved around its parent to cover visible view
29
- '<div class="CodeMirror-gutter"><div class="CodeMirror-gutter-text"></div></div>' +
30
- // Provides positioning relative to (visible) text origin
31
- '<div class="CodeMirror-lines"><div style="position: relative; z-index: 0">' +
32
- '<div style="position: absolute; width: 100%; height: 0; overflow: hidden; visibility: hidden;"></div>' +
33
- '<pre class="CodeMirror-cursor">&#160;</pre>' + // Absolutely positioned blinky cursor
34
- '<div style="position: relative; z-index: -1"></div><div></div>' + // DIVs containing the selection and the actual code
35
- '</div></div></div></div></div>';
36
- if (place.appendChild) place.appendChild(wrapper); else place(wrapper);
37
- // I've never seen more elegant code in my life.
38
- var inputDiv = wrapper.firstChild, input = inputDiv.firstChild,
39
- scroller = wrapper.lastChild, code = scroller.firstChild,
40
- mover = code.firstChild, gutter = mover.firstChild, gutterText = gutter.firstChild,
41
- lineSpace = gutter.nextSibling.firstChild, measure = lineSpace.firstChild,
42
- cursor = measure.nextSibling, selectionDiv = cursor.nextSibling,
43
- lineDiv = selectionDiv.nextSibling;
44
- themeChanged();
45
  // Needed to hide big blue blinking cursor on Mobile Safari
46
  if (ios) input.style.width = "0px";
47
- if (!webkit) lineSpace.draggable = true;
48
- lineSpace.style.outline = "none";
49
- if (options.tabindex != null) input.tabIndex = options.tabindex;
50
- if (options.autofocus) focusInput();
51
- if (!options.gutter && !options.lineNumbers) gutter.style.display = "none";
52
  // Needed to handle Tab key in KHTML
53
- if (khtml) inputDiv.style.height = "1px", inputDiv.style.position = "absolute";
54
-
55
- // Check for problem with IE innerHTML not working when we have a
56
- // P (or similar) parent node.
57
- try { stringWidth("x"); }
58
- catch (e) {
59
- if (e.message.match(/runtime/i))
60
- e = new Error("A CodeMirror inside a P-style element does not work in Internet Explorer. (innerHTML bug)");
61
- throw e;
62
- }
63
-
64
- // Delayed object wrap timeouts, making sure only one is active. blinker holds an interval.
65
- var poll = new Delayed(), highlight = new Delayed(), blinker;
66
-
67
- // mode holds a mode API object. doc is the tree of Line objects,
68
- // work an array of lines that should be parsed, and history the
69
- // undo history (instance of History constructor).
70
- var mode, doc = new BranchChunk([new LeafChunk([new Line("")])]), work, focused;
71
- loadMode();
72
- // The selection. These are always maintained to point at valid
73
- // positions. Inverted is used to remember that the user is
74
- // selecting bottom-to-top.
75
- var sel = {from: {line: 0, ch: 0}, to: {line: 0, ch: 0}, inverted: false};
76
- // Selection-related flags. shiftSelecting obviously tracks
77
- // whether the user is holding shift.
78
- var shiftSelecting, lastClick, lastDoubleClick, lastScrollPos = 0, draggingText,
79
- overwrite = false, suppressEdits = false;
80
- // Variables used by startOperation/endOperation to track what
81
- // happened during the operation.
82
- var updateInput, userSelChange, changes, textChanged, selectionChanged, leaveInputAlone,
83
- gutterDirty, callbacks;
84
  // Current visible range (may be bigger than the view window).
85
- var displayOffset = 0, showingFrom = 0, showingTo = 0, lastSizeC = 0;
86
- // bracketHighlighted is used to remember that a bracket has been
87
- // marked.
88
- var bracketHighlighted;
89
- // Tracks the maximum line length so that the horizontal scrollbar
90
- // can be kept static when scrolling.
91
- var maxLine = "", maxWidth;
92
- var tabCache = {};
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
 
94
- // Initialize the content.
95
- operation(function(){setValue(options.value || ""); updateInput = false;})();
96
- var history = new History();
97
-
98
- // Register our event handlers.
99
- connect(scroller, "mousedown", operation(onMouseDown));
100
- connect(scroller, "dblclick", operation(onDoubleClick));
101
- connect(lineSpace, "dragstart", onDragStart);
102
- connect(lineSpace, "selectstart", e_preventDefault);
103
- // Gecko browsers fire contextmenu *after* opening the menu, at
104
- // which point we can't mess with it anymore. Context menu is
105
- // handled in onMouseDown for Gecko.
106
- if (!gecko) connect(scroller, "contextmenu", onContextMenu);
107
- connect(scroller, "scroll", function() {
108
- lastScrollPos = scroller.scrollTop;
109
- updateDisplay([]);
110
- if (options.fixedGutter) gutter.style.left = scroller.scrollLeft + "px";
111
- if (options.onScroll) options.onScroll(instance);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
112
  });
113
- connect(window, "resize", function() {updateDisplay(true);});
114
- connect(input, "keyup", operation(onKeyUp));
115
- connect(input, "input", fastPoll);
116
- connect(input, "keydown", operation(onKeyDown));
117
- connect(input, "keypress", operation(onKeyPress));
118
- connect(input, "focus", onFocus);
119
- connect(input, "blur", onBlur);
120
-
121
- connect(scroller, "dragenter", e_stop);
122
- connect(scroller, "dragover", e_stop);
123
- connect(scroller, "drop", operation(onDrop));
124
- connect(scroller, "paste", function(){focusInput(); fastPoll();});
125
- connect(input, "paste", fastPoll);
126
- connect(input, "cut", operation(function(){
127
- if (!options.readOnly) replaceSelection("");
128
- }));
129
 
130
- // Needed to handle Tab key in KHTML
131
- if (khtml) connect(code, "mouseup", function() {
132
- if (document.activeElement == input) input.blur();
133
- focusInput();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
134
  });
 
135
 
136
- // IE throws unspecified error in certain cases, when
137
- // trying to access activeElement before onload
138
- var hasFocus; try { hasFocus = (document.activeElement == input); } catch(e) { }
139
- if (hasFocus || options.autofocus) setTimeout(onFocus, 20);
140
- else onBlur();
141
-
142
- function isLine(l) {return l >= 0 && l < doc.size;}
143
- // The instance object that we'll return. Mostly calls out to
144
- // local functions in the CodeMirror function. Some do some extra
145
- // range checking and/or clipping. operation is used to wrap the
146
- // call so that changes it makes are tracked, and the display is
147
- // updated afterwards.
148
- var instance = wrapper.CodeMirror = {
149
- getValue: getValue,
150
- setValue: operation(setValue),
151
- getSelection: getSelection,
152
- replaceSelection: operation(replaceSelection),
153
- focus: function(){window.focus(); focusInput(); onFocus(); fastPoll();},
154
- setOption: function(option, value) {
155
- var oldVal = options[option];
156
- options[option] = value;
157
- if (option == "mode" || option == "indentUnit") loadMode();
158
- else if (option == "readOnly" && value == "nocursor") {onBlur(); input.blur();}
159
- else if (option == "readOnly" && !value) {resetInput(true);}
160
- else if (option == "theme") themeChanged();
161
- else if (option == "lineWrapping" && oldVal != value) operation(wrappingChanged)();
162
- else if (option == "tabSize") updateDisplay(true);
163
- if (option == "lineNumbers" || option == "gutter" || option == "firstLineNumber" || option == "theme") {
164
- gutterChanged();
165
- updateDisplay(true);
166
- }
167
- },
168
- getOption: function(option) {return options[option];},
169
- undo: operation(undo),
170
- redo: operation(redo),
171
- indentLine: operation(function(n, dir) {
172
- if (typeof dir != "string") {
173
- if (dir == null) dir = options.smartIndent ? "smart" : "prev";
174
- else dir = dir ? "add" : "subtract";
175
- }
176
- if (isLine(n)) indentLine(n, dir);
177
- }),
178
- indentSelection: operation(indentSelected),
179
- historySize: function() {return {undo: history.done.length, redo: history.undone.length};},
180
- clearHistory: function() {history = new History();},
181
- matchBrackets: operation(function(){matchBrackets(true);}),
182
- getTokenAt: operation(function(pos) {
183
- pos = clipPos(pos);
184
- return getLine(pos.line).getTokenAt(mode, getStateBefore(pos.line), pos.ch);
185
- }),
186
- getStateAfter: function(line) {
187
- line = clipLine(line == null ? doc.size - 1: line);
188
- return getStateBefore(line + 1);
189
- },
190
- cursorCoords: function(start, mode) {
191
- if (start == null) start = sel.inverted;
192
- return this.charCoords(start ? sel.from : sel.to, mode);
193
- },
194
- charCoords: function(pos, mode) {
195
- pos = clipPos(pos);
196
- if (mode == "local") return localCoords(pos, false);
197
- if (mode == "div") return localCoords(pos, true);
198
- return pageCoords(pos);
199
- },
200
- coordsChar: function(coords) {
201
- var off = eltOffset(lineSpace);
202
- return coordsChar(coords.x - off.left, coords.y - off.top);
203
- },
204
- markText: operation(markText),
205
- setBookmark: setBookmark,
206
- findMarksAt: findMarksAt,
207
- setMarker: operation(addGutterMarker),
208
- clearMarker: operation(removeGutterMarker),
209
- setLineClass: operation(setLineClass),
210
- hideLine: operation(function(h) {return setLineHidden(h, true);}),
211
- showLine: operation(function(h) {return setLineHidden(h, false);}),
212
- onDeleteLine: function(line, f) {
213
- if (typeof line == "number") {
214
- if (!isLine(line)) return null;
215
- line = getLine(line);
216
- }
217
- (line.handlers || (line.handlers = [])).push(f);
218
- return line;
219
- },
220
- lineInfo: lineInfo,
221
- addWidget: function(pos, node, scroll, vert, horiz) {
222
- pos = localCoords(clipPos(pos));
223
- var top = pos.yBot, left = pos.x;
224
- node.style.position = "absolute";
225
- code.appendChild(node);
226
- if (vert == "over") top = pos.y;
227
- else if (vert == "near") {
228
- var vspace = Math.max(scroller.offsetHeight, doc.height * textHeight()),
229
- hspace = Math.max(code.clientWidth, lineSpace.clientWidth) - paddingLeft();
230
- if (pos.yBot + node.offsetHeight > vspace && pos.y > node.offsetHeight)
231
- top = pos.y - node.offsetHeight;
232
- if (left + node.offsetWidth > hspace)
233
- left = hspace - node.offsetWidth;
234
- }
235
- node.style.top = (top + paddingTop()) + "px";
236
- node.style.left = node.style.right = "";
237
- if (horiz == "right") {
238
- left = code.clientWidth - node.offsetWidth;
239
- node.style.right = "0px";
240
- } else {
241
- if (horiz == "left") left = 0;
242
- else if (horiz == "middle") left = (code.clientWidth - node.offsetWidth) / 2;
243
- node.style.left = (left + paddingLeft()) + "px";
244
- }
245
- if (scroll)
246
- scrollIntoView(left, top, left + node.offsetWidth, top + node.offsetHeight);
247
- },
248
-
249
- lineCount: function() {return doc.size;},
250
- clipPos: clipPos,
251
- getCursor: function(start) {
252
- if (start == null) start = sel.inverted;
253
- return copyPos(start ? sel.from : sel.to);
254
- },
255
- somethingSelected: function() {return !posEq(sel.from, sel.to);},
256
- setCursor: operation(function(line, ch, user) {
257
- if (ch == null && typeof line.line == "number") setCursor(line.line, line.ch, user);
258
- else setCursor(line, ch, user);
259
- }),
260
- setSelection: operation(function(from, to, user) {
261
- (user ? setSelectionUser : setSelection)(clipPos(from), clipPos(to || from));
262
- }),
263
- getLine: function(line) {if (isLine(line)) return getLine(line).text;},
264
- getLineHandle: function(line) {if (isLine(line)) return getLine(line);},
265
- setLine: operation(function(line, text) {
266
- if (isLine(line)) replaceRange(text, {line: line, ch: 0}, {line: line, ch: getLine(line).text.length});
267
- }),
268
- removeLine: operation(function(line) {
269
- if (isLine(line)) replaceRange("", {line: line, ch: 0}, clipPos({line: line+1, ch: 0}));
270
- }),
271
- replaceRange: operation(replaceRange),
272
- getRange: function(from, to) {return getRange(clipPos(from), clipPos(to));},
273
-
274
- triggerOnKeyDown: operation(onKeyDown),
275
- execCommand: function(cmd) {return commands[cmd](instance);},
276
- // Stuff used by commands, probably not much use to outside code.
277
- moveH: operation(moveH),
278
- deleteH: operation(deleteH),
279
- moveV: operation(moveV),
280
- toggleOverwrite: function() {
281
- if(overwrite){
282
- overwrite = false;
283
- cursor.className = cursor.className.replace(" CodeMirror-overwrite", "");
284
- } else {
285
- overwrite = true;
286
- cursor.className += " CodeMirror-overwrite";
287
- }
288
- },
289
-
290
- posFromIndex: function(off) {
291
- var lineNo = 0, ch;
292
- doc.iter(0, doc.size, function(line) {
293
- var sz = line.text.length + 1;
294
- if (sz > off) { ch = off; return true; }
295
- off -= sz;
296
- ++lineNo;
297
- });
298
- return clipPos({line: lineNo, ch: ch});
299
- },
300
- indexFromPos: function (coords) {
301
- if (coords.line < 0 || coords.ch < 0) return 0;
302
- var index = coords.ch;
303
- doc.iter(0, coords.line, function (line) {
304
- index += line.text.length + 1;
305
- });
306
- return index;
307
- },
308
- scrollTo: function(x, y) {
309
- if (x != null) scroller.scrollLeft = x;
310
- if (y != null) scroller.scrollTop = y;
311
- updateDisplay([]);
312
- },
313
-
314
- operation: function(f){return operation(f)();},
315
- refresh: function(){
316
- updateDisplay(true);
317
- if (scroller.scrollHeight > lastScrollPos)
318
- scroller.scrollTop = lastScrollPos;
319
- },
320
- getInputField: function(){return input;},
321
- getWrapperElement: function(){return wrapper;},
322
- getScrollerElement: function(){return scroller;},
323
- getGutterElement: function(){return gutter;}
324
- };
325
 
326
- function getLine(n) { return getLineAt(doc, n); }
327
- function updateLineHeight(line, height) {
328
- gutterDirty = true;
329
- var diff = height - line.height;
330
- for (var n = line; n; n = n.parent) n.height += diff;
 
 
331
  }
 
 
 
332
 
333
- function setValue(code) {
334
- var top = {line: 0, ch: 0};
335
- updateLines(top, {line: doc.size - 1, ch: getLine(doc.size-1).text.length},
336
- splitLines(code), top, top);
337
- updateInput = true;
 
 
 
 
 
 
 
 
338
  }
339
- function getValue(code) {
340
- var text = [];
341
- doc.iter(0, doc.size, function(line) { text.push(line.text); });
342
- return text.join("\n");
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
343
  }
 
 
 
 
 
344
 
345
- function onMouseDown(e) {
346
- setShift(e_prop(e, "shiftKey"));
347
- // Check whether this is a click in a widget
348
- for (var n = e_target(e); n != wrapper; n = n.parentNode)
349
- if (n.parentNode == code && n != mover) return;
 
 
 
 
350
 
351
- // See if this is a click in the gutter
352
- for (var n = e_target(e); n != wrapper; n = n.parentNode)
353
- if (n.parentNode == gutterText) {
354
- if (options.onGutterClick)
355
- options.onGutterClick(instance, indexOf(gutterText.childNodes, n) + showingFrom, e);
356
- return e_preventDefault(e);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
357
  }
 
358
 
359
- var start = posFromMouse(e);
 
 
 
 
 
 
 
 
 
 
 
 
 
360
 
361
- switch (e_button(e)) {
362
- case 3:
363
- if (gecko && !mac) onContextMenu(e);
364
- return;
365
- case 2:
366
- if (start) setCursor(start.line, start.ch, true);
367
- return;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
368
  }
369
- // For button 1, if it was clicked inside the editor
370
- // (posFromMouse returning non-null), we have to adjust the
371
- // selection.
372
- if (!start) {if (e_target(e) == scroller) e_preventDefault(e); return;}
373
-
374
- if (!focused) onFocus();
375
-
376
- var now = +new Date;
377
- if (lastDoubleClick && lastDoubleClick.time > now - 400 && posEq(lastDoubleClick.pos, start)) {
378
- e_preventDefault(e);
379
- setTimeout(focusInput, 20);
380
- return selectLine(start.line);
381
- } else if (lastClick && lastClick.time > now - 400 && posEq(lastClick.pos, start)) {
382
- lastDoubleClick = {time: now, pos: start};
383
- e_preventDefault(e);
384
- return selectWordAt(start);
385
- } else { lastClick = {time: now, pos: start}; }
386
-
387
- var last = start, going;
388
- if (dragAndDrop && !options.readOnly && !posEq(sel.from, sel.to) &&
389
- !posLess(start, sel.from) && !posLess(sel.to, start)) {
390
- // Let the drag handler handle this.
391
- if (webkit) lineSpace.draggable = true;
392
- var up = connect(document, "mouseup", operation(function(e2) {
393
- if (webkit) lineSpace.draggable = false;
394
- draggingText = false;
395
- up();
396
- if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) {
397
- e_preventDefault(e2);
398
- setCursor(start.line, start.ch, true);
399
- focusInput();
400
- }
401
- }), true);
402
- draggingText = true;
403
- // IE's approach to draggable
404
- if (lineSpace.dragDrop) lineSpace.dragDrop();
405
- return;
406
  }
407
- e_preventDefault(e);
408
- setCursor(start.line, start.ch, true);
409
-
410
- function extend(e) {
411
- var cur = posFromMouse(e, true);
412
- if (cur && !posEq(cur, last)) {
413
- if (!focused) onFocus();
414
- last = cur;
415
- setSelectionUser(start, cur);
416
- updateInput = false;
417
- var visible = visibleLines();
418
- if (cur.line >= visible.to || cur.line < visible.from)
419
- going = setTimeout(operation(function(){extend(e);}), 150);
 
 
 
 
 
 
 
 
 
 
 
420
  }
421
  }
 
 
 
 
422
 
423
- function done(e) {
424
- clearTimeout(going);
425
- var cur = posFromMouse(e);
426
- if (cur) setSelectionUser(start, cur);
427
- e_preventDefault(e);
428
- focusInput();
429
- updateInput = true;
430
- move(); up();
431
- }
432
- var move = connect(document, "mousemove", operation(function(e) {
433
- clearTimeout(going);
434
- e_preventDefault(e);
435
- if (!ie && !e_button(e)) done(e);
436
- else extend(e);
437
- }), true);
438
- var up = connect(document, "mouseup", operation(done), true);
439
- }
440
- function onDoubleClick(e) {
441
- for (var n = e_target(e); n != wrapper; n = n.parentNode)
442
- if (n.parentNode == gutterText) return e_preventDefault(e);
443
- var start = posFromMouse(e);
444
- if (!start) return;
445
- lastDoubleClick = {time: +new Date, pos: start};
446
- e_preventDefault(e);
447
- selectWordAt(start);
448
- }
449
- function onDrop(e) {
450
- e.preventDefault();
451
- var pos = posFromMouse(e, true), files = e.dataTransfer.files;
452
- if (!pos || options.readOnly) return;
453
- if (files && files.length && window.FileReader && window.File) {
454
- function loadFile(file, i) {
455
- var reader = new FileReader;
456
- reader.onload = function() {
457
- text[i] = reader.result;
458
- if (++read == n) {
459
- pos = clipPos(pos);
460
- operation(function() {
461
- var end = replaceRange(text.join(""), pos, pos);
462
- setSelectionUser(pos, end);
463
- })();
464
- }
465
- };
466
- reader.readAsText(file);
467
- }
468
- var n = files.length, text = Array(n), read = 0;
469
- for (var i = 0; i < n; ++i) loadFile(files[i], i);
470
- }
471
- else {
472
- try {
473
- var text = e.dataTransfer.getData("Text");
474
- if (text) {
475
- var curFrom = sel.from, curTo = sel.to;
476
- setSelectionUser(pos, pos);
477
- if (draggingText) replaceRange("", curFrom, curTo);
478
- replaceSelection(text);
479
- focusInput();
480
- }
481
- }
482
- catch(e){}
483
  }
 
484
  }
485
- function onDragStart(e) {
486
- var txt = getSelection();
487
- e.dataTransfer.setData("Text", txt);
488
-
489
- // Use dummy image instead of default browsers image.
490
- if (gecko || chrome) {
491
- var img = document.createElement('img');
492
- img.scr = 'data:image/gif;base64,R0lGODdhAgACAIAAAAAAAP///ywAAAAAAgACAAACAoRRADs='; //1x1 image
493
- e.dataTransfer.setDragImage(img, 0, 0);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
494
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
495
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
496
 
497
- function doHandleBinding(bound, dropShift) {
498
- if (typeof bound == "string") {
499
- bound = commands[bound];
500
- if (!bound) return false;
 
 
 
 
 
 
501
  }
502
- var prevShift = shiftSelecting;
503
- try {
504
- if (options.readOnly) suppressEdits = true;
505
- if (dropShift) shiftSelecting = null;
506
- bound(instance);
507
- } catch(e) {
508
- if (e != Pass) throw e;
509
- return false;
510
- } finally {
511
- shiftSelecting = prevShift;
512
- suppressEdits = false;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
513
  }
514
- return true;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
515
  }
516
- function handleKeyBinding(e) {
517
- // Handle auto keymap transitions
518
- var startMap = getKeyMap(options.keyMap), next = startMap.auto;
519
- clearTimeout(maybeTransition);
520
- if (next && !isModifierKey(e)) maybeTransition = setTimeout(function() {
521
- if (getKeyMap(options.keyMap) == startMap) {
522
- options.keyMap = (next.call ? next.call(null, instance) : next);
 
 
 
 
 
 
523
  }
524
- }, 50);
525
-
526
- var name = keyNames[e_prop(e, "keyCode")], handled = false;
527
- if (name == null || e.altGraphKey) return false;
528
- if (e_prop(e, "altKey")) name = "Alt-" + name;
529
- if (e_prop(e, "ctrlKey")) name = "Ctrl-" + name;
530
- if (e_prop(e, "metaKey")) name = "Cmd-" + name;
531
-
532
- if (e_prop(e, "shiftKey")) {
533
- handled = lookupKey("Shift-" + name, options.extraKeys, options.keyMap,
534
- function(b) {return doHandleBinding(b, true);})
535
- || lookupKey(name, options.extraKeys, options.keyMap, function(b) {
536
- if (typeof b == "string" && /^go[A-Z]/.test(b)) return doHandleBinding(b);
537
- });
538
  } else {
539
- handled = lookupKey(name, options.extraKeys, options.keyMap, doHandleBinding);
540
- }
541
- if (handled) {
542
- e_preventDefault(e);
543
- if (ie) { e.oldKeyCode = e.keyCode; e.keyCode = 0; }
544
- }
545
- return handled;
546
- }
547
- function handleCharBinding(e, ch) {
548
- var handled = lookupKey("'" + ch + "'", options.extraKeys,
549
- options.keyMap, doHandleBinding);
550
- if (handled) e_preventDefault(e);
551
- return handled;
552
- }
553
-
554
- var lastStoppedKey = null, maybeTransition;
555
- function onKeyDown(e) {
556
- if (!focused) onFocus();
557
- if (ie && e.keyCode == 27) { e.returnValue = false; }
558
- if (pollingFast) { if (readInput()) pollingFast = false; }
559
- if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return;
560
- var code = e_prop(e, "keyCode");
561
- // IE does strange things with escape.
562
- setShift(code == 16 || e_prop(e, "shiftKey"));
563
- // First give onKeyEvent option a chance to handle this.
564
- var handled = handleKeyBinding(e);
565
- if (window.opera) {
566
- lastStoppedKey = handled ? code : null;
567
- // Opera has no cut event... we try to at least catch the key combo
568
- if (!handled && code == 88 && e_prop(e, mac ? "metaKey" : "ctrlKey"))
569
- replaceSelection("");
570
- }
571
- }
572
- function onKeyPress(e) {
573
- if (pollingFast) readInput();
574
- if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return;
575
- var keyCode = e_prop(e, "keyCode"), charCode = e_prop(e, "charCode");
576
- if (window.opera && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;}
577
- if (((window.opera && !e.which) || khtml) && handleKeyBinding(e)) return;
578
- var ch = String.fromCharCode(charCode == null ? keyCode : charCode);
579
- if (options.electricChars && mode.electricChars && options.smartIndent && !options.readOnly) {
580
- if (mode.electricChars.indexOf(ch) > -1)
581
- setTimeout(operation(function() {indentLine(sel.to.line, "smart");}), 75);
582
- }
583
- if (handleCharBinding(e, ch)) return;
584
- fastPoll();
585
- }
586
- function onKeyUp(e) {
587
- if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return;
588
- if (e_prop(e, "keyCode") == 16) shiftSelecting = null;
589
- }
590
-
591
- function onFocus() {
592
- if (options.readOnly == "nocursor") return;
593
- if (!focused) {
594
- if (options.onFocus) options.onFocus(instance);
595
- focused = true;
596
- if (wrapper.className.search(/\bCodeMirror-focused\b/) == -1)
597
- wrapper.className += " CodeMirror-focused";
598
- if (!leaveInputAlone) resetInput(true);
599
- }
600
- slowPoll();
601
- restartBlink();
602
- }
603
- function onBlur() {
604
- if (focused) {
605
- if (options.onBlur) options.onBlur(instance);
606
- focused = false;
607
- if (bracketHighlighted)
608
- operation(function(){
609
- if (bracketHighlighted) { bracketHighlighted(); bracketHighlighted = null; }
610
- })();
611
- wrapper.className = wrapper.className.replace(" CodeMirror-focused", "");
612
- }
613
- clearInterval(blinker);
614
- setTimeout(function() {if (!focused) shiftSelecting = null;}, 150);
615
- }
616
-
617
- // Replace the range from from to to by the strings in newText.
618
- // Afterwards, set the selection to selFrom, selTo.
619
- function updateLines(from, to, newText, selFrom, selTo) {
620
- if (suppressEdits) return;
621
- if (history) {
622
- var old = [];
623
- doc.iter(from.line, to.line + 1, function(line) { old.push(line.text); });
624
- history.addChange(from.line, newText.length, old);
625
- while (history.done.length > options.undoDepth) history.done.shift();
626
- }
627
- updateLinesNoUndo(from, to, newText, selFrom, selTo);
628
- }
629
- function unredoHelper(from, to) {
630
- if (!from.length) return;
631
- var set = from.pop(), out = [];
632
- for (var i = set.length - 1; i >= 0; i -= 1) {
633
- var change = set[i];
634
- var replaced = [], end = change.start + change.added;
635
- doc.iter(change.start, end, function(line) { replaced.push(line.text); });
636
- out.push({start: change.start, added: change.old.length, old: replaced});
637
- var pos = clipPos({line: change.start + change.old.length - 1,
638
- ch: editEnd(replaced[replaced.length-1], change.old[change.old.length-1])});
639
- updateLinesNoUndo({line: change.start, ch: 0}, {line: end - 1, ch: getLine(end-1).text.length}, change.old, pos, pos);
640
- }
641
- updateInput = true;
642
- to.push(out);
643
- }
644
- function undo() {unredoHelper(history.done, history.undone);}
645
- function redo() {unredoHelper(history.undone, history.done);}
646
-
647
- function updateLinesNoUndo(from, to, newText, selFrom, selTo) {
648
- if (suppressEdits) return;
649
- var recomputeMaxLength = false, maxLineLength = maxLine.length;
650
- if (!options.lineWrapping)
651
- doc.iter(from.line, to.line, function(line) {
652
- if (line.text.length == maxLineLength) {recomputeMaxLength = true; return true;}
653
- });
654
- if (from.line != to.line || newText.length > 1) gutterDirty = true;
655
-
656
- var nlines = to.line - from.line, firstLine = getLine(from.line), lastLine = getLine(to.line);
657
- // First adjust the line structure, taking some care to leave highlighting intact.
658
- if (from.ch == 0 && to.ch == 0 && newText[newText.length - 1] == "") {
659
- // This is a whole-line replace. Treated specially to make
660
- // sure line objects move the way they are supposed to.
661
- var added = [], prevLine = null;
662
- if (from.line) {
663
- prevLine = getLine(from.line - 1);
664
- prevLine.fixMarkEnds(lastLine);
665
- } else lastLine.fixMarkStarts();
666
- for (var i = 0, e = newText.length - 1; i < e; ++i)
667
- added.push(Line.inheritMarks(newText[i], prevLine));
668
- if (nlines) doc.remove(from.line, nlines, callbacks);
669
- if (added.length) doc.insert(from.line, added);
670
- } else if (firstLine == lastLine) {
671
- if (newText.length == 1)
672
- firstLine.replace(from.ch, to.ch, newText[0]);
673
- else {
674
- lastLine = firstLine.split(to.ch, newText[newText.length-1]);
675
- firstLine.replace(from.ch, null, newText[0]);
676
- firstLine.fixMarkEnds(lastLine);
677
- var added = [];
678
- for (var i = 1, e = newText.length - 1; i < e; ++i)
679
- added.push(Line.inheritMarks(newText[i], firstLine));
680
- added.push(lastLine);
681
- doc.insert(from.line + 1, added);
682
  }
683
- } else if (newText.length == 1) {
684
- firstLine.replace(from.ch, null, newText[0]);
685
- lastLine.replace(null, to.ch, "");
686
- firstLine.append(lastLine);
687
- doc.remove(from.line + 1, nlines, callbacks);
688
  } else {
689
- var added = [];
690
- firstLine.replace(from.ch, null, newText[0]);
691
- lastLine.replace(null, to.ch, newText[newText.length-1]);
692
- firstLine.fixMarkEnds(lastLine);
693
- for (var i = 1, e = newText.length - 1; i < e; ++i)
694
- added.push(Line.inheritMarks(newText[i], firstLine));
695
- if (nlines > 1) doc.remove(from.line + 1, nlines - 1, callbacks);
696
- doc.insert(from.line + 1, added);
697
  }
698
- if (options.lineWrapping) {
699
- var perLine = scroller.clientWidth / charWidth() - 3;
700
- doc.iter(from.line, from.line + newText.length, function(line) {
701
- if (line.hidden) return;
702
- var guess = Math.ceil(line.text.length / perLine) || 1;
703
- if (guess != line.height) updateLineHeight(line, guess);
704
- });
705
- } else {
706
- doc.iter(from.line, i + newText.length, function(line) {
707
- var l = line.text;
708
- if (l.length > maxLineLength) {
709
- maxLine = l; maxLineLength = l.length; maxWidth = null;
710
- recomputeMaxLength = false;
711
- }
712
- });
713
- if (recomputeMaxLength) {
714
- maxLineLength = 0; maxLine = ""; maxWidth = null;
715
- doc.iter(0, doc.size, function(line) {
716
- var l = line.text;
717
- if (l.length > maxLineLength) {
718
- maxLineLength = l.length; maxLine = l;
719
- }
720
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
721
  }
 
722
  }
 
 
723
 
724
- // Add these lines to the work array, so that they will be
725
- // highlighted. Adjust work lines if lines were added/removed.
726
- var newWork = [], lendiff = newText.length - nlines - 1;
727
- for (var i = 0, l = work.length; i < l; ++i) {
728
- var task = work[i];
729
- if (task < from.line) newWork.push(task);
730
- else if (task > to.line) newWork.push(task + lendiff);
731
- }
732
- var hlEnd = from.line + Math.min(newText.length, 500);
733
- highlightLines(from.line, hlEnd);
734
- newWork.push(hlEnd);
735
- work = newWork;
736
- startWorker(100);
737
- // Remember that these lines changed, for updating the display
738
- changes.push({from: from.line, to: to.line + 1, diff: lendiff});
739
- var changeObj = {from: from, to: to, text: newText};
740
- if (textChanged) {
741
- for (var cur = textChanged; cur.next; cur = cur.next) {}
742
- cur.next = changeObj;
743
- } else textChanged = changeObj;
 
 
 
 
 
 
 
744
 
745
- // Update the selection
746
- function updateLine(n) {return n <= Math.min(to.line, to.line + lendiff) ? n : n + lendiff;}
747
- setSelection(selFrom, selTo, updateLine(sel.from.line), updateLine(sel.to.line));
 
 
748
 
749
- // Make sure the scroll-size div has the correct height.
750
- if (scroller.clientHeight)
751
- code.style.height = (doc.height * textHeight() + 2 * paddingTop()) + "px";
 
 
752
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
753
 
754
- function replaceRange(code, from, to) {
755
- from = clipPos(from);
756
- if (!to) to = from; else to = clipPos(to);
757
- code = splitLines(code);
758
- function adjustPos(pos) {
759
- if (posLess(pos, from)) return pos;
760
- if (!posLess(to, pos)) return end;
761
- var line = pos.line + code.length - (to.line - from.line) - 1;
762
- var ch = pos.ch;
763
- if (pos.line == to.line)
764
- ch += code[code.length-1].length - (to.ch - (to.line == from.line ? from.ch : 0));
765
- return {line: line, ch: ch};
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
766
  }
767
- var end;
768
- replaceRange1(code, from, to, function(end1) {
769
- end = end1;
770
- return {from: adjustPos(sel.from), to: adjustPos(sel.to)};
771
- });
772
- return end;
773
  }
774
- function replaceSelection(code, collapse) {
775
- replaceRange1(splitLines(code), sel.from, sel.to, function(end) {
776
- if (collapse == "end") return {from: end, to: end};
777
- else if (collapse == "start") return {from: sel.from, to: sel.from};
778
- else return {from: sel.from, to: end};
779
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
780
  }
781
- function replaceRange1(code, from, to, computeSel) {
782
- var endch = code.length == 1 ? code[0].length + from.ch : code[code.length-1].length;
783
- var newSel = computeSel({line: from.line + code.length - 1, ch: endch});
784
- updateLines(from, to, code, newSel.from, newSel.to);
 
 
 
 
 
 
 
 
 
 
 
785
  }
786
 
787
- function getRange(from, to) {
788
- var l1 = from.line, l2 = to.line;
789
- if (l1 == l2) return getLine(l1).text.slice(from.ch, to.ch);
790
- var code = [getLine(l1).text.slice(from.ch)];
791
- doc.iter(l1 + 1, l2, function(line) { code.push(line.text); });
792
- code.push(getLine(l2).text.slice(0, to.ch));
793
- return code.join("\n");
 
 
 
 
 
 
 
 
 
 
 
 
 
794
  }
795
- function getSelection() {
796
- return getRange(sel.from, sel.to);
 
 
 
 
 
 
 
 
 
 
 
 
797
  }
 
 
 
 
 
 
798
 
799
- var pollingFast = false; // Ensures slowPoll doesn't cancel fastPoll
800
- function slowPoll() {
801
- if (pollingFast) return;
802
- poll.set(options.pollInterval, function() {
803
- startOperation();
804
- readInput();
805
- if (focused) slowPoll();
806
- endOperation();
807
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
808
  }
809
- function fastPoll() {
810
- var missed = false;
811
- pollingFast = true;
812
- function p() {
813
- startOperation();
814
- var changed = readInput();
815
- if (!changed && !missed) {missed = true; poll.set(60, p);}
816
- else {pollingFast = false; slowPoll();}
817
- endOperation();
818
- }
819
- poll.set(20, p);
820
- }
821
-
822
- // Previnput is a hack to work with IME. If we reset the textarea
823
- // on every change, that breaks IME. So we look for changes
824
- // compared to the previous content instead. (Modern browsers have
825
- // events that indicate IME taking place, but these are not widely
826
- // supported or compatible enough yet to rely on.)
827
- var prevInput = "";
828
- function readInput() {
829
- if (leaveInputAlone || !focused || hasSelection(input) || options.readOnly) return false;
830
- var text = input.value;
831
- if (text == prevInput) return false;
832
- shiftSelecting = null;
833
- var same = 0, l = Math.min(prevInput.length, text.length);
834
- while (same < l && prevInput[same] == text[same]) ++same;
835
- if (same < prevInput.length)
836
- sel.from = {line: sel.from.line, ch: sel.from.ch - (prevInput.length - same)};
837
- else if (overwrite && posEq(sel.from, sel.to))
838
- sel.to = {line: sel.to.line, ch: Math.min(getLine(sel.to.line).text.length, sel.to.ch + (text.length - same))};
839
- replaceSelection(text.slice(same), "end");
840
- prevInput = text;
841
- return true;
842
  }
843
- function resetInput(user) {
844
- if (!posEq(sel.from, sel.to)) {
845
- prevInput = "";
846
- input.value = getSelection();
847
- selectInput(input);
848
- } else if (user) prevInput = input.value = "";
849
- }
850
-
851
- function focusInput() {
852
- if (options.readOnly != "nocursor") input.focus();
853
- }
854
-
855
- function scrollEditorIntoView() {
856
- if (!cursor.getBoundingClientRect) return;
857
- var rect = cursor.getBoundingClientRect();
858
- // IE returns bogus coordinates when the instance sits inside of an iframe and the cursor is hidden
859
- if (ie && rect.top == rect.bottom) return;
860
- var winH = window.innerHeight || Math.max(document.body.offsetHeight, document.documentElement.offsetHeight);
861
- if (rect.top < 0 || rect.bottom > winH) cursor.scrollIntoView();
862
- }
863
- function scrollCursorIntoView() {
864
- var cursor = localCoords(sel.inverted ? sel.from : sel.to);
865
- var x = options.lineWrapping ? Math.min(cursor.x, lineSpace.offsetWidth) : cursor.x;
866
- return scrollIntoView(x, cursor.y, x, cursor.yBot);
867
- }
868
- function scrollIntoView(x1, y1, x2, y2) {
869
- var pl = paddingLeft(), pt = paddingTop();
870
- y1 += pt; y2 += pt; x1 += pl; x2 += pl;
871
- var screen = scroller.clientHeight, screentop = scroller.scrollTop, scrolled = false, result = true;
872
- if (y1 < screentop) {scroller.scrollTop = Math.max(0, y1); scrolled = true;}
873
- else if (y2 > screentop + screen) {scroller.scrollTop = y2 - screen; scrolled = true;}
874
-
875
- var screenw = scroller.clientWidth, screenleft = scroller.scrollLeft;
876
- var gutterw = options.fixedGutter ? gutter.clientWidth : 0;
877
- if (x1 < screenleft + gutterw) {
878
- if (x1 < 50) x1 = 0;
879
- scroller.scrollLeft = Math.max(0, x1 - 10 - gutterw);
880
- scrolled = true;
881
- }
882
- else if (x2 > screenw + screenleft - 3) {
883
- scroller.scrollLeft = x2 + 10 - screenw;
884
- scrolled = true;
885
- if (x2 > code.clientWidth) result = false;
886
- }
887
- if (scrolled && options.onScroll) options.onScroll(instance);
888
  return result;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
889
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
890
 
891
- function visibleLines() {
892
- var lh = textHeight(), top = scroller.scrollTop - paddingTop();
893
- var from_height = Math.max(0, Math.floor(top / lh));
894
- var to_height = Math.ceil((top + scroller.clientHeight) / lh);
895
- return {from: lineAtHeight(doc, from_height),
896
- to: lineAtHeight(doc, to_height)};
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
897
  }
898
- // Uses a set of changes plus the current scroll position to
899
- // determine which DOM updates have to be made, and makes the
900
- // updates.
901
- function updateDisplay(changes, suppressCallback) {
902
- if (!scroller.clientWidth) {
903
- showingFrom = showingTo = displayOffset = 0;
904
- return;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
905
  }
906
- // Compute the new visible window
907
- var visible = visibleLines();
908
- // Bail out if the visible area is already rendered and nothing changed.
909
- if (changes !== true && changes.length == 0 && visible.from > showingFrom && visible.to < showingTo) return;
910
- var from = Math.max(visible.from - 100, 0), to = Math.min(doc.size, visible.to + 100);
911
- if (showingFrom < from && from - showingFrom < 20) from = showingFrom;
912
- if (showingTo > to && showingTo - to < 20) to = Math.min(doc.size, showingTo);
913
-
914
- // Create a range of theoretically intact lines, and punch holes
915
- // in that using the change info.
916
- var intact = changes === true ? [] :
917
- computeIntact([{from: showingFrom, to: showingTo, domStart: 0}], changes);
918
- // Clip off the parts that won't be visible
919
- var intactLines = 0;
920
- for (var i = 0; i < intact.length; ++i) {
921
- var range = intact[i];
922
- if (range.from < from) {range.domStart += (from - range.from); range.from = from;}
923
- if (range.to > to) range.to = to;
924
- if (range.from >= range.to) intact.splice(i--, 1);
925
- else intactLines += range.to - range.from;
926
- }
927
- if (intactLines == to - from) return;
928
- intact.sort(function(a, b) {return a.domStart - b.domStart;});
929
-
930
- var th = textHeight(), gutterDisplay = gutter.style.display;
931
- lineDiv.style.display = "none";
932
- patchDisplay(from, to, intact);
933
- lineDiv.style.display = gutter.style.display = "";
934
-
935
- // Position the mover div to align with the lines it's supposed
936
- // to be showing (which will cover the visible display)
937
- var different = from != showingFrom || to != showingTo || lastSizeC != scroller.clientHeight + th;
938
- // This is just a bogus formula that detects when the editor is
939
- // resized or the font size changes.
940
- if (different) lastSizeC = scroller.clientHeight + th;
941
- showingFrom = from; showingTo = to;
942
- displayOffset = heightAtLine(doc, from);
943
- mover.style.top = (displayOffset * th) + "px";
944
- if (scroller.clientHeight)
945
- code.style.height = (doc.height * th + 2 * paddingTop()) + "px";
946
-
947
- // Since this is all rather error prone, it is honoured with the
948
- // only assertion in the whole file.
949
- if (lineDiv.childNodes.length != showingTo - showingFrom)
950
- throw new Error("BAD PATCH! " + JSON.stringify(intact) + " size=" + (showingTo - showingFrom) +
951
- " nodes=" + lineDiv.childNodes.length);
952
-
953
- function checkHeights() {
954
- maxWidth = scroller.clientWidth;
955
- var curNode = lineDiv.firstChild, heightChanged = false;
956
- doc.iter(showingFrom, showingTo, function(line) {
957
- if (!line.hidden) {
958
- var height = Math.round(curNode.offsetHeight / th) || 1;
959
- if (line.height != height) {
960
- updateLineHeight(line, height);
961
- gutterDirty = heightChanged = true;
962
- }
963
- }
964
- curNode = curNode.nextSibling;
965
- });
966
- if (heightChanged)
967
- code.style.height = (doc.height * th + 2 * paddingTop()) + "px";
968
- return heightChanged;
969
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
970
 
971
- if (options.lineWrapping) {
972
- checkHeights();
973
- } else {
974
- if (maxWidth == null) maxWidth = stringWidth(maxLine);
975
- if (maxWidth > scroller.clientWidth) {
976
- lineSpace.style.width = maxWidth + "px";
977
- // Needed to prevent odd wrapping/hiding of widgets placed in here.
978
- code.style.width = "";
979
- code.style.width = scroller.scrollWidth + "px";
980
- } else {
981
- lineSpace.style.width = code.style.width = "";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
982
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
983
  }
984
 
985
- gutter.style.display = gutterDisplay;
986
- if (different || gutterDirty) {
987
- // If the gutter grew in size, re-check heights. If those changed, re-draw gutter.
988
- updateGutter() && options.lineWrapping && checkHeights() && updateGutter();
 
 
 
 
 
989
  }
990
- updateSelection();
991
- if (!suppressCallback && options.onUpdate) options.onUpdate(instance);
992
- return true;
993
  }
994
 
995
- function computeIntact(intact, changes) {
996
- for (var i = 0, l = changes.length || 0; i < l; ++i) {
997
- var change = changes[i], intact2 = [], diff = change.diff || 0;
998
- for (var j = 0, l2 = intact.length; j < l2; ++j) {
999
- var range = intact[j];
1000
- if (change.to <= range.from && change.diff)
1001
- intact2.push({from: range.from + diff, to: range.to + diff,
1002
- domStart: range.domStart});
1003
- else if (change.to <= range.from || change.from >= range.to)
1004
- intact2.push(range);
1005
- else {
1006
- if (change.from > range.from)
1007
- intact2.push({from: range.from, to: change.from, domStart: range.domStart});
1008
- if (change.to < range.to)
1009
- intact2.push({from: change.to + diff, to: range.to + diff,
1010
- domStart: range.domStart + (change.to - range.from)});
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1011
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1012
  }
1013
- intact = intact2;
1014
  }
1015
- return intact;
1016
  }
 
1017
 
1018
- function patchDisplay(from, to, intact) {
1019
- // The first pass removes the DOM nodes that aren't intact.
1020
- if (!intact.length) lineDiv.innerHTML = "";
1021
- else {
1022
- function killNode(node) {
1023
- var tmp = node.nextSibling;
1024
- node.parentNode.removeChild(node);
1025
- return tmp;
1026
- }
1027
- var domPos = 0, curNode = lineDiv.firstChild, n;
1028
- for (var i = 0; i < intact.length; ++i) {
1029
- var cur = intact[i];
1030
- while (cur.domStart > domPos) {curNode = killNode(curNode); domPos++;}
1031
- for (var j = 0, e = cur.to - cur.from; j < e; ++j) {curNode = curNode.nextSibling; domPos++;}
1032
- }
1033
- while (curNode) curNode = killNode(curNode);
1034
- }
1035
- // This pass fills in the lines that actually changed.
1036
- var nextIntact = intact.shift(), curNode = lineDiv.firstChild, j = from;
1037
- var scratch = document.createElement("div");
1038
- doc.iter(from, to, function(line) {
1039
- if (nextIntact && nextIntact.to == j) nextIntact = intact.shift();
1040
- if (!nextIntact || nextIntact.from > j) {
1041
- if (line.hidden) var html = scratch.innerHTML = "<pre></pre>";
1042
- else {
1043
- var html = '<pre' + (line.className ? ' class="' + line.className + '"' : '') + '>'
1044
- + line.getHTML(makeTab) + '</pre>';
1045
- // Kludge to make sure the styled element lies behind the selection (by z-index)
1046
- if (line.bgClassName)
1047
- html = '<div style="position: relative"><pre class="' + line.bgClassName +
1048
- '" style="position: absolute; left: 0; right: 0; top: 0; bottom: 0; z-index: -2">&#160;</pre>' + html + "</div>";
1049
- }
1050
- scratch.innerHTML = html;
1051
- lineDiv.insertBefore(scratch.firstChild, curNode);
1052
- } else {
1053
- curNode = curNode.nextSibling;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1054
  }
1055
- ++j;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1056
  });
 
 
1057
  }
 
1058
 
1059
- function updateGutter() {
1060
- if (!options.gutter && !options.lineNumbers) return;
1061
- var hText = mover.offsetHeight, hEditor = scroller.clientHeight;
1062
- gutter.style.height = (hText - hEditor < 2 ? hEditor : hText) + "px";
1063
- var html = [], i = showingFrom, normalNode;
1064
- doc.iter(showingFrom, Math.max(showingTo, showingFrom + 1), function(line) {
1065
- if (line.hidden) {
1066
- html.push("<pre></pre>");
1067
- } else {
1068
- var marker = line.gutterMarker;
1069
- var text = options.lineNumbers ? i + options.firstLineNumber : null;
1070
- if (marker && marker.text)
1071
- text = marker.text.replace("%N%", text != null ? text : "");
1072
- else if (text == null)
1073
- text = "\u00a0";
1074
- html.push((marker && marker.style ? '<pre class="' + marker.style + '">' : "<pre>"), text);
1075
- for (var j = 1; j < line.height; ++j) html.push("<br/>&#160;");
1076
- html.push("</pre>");
1077
- if (!marker) normalNode = i;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1078
  }
1079
- ++i;
1080
  });
1081
- gutter.style.display = "none";
1082
- gutterText.innerHTML = html.join("");
1083
- // Make sure scrolling doesn't cause number gutter size to pop
1084
- if (normalNode != null) {
1085
- var node = gutterText.childNodes[normalNode - showingFrom];
1086
- var minwidth = String(doc.size).length, val = eltText(node), pad = "";
1087
- while (val.length + pad.length < minwidth) pad += "\u00a0";
1088
- if (pad) node.insertBefore(document.createTextNode(pad), node.firstChild);
1089
- }
1090
- gutter.style.display = "";
1091
- var resized = Math.abs((parseInt(lineSpace.style.marginLeft) || 0) - gutter.offsetWidth) > 2;
1092
- lineSpace.style.marginLeft = gutter.offsetWidth + "px";
1093
- gutterDirty = false;
1094
- return resized;
1095
- }
1096
- function updateSelection() {
1097
- var collapsed = posEq(sel.from, sel.to);
1098
- var fromPos = localCoords(sel.from, true);
1099
- var toPos = collapsed ? fromPos : localCoords(sel.to, true);
1100
- var headPos = sel.inverted ? fromPos : toPos, th = textHeight();
1101
- var wrapOff = eltOffset(wrapper), lineOff = eltOffset(lineDiv);
1102
- inputDiv.style.top = Math.max(0, Math.min(scroller.offsetHeight, headPos.y + lineOff.top - wrapOff.top)) + "px";
1103
- inputDiv.style.left = Math.max(0, Math.min(scroller.offsetWidth, headPos.x + lineOff.left - wrapOff.left)) + "px";
1104
- if (collapsed) {
1105
- cursor.style.top = headPos.y + "px";
1106
- cursor.style.left = (options.lineWrapping ? Math.min(headPos.x, lineSpace.offsetWidth) : headPos.x) + "px";
1107
- cursor.style.display = "";
1108
- selectionDiv.style.display = "none";
1109
  } else {
1110
- var sameLine = fromPos.y == toPos.y, html = "";
1111
- function add(left, top, right, height) {
1112
- html += '<div class="CodeMirror-selected" style="position: absolute; left: ' + left +
1113
- 'px; top: ' + top + 'px; right: ' + right + 'px; height: ' + height + 'px"></div>';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1114
  }
1115
- var clientWidth = lineSpace.clientWidth || lineSpace.offsetWidth;
1116
- var clientHeight = lineSpace.clientHeight || lineSpace.offsetHeight;
1117
- if (sel.from.ch && fromPos.y >= 0) {
1118
- var right = sameLine ? clientWidth - toPos.x : 0;
1119
- add(fromPos.x, fromPos.y, right, th);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1120
  }
1121
- var middleStart = Math.max(0, fromPos.y + (sel.from.ch ? th : 0));
1122
- var middleHeight = Math.min(toPos.y, clientHeight) - middleStart;
1123
- if (middleHeight > 0.2 * th)
1124
- add(0, middleStart, 0, middleHeight);
1125
- if ((!sameLine || !sel.from.ch) && toPos.y < clientHeight - .5 * th)
1126
- add(0, toPos.y, clientWidth - toPos.x, th);
1127
- selectionDiv.innerHTML = html;
1128
- cursor.style.display = "none";
1129
- selectionDiv.style.display = "";
1130
- }
1131
- }
1132
-
1133
- function setShift(val) {
1134
- if (val) shiftSelecting = shiftSelecting || (sel.inverted ? sel.to : sel.from);
1135
- else shiftSelecting = null;
1136
- }
1137
- function setSelectionUser(from, to) {
1138
- var sh = shiftSelecting && clipPos(shiftSelecting);
1139
- if (sh) {
1140
- if (posLess(sh, from)) from = sh;
1141
- else if (posLess(to, sh)) to = sh;
1142
- }
1143
- setSelection(from, to);
1144
- userSelChange = true;
1145
- }
1146
- // Update the selection. Last two args are only used by
1147
- // updateLines, since they have to be expressed in the line
1148
- // numbers before the update.
1149
- function setSelection(from, to, oldFrom, oldTo) {
1150
- goalColumn = null;
1151
- if (oldFrom == null) {oldFrom = sel.from.line; oldTo = sel.to.line;}
1152
- if (posEq(sel.from, from) && posEq(sel.to, to)) return;
1153
- if (posLess(to, from)) {var tmp = to; to = from; from = tmp;}
1154
-
1155
- // Skip over hidden lines.
1156
- if (from.line != oldFrom) {
1157
- var from1 = skipHidden(from, oldFrom, sel.from.ch);
1158
- // If there is no non-hidden line left, force visibility on current line
1159
- if (!from1) setLineHidden(from.line, false);
1160
- else from = from1;
1161
- }
1162
- if (to.line != oldTo) to = skipHidden(to, oldTo, sel.to.ch);
1163
-
1164
- if (posEq(from, to)) sel.inverted = false;
1165
- else if (posEq(from, sel.to)) sel.inverted = false;
1166
- else if (posEq(to, sel.from)) sel.inverted = true;
1167
-
1168
- if (options.autoClearEmptyLines && posEq(sel.from, sel.to)) {
1169
- var head = sel.inverted ? from : to;
1170
- if (head.line != sel.from.line && sel.from.line < doc.size) {
1171
- var oldLine = getLine(sel.from.line);
1172
- if (/^\s+$/.test(oldLine.text))
1173
- setTimeout(operation(function() {
1174
- if (oldLine.parent && /^\s+$/.test(oldLine.text)) {
1175
- var no = lineNo(oldLine);
1176
- replaceRange("", {line: no, ch: 0}, {line: no, ch: oldLine.text.length});
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1177
  }
1178
- }, 10));
 
 
 
1179
  }
 
1180
  }
 
 
 
1181
 
1182
- sel.from = from; sel.to = to;
1183
- selectionChanged = true;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1184
  }
1185
- function skipHidden(pos, oldLine, oldCh) {
1186
- function getNonHidden(dir) {
1187
- var lNo = pos.line + dir, end = dir == 1 ? doc.size : -1;
1188
- while (lNo != end) {
1189
- var line = getLine(lNo);
1190
- if (!line.hidden) {
1191
- var ch = pos.ch;
1192
- if (ch > oldCh || ch > line.text.length) ch = line.text.length;
1193
- return {line: lNo, ch: ch};
1194
- }
1195
- lNo += dir;
1196
- }
1197
  }
1198
- var line = getLine(pos.line);
1199
- if (!line.hidden) return pos;
1200
- if (pos.line >= oldLine) return getNonHidden(1) || getNonHidden(-1);
1201
- else return getNonHidden(-1) || getNonHidden(1);
1202
- }
1203
- function setCursor(line, ch, user) {
1204
- var pos = clipPos({line: line, ch: ch || 0});
1205
- (user ? setSelectionUser : setSelection)(pos, pos);
1206
- }
1207
-
1208
- function clipLine(n) {return Math.max(0, Math.min(n, doc.size-1));}
1209
- function clipPos(pos) {
1210
- if (pos.line < 0) return {line: 0, ch: 0};
1211
- if (pos.line >= doc.size) return {line: doc.size-1, ch: getLine(doc.size-1).text.length};
1212
- var ch = pos.ch, linelen = getLine(pos.line).text.length;
1213
- if (ch == null || ch > linelen) return {line: pos.line, ch: linelen};
1214
- else if (ch < 0) return {line: pos.line, ch: 0};
1215
- else return pos;
1216
- }
1217
-
1218
- function findPosH(dir, unit) {
1219
- var end = sel.inverted ? sel.from : sel.to, line = end.line, ch = end.ch;
1220
- var lineObj = getLine(line);
1221
- function findNextLine() {
1222
- for (var l = line + dir, e = dir < 0 ? -1 : doc.size; l != e; l += dir) {
1223
- var lo = getLine(l);
1224
- if (!lo.hidden) { line = l; lineObj = lo; return true; }
1225
- }
1226
  }
1227
- function moveOnce(boundToLine) {
1228
- if (ch == (dir < 0 ? 0 : lineObj.text.length)) {
1229
- if (!boundToLine && findNextLine()) ch = dir < 0 ? lineObj.text.length : 0;
1230
- else return false;
1231
- } else ch += dir;
1232
- return true;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1233
  }
1234
- if (unit == "char") moveOnce();
1235
- else if (unit == "column") moveOnce(true);
1236
- else if (unit == "word") {
1237
- var sawWord = false;
1238
- for (;;) {
1239
- if (dir < 0) if (!moveOnce()) break;
1240
- if (isWordChar(lineObj.text.charAt(ch))) sawWord = true;
1241
- else if (sawWord) {if (dir < 0) {dir = 1; moveOnce();} break;}
1242
- if (dir > 0) if (!moveOnce()) break;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1243
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1244
  }
1245
- return {line: line, ch: ch};
1246
- }
1247
- function moveH(dir, unit) {
1248
- var pos = dir < 0 ? sel.from : sel.to;
1249
- if (shiftSelecting || posEq(sel.from, sel.to)) pos = findPosH(dir, unit);
1250
- setCursor(pos.line, pos.ch, true);
1251
- }
1252
- function deleteH(dir, unit) {
1253
- if (!posEq(sel.from, sel.to)) replaceRange("", sel.from, sel.to);
1254
- else if (dir < 0) replaceRange("", findPosH(dir, unit), sel.to);
1255
- else replaceRange("", sel.from, findPosH(dir, unit));
1256
- userSelChange = true;
1257
- }
1258
- var goalColumn = null;
1259
- function moveV(dir, unit) {
1260
- var dist = 0, pos = localCoords(sel.inverted ? sel.from : sel.to, true);
1261
- if (goalColumn != null) pos.x = goalColumn;
1262
- if (unit == "page") dist = Math.min(scroller.clientHeight, window.innerHeight || document.documentElement.clientHeight);
1263
- else if (unit == "line") dist = textHeight();
1264
- var target = coordsChar(pos.x, pos.y + dist * dir + 2);
1265
- if (unit == "page") scroller.scrollTop += localCoords(target, true).y - pos.y;
1266
- setCursor(target.line, target.ch, true);
1267
- goalColumn = pos.x;
1268
- }
1269
-
1270
- function selectWordAt(pos) {
1271
- var line = getLine(pos.line).text;
1272
- var start = pos.ch, end = pos.ch;
1273
- while (start > 0 && isWordChar(line.charAt(start - 1))) --start;
1274
- while (end < line.length && isWordChar(line.charAt(end))) ++end;
1275
- setSelectionUser({line: pos.line, ch: start}, {line: pos.line, ch: end});
1276
- }
1277
- function selectLine(line) {
1278
- setSelectionUser({line: line, ch: 0}, clipPos({line: line + 1, ch: 0}));
1279
- }
1280
- function indentSelected(mode) {
1281
- if (posEq(sel.from, sel.to)) return indentLine(sel.from.line, mode);
1282
  var e = sel.to.line - (sel.to.ch ? 0 : 1);
1283
- for (var i = sel.from.line; i <= e; ++i) indentLine(i, mode);
1284
- }
1285
-
1286
- function indentLine(n, how) {
1287
- if (!how) how = "add";
1288
- if (how == "smart") {
1289
- if (!mode.indent) how = "prev";
1290
- else var state = getStateBefore(n);
1291
- }
1292
-
1293
- var line = getLine(n), curSpace = line.indentation(options.tabSize),
1294
- curSpaceString = line.text.match(/^\s*/)[0], indentation;
1295
- if (how == "prev") {
1296
- if (n) indentation = getLine(n-1).indentation(options.tabSize);
1297
- else indentation = 0;
1298
- }
1299
- else if (how == "smart") indentation = mode.indent(state, line.text.slice(curSpaceString.length), line.text);
1300
- else if (how == "add") indentation = curSpace + options.indentUnit;
1301
- else if (how == "subtract") indentation = curSpace - options.indentUnit;
1302
- indentation = Math.max(0, indentation);
1303
- var diff = indentation - curSpace;
1304
-
1305
- if (!diff) {
1306
- if (sel.from.line != n && sel.to.line != n) return;
1307
- var indentString = curSpaceString;
1308
- }
1309
- else {
1310
- var indentString = "", pos = 0;
1311
- if (options.indentWithTabs)
1312
- for (var i = Math.floor(indentation / options.tabSize); i; --i) {pos += options.tabSize; indentString += "\t";}
1313
- while (pos < indentation) {++pos; indentString += " ";}
1314
- }
1315
-
1316
- replaceRange(indentString, {line: n, ch: 0}, {line: n, ch: curSpaceString.length});
1317
- }
1318
-
1319
- function loadMode() {
1320
- mode = CodeMirror.getMode(options, options.mode);
1321
- doc.iter(0, doc.size, function(line) { line.stateAfter = null; });
1322
- work = [0];
1323
- startWorker();
1324
- }
1325
- function gutterChanged() {
1326
- var visible = options.gutter || options.lineNumbers;
1327
- gutter.style.display = visible ? "" : "none";
1328
- if (visible) gutterDirty = true;
1329
- else lineDiv.parentNode.style.marginLeft = 0;
1330
- }
1331
- function wrappingChanged(from, to) {
1332
- if (options.lineWrapping) {
1333
- wrapper.className += " CodeMirror-wrap";
1334
- var perLine = scroller.clientWidth / charWidth() - 3;
1335
- doc.iter(0, doc.size, function(line) {
1336
- if (line.hidden) return;
1337
- var guess = Math.ceil(line.text.length / perLine) || 1;
1338
- if (guess != 1) updateLineHeight(line, guess);
1339
- });
1340
- lineSpace.style.width = code.style.width = "";
1341
- } else {
1342
- wrapper.className = wrapper.className.replace(" CodeMirror-wrap", "");
1343
- maxWidth = null; maxLine = "";
1344
- doc.iter(0, doc.size, function(line) {
1345
- if (line.height != 1 && !line.hidden) updateLineHeight(line, 1);
1346
- if (line.text.length > maxLine.length) maxLine = line.text;
1347
- });
1348
- }
1349
- changes.push({from: 0, to: doc.size});
1350
- }
1351
- function makeTab(col) {
1352
- var w = options.tabSize - col % options.tabSize, cached = tabCache[w];
1353
- if (cached) return cached;
1354
- for (var str = '<span class="cm-tab">', i = 0; i < w; ++i) str += " ";
1355
- return (tabCache[w] = {html: str + "</span>", width: w});
1356
- }
1357
- function themeChanged() {
1358
- scroller.className = scroller.className.replace(/\s*cm-s-\w+/g, "") +
1359
- options.theme.replace(/(^|\s)\s*/g, " cm-s-");
1360
- }
1361
-
1362
- function TextMarker() { this.set = []; }
1363
- TextMarker.prototype.clear = operation(function() {
1364
- var min = Infinity, max = -Infinity;
1365
- for (var i = 0, e = this.set.length; i < e; ++i) {
1366
- var line = this.set[i], mk = line.marked;
1367
- if (!mk || !line.parent) continue;
1368
- var lineN = lineNo(line);
1369
- min = Math.min(min, lineN); max = Math.max(max, lineN);
1370
- for (var j = 0; j < mk.length; ++j)
1371
- if (mk[j].marker == this) mk.splice(j--, 1);
1372
- }
1373
- if (min != Infinity)
1374
- changes.push({from: min, to: max + 1});
1375
- });
1376
- TextMarker.prototype.find = function() {
1377
- var from, to;
1378
- for (var i = 0, e = this.set.length; i < e; ++i) {
1379
- var line = this.set[i], mk = line.marked;
1380
- for (var j = 0; j < mk.length; ++j) {
1381
- var mark = mk[j];
1382
- if (mark.marker == this) {
1383
- if (mark.from != null || mark.to != null) {
1384
- var found = lineNo(line);
1385
- if (found != null) {
1386
- if (mark.from != null) from = {line: found, ch: mark.from};
1387
- if (mark.to != null) to = {line: found, ch: mark.to};
1388
- }
1389
- }
1390
  }
1391
  }
 
1392
  }
1393
- return {from: from, to: to};
1394
- };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1395
 
1396
- function markText(from, to, className) {
1397
- from = clipPos(from); to = clipPos(to);
1398
- var tm = new TextMarker();
1399
- if (!posLess(from, to)) return tm;
1400
- function add(line, from, to, className) {
1401
- getLine(line).addMark(new MarkedText(from, to, className, tm));
1402
- }
1403
- if (from.line == to.line) add(from.line, from.ch, to.ch, className);
1404
- else {
1405
- add(from.line, from.ch, null, className);
1406
- for (var i = from.line + 1, e = to.line; i < e; ++i)
1407
- add(i, null, null, className);
1408
- add(to.line, null, to.ch, className);
1409
- }
1410
- changes.push({from: from.line, to: to.line + 1});
1411
- return tm;
1412
- }
1413
-
1414
- function setBookmark(pos) {
1415
- pos = clipPos(pos);
1416
- var bm = new Bookmark(pos.ch);
1417
- getLine(pos.line).addMark(bm);
1418
- return bm;
1419
- }
1420
-
1421
- function findMarksAt(pos) {
1422
- pos = clipPos(pos);
1423
- var markers = [], marked = getLine(pos.line).marked;
1424
- if (!marked) return markers;
1425
- for (var i = 0, e = marked.length; i < e; ++i) {
1426
- var m = marked[i];
1427
- if ((m.from == null || m.from <= pos.ch) &&
1428
- (m.to == null || m.to >= pos.ch))
1429
- markers.push(m.marker || m);
 
 
 
 
1430
  }
1431
  return markers;
1432
- }
1433
 
1434
- function addGutterMarker(line, text, className) {
1435
- if (typeof line == "number") line = getLine(clipLine(line));
1436
- line.gutterMarker = {text: text, style: className};
1437
- gutterDirty = true;
1438
- return line;
1439
- }
1440
- function removeGutterMarker(line) {
1441
- if (typeof line == "number") line = getLine(clipLine(line));
1442
- line.gutterMarker = null;
1443
- gutterDirty = true;
1444
- }
1445
-
1446
- function changeLine(handle, op) {
1447
- var no = handle, line = handle;
1448
- if (typeof handle == "number") line = getLine(clipLine(handle));
1449
- else no = lineNo(handle);
1450
- if (no == null) return null;
1451
- if (op(line, no)) changes.push({from: no, to: no + 1});
1452
- else return null;
1453
- return line;
1454
- }
1455
- function setLineClass(handle, className, bgClassName) {
1456
- return changeLine(handle, function(line) {
1457
- if (line.className != className || line.bgClassName != bgClassName) {
1458
- line.className = className;
1459
- line.bgClassName = bgClassName;
1460
- return true;
1461
  }
 
1462
  });
1463
- }
1464
- function setLineHidden(handle, hidden) {
1465
- return changeLine(handle, function(line, no) {
1466
- if (line.hidden != hidden) {
1467
- line.hidden = hidden;
1468
- updateLineHeight(line, hidden ? 0 : 1);
1469
- var fline = sel.from.line, tline = sel.to.line;
1470
- if (hidden && (fline == no || tline == no)) {
1471
- var from = fline == no ? skipHidden({line: fline, ch: 0}, fline, 0) : sel.from;
1472
- var to = tline == no ? skipHidden({line: tline, ch: 0}, tline, 0) : sel.to;
1473
- // Can't hide the last visible line, we'd have no place to put the cursor
1474
- if (!to) return;
1475
- setSelection(from, to);
1476
- }
1477
- return (gutterDirty = true);
 
 
 
 
 
 
 
1478
  }
 
1479
  });
1480
- }
 
 
 
 
1481
 
1482
- function lineInfo(line) {
 
 
1483
  if (typeof line == "number") {
1484
- if (!isLine(line)) return null;
1485
  var n = line;
1486
- line = getLine(line);
1487
  if (!line) return null;
1488
- }
1489
- else {
1490
  var n = lineNo(line);
1491
  if (n == null) return null;
1492
  }
1493
- var marker = line.gutterMarker;
1494
- return {line: n, handle: line, text: line.text, markerText: marker && marker.text,
1495
- markerClass: marker && marker.style, lineClass: line.className, bgClass: line.bgClassName};
1496
- }
1497
 
1498
- function stringWidth(str) {
1499
- measure.innerHTML = "<pre><span>x</span></pre>";
1500
- measure.firstChild.firstChild.firstChild.nodeValue = str;
1501
- return measure.firstChild.firstChild.offsetWidth || 10;
1502
- }
1503
- // These are used to go from pixel positions to character
1504
- // positions, taking varying character widths into account.
1505
- function charFromX(line, x) {
1506
- if (x <= 0) return 0;
1507
- var lineObj = getLine(line), text = lineObj.text;
1508
- function getX(len) {
1509
- measure.innerHTML = "<pre><span>" + lineObj.getHTML(makeTab, len) + "</span></pre>";
1510
- return measure.firstChild.firstChild.offsetWidth;
1511
- }
1512
- var from = 0, fromX = 0, to = text.length, toX;
1513
- // Guess a suitable upper bound for our search.
1514
- var estimated = Math.min(to, Math.ceil(x / charWidth()));
1515
- for (;;) {
1516
- var estX = getX(estimated);
1517
- if (estX <= x && estimated < to) estimated = Math.min(to, Math.ceil(estimated * 1.2));
1518
- else {toX = estX; to = estimated; break;}
1519
- }
1520
- if (x > toX) return to;
1521
- // Try to guess a suitable lower bound as well.
1522
- estimated = Math.floor(to * 0.8); estX = getX(estimated);
1523
- if (estX < x) {from = estimated; fromX = estX;}
1524
- // Do a binary search between these bounds.
1525
- for (;;) {
1526
- if (to - from <= 1) return (toX - x > x - fromX) ? from : to;
1527
- var middle = Math.ceil((from + to) / 2), middleX = getX(middle);
1528
- if (middleX > x) {to = middle; toX = middleX;}
1529
- else {from = middle; fromX = middleX;}
1530
- }
1531
- }
1532
-
1533
- var tempId = Math.floor(Math.random() * 0xffffff).toString(16);
1534
- function measureLine(line, ch) {
1535
- if (ch == 0) return {top: 0, left: 0};
1536
- var extra = "";
1537
- // Include extra text at the end to make sure the measured line is wrapped in the right way.
1538
- if (options.lineWrapping) {
1539
- var end = line.text.indexOf(" ", ch + 6);
1540
- extra = htmlEscape(line.text.slice(ch + 1, end < 0 ? line.text.length : end + (ie ? 5 : 0)));
1541
- }
1542
- measure.innerHTML = "<pre>" + line.getHTML(makeTab, ch) +
1543
- '<span id="CodeMirror-temp-' + tempId + '">' + htmlEscape(line.text.charAt(ch) || " ") + "</span>" +
1544
- extra + "</pre>";
1545
- var elt = document.getElementById("CodeMirror-temp-" + tempId);
1546
- var top = elt.offsetTop, left = elt.offsetLeft;
1547
- // Older IEs report zero offsets for spans directly after a wrap
1548
- if (ie && top == 0 && left == 0) {
1549
- var backup = document.createElement("span");
1550
- backup.innerHTML = "x";
1551
- elt.parentNode.insertBefore(backup, elt.nextSibling);
1552
- top = backup.offsetTop;
1553
- }
1554
- return {top: top, left: left};
1555
- }
1556
- function localCoords(pos, inLineWrap) {
1557
- var x, lh = textHeight(), y = lh * (heightAtLine(doc, pos.line) - (inLineWrap ? displayOffset : 0));
1558
- if (pos.ch == 0) x = 0;
1559
- else {
1560
- var sp = measureLine(getLine(pos.line), pos.ch);
1561
- x = sp.left;
1562
- if (options.lineWrapping) y += Math.max(0, sp.top);
1563
- }
1564
- return {x: x, y: y, yBot: y + lh};
1565
- }
1566
- // Coords must be lineSpace-local
1567
- function coordsChar(x, y) {
1568
- if (y < 0) y = 0;
1569
- var th = textHeight(), cw = charWidth(), heightPos = displayOffset + Math.floor(y / th);
1570
- var lineNo = lineAtHeight(doc, heightPos);
1571
- if (lineNo >= doc.size) return {line: doc.size - 1, ch: getLine(doc.size - 1).text.length};
1572
- var lineObj = getLine(lineNo), text = lineObj.text;
1573
- var tw = options.lineWrapping, innerOff = tw ? heightPos - heightAtLine(doc, lineNo) : 0;
1574
- if (x <= 0 && innerOff == 0) return {line: lineNo, ch: 0};
1575
- function getX(len) {
1576
- var sp = measureLine(lineObj, len);
1577
- if (tw) {
1578
- var off = Math.round(sp.top / th);
1579
- return Math.max(0, sp.left + (off - innerOff) * scroller.clientWidth);
1580
- }
1581
- return sp.left;
1582
  }
1583
- var from = 0, fromX = 0, to = text.length, toX;
1584
- // Guess a suitable upper bound for our search.
1585
- var estimated = Math.min(to, Math.ceil((x + innerOff * scroller.clientWidth * .9) / cw));
1586
- for (;;) {
1587
- var estX = getX(estimated);
1588
- if (estX <= x && estimated < to) estimated = Math.min(to, Math.ceil(estimated * 1.2));
1589
- else {toX = estX; to = estimated; break;}
1590
- }
1591
- if (x > toX) return {line: lineNo, ch: to};
1592
- // Try to guess a suitable lower bound as well.
1593
- estimated = Math.floor(to * 0.8); estX = getX(estimated);
1594
- if (estX < x) {from = estimated; fromX = estX;}
1595
- // Do a binary search between these bounds.
1596
- for (;;) {
1597
- if (to - from <= 1) return {line: lineNo, ch: (toX - x > x - fromX) ? from : to};
1598
- var middle = Math.ceil((from + to) / 2), middleX = getX(middle);
1599
- if (middleX > x) {to = middle; toX = middleX;}
1600
- else {from = middle; fromX = middleX;}
1601
- }
1602
- }
1603
- function pageCoords(pos) {
1604
- var local = localCoords(pos, true), off = eltOffset(lineSpace);
1605
- return {x: off.left + local.x, y: off.top + local.y, yBot: off.top + local.yBot};
1606
- }
1607
-
1608
- var cachedHeight, cachedHeightFor, measureText;
1609
- function textHeight() {
1610
- if (measureText == null) {
1611
- measureText = "<pre>";
1612
- for (var i = 0; i < 49; ++i) measureText += "x<br/>";
1613
- measureText += "x</pre>";
1614
- }
1615
- var offsetHeight = lineDiv.clientHeight;
1616
- if (offsetHeight == cachedHeightFor) return cachedHeight;
1617
- cachedHeightFor = offsetHeight;
1618
- measure.innerHTML = measureText;
1619
- cachedHeight = measure.firstChild.offsetHeight / 50 || 1;
1620
- measure.innerHTML = "";
1621
- return cachedHeight;
1622
- }
1623
- var cachedWidth, cachedWidthFor = 0;
1624
- function charWidth() {
1625
- if (scroller.clientWidth == cachedWidthFor) return cachedWidth;
1626
- cachedWidthFor = scroller.clientWidth;
1627
- return (cachedWidth = stringWidth("x"));
1628
- }
1629
- function paddingTop() {return lineSpace.offsetTop;}
1630
- function paddingLeft() {return lineSpace.offsetLeft;}
1631
-
1632
- function posFromMouse(e, liberal) {
1633
- var offW = eltOffset(scroller, true), x, y;
1634
- // Fails unpredictably on IE[67] when mouse is dragged around quickly.
1635
- try { x = e.clientX; y = e.clientY; } catch (e) { return null; }
1636
- // This is a mess of a heuristic to try and determine whether a
1637
- // scroll-bar was clicked or not, and to return null if one was
1638
- // (and !liberal).
1639
- if (!liberal && (x - offW.left > scroller.clientWidth || y - offW.top > scroller.clientHeight))
1640
- return null;
1641
- var offL = eltOffset(lineSpace, true);
1642
- return coordsChar(x - offL.left, y - offL.top);
1643
- }
1644
- function onContextMenu(e) {
1645
- var pos = posFromMouse(e), scrollPos = scroller.scrollTop;
1646
- if (!pos || window.opera) return; // Opera is difficult.
1647
- if (posEq(sel.from, sel.to) || posLess(pos, sel.from) || !posLess(pos, sel.to))
1648
- operation(setCursor)(pos.line, pos.ch);
1649
-
1650
- var oldCSS = input.style.cssText;
1651
- inputDiv.style.position = "absolute";
1652
- input.style.cssText = "position: fixed; width: 30px; height: 30px; top: " + (e.clientY - 5) +
1653
- "px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: white; " +
1654
- "border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);";
1655
- leaveInputAlone = true;
1656
- var val = input.value = getSelection();
1657
- focusInput();
1658
- selectInput(input);
1659
- function rehide() {
1660
- var newVal = splitLines(input.value).join("\n");
1661
- if (newVal != val) operation(replaceSelection)(newVal, "end");
1662
- inputDiv.style.position = "relative";
1663
- input.style.cssText = oldCSS;
1664
- if (ie_lt9) scroller.scrollTop = scrollPos;
1665
- leaveInputAlone = false;
1666
- resetInput(true);
1667
- slowPoll();
1668
- }
1669
-
1670
- if (gecko) {
1671
- e_stop(e);
1672
- var mouseup = connect(window, "mouseup", function() {
1673
- mouseup();
1674
- setTimeout(rehide, 20);
1675
- }, true);
1676
  } else {
1677
- setTimeout(rehide, 50);
1678
- }
1679
- }
1680
-
1681
- // Cursor-blinking
1682
- function restartBlink() {
1683
- clearInterval(blinker);
1684
- var on = true;
1685
- cursor.style.visibility = "";
1686
- blinker = setInterval(function() {
1687
- cursor.style.visibility = (on = !on) ? "" : "hidden";
1688
- }, 650);
1689
- }
1690
-
1691
- var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<"};
1692
- function matchBrackets(autoclear) {
1693
- var head = sel.inverted ? sel.from : sel.to, line = getLine(head.line), pos = head.ch - 1;
1694
- var match = (pos >= 0 && matching[line.text.charAt(pos)]) || matching[line.text.charAt(++pos)];
1695
- if (!match) return;
1696
- var ch = match.charAt(0), forward = match.charAt(1) == ">", d = forward ? 1 : -1, st = line.styles;
1697
- for (var off = pos + 1, i = 0, e = st.length; i < e; i+=2)
1698
- if ((off -= st[i].length) <= 0) {var style = st[i+1]; break;}
1699
-
1700
- var stack = [line.text.charAt(pos)], re = /[(){}[\]]/;
1701
- function scan(line, from, to) {
1702
- if (!line.text) return;
1703
- var st = line.styles, pos = forward ? 0 : line.text.length - 1, cur;
1704
- for (var i = forward ? 0 : st.length - 2, e = forward ? st.length : -2; i != e; i += 2*d) {
1705
- var text = st[i];
1706
- if (st[i+1] != null && st[i+1] != style) {pos += d * text.length; continue;}
1707
- for (var j = forward ? 0 : text.length - 1, te = forward ? text.length : -1; j != te; j += d, pos+=d) {
1708
- if (pos >= from && pos < to && re.test(cur = text.charAt(j))) {
1709
- var match = matching[cur];
1710
- if (match.charAt(1) == ">" == forward) stack.push(cur);
1711
- else if (stack.pop() != match.charAt(0)) return {pos: pos, match: false};
1712
- else if (!stack.length) return {pos: pos, match: true};
1713
- }
1714
- }
1715
- }
1716
  }
1717
- for (var i = head.line, e = forward ? Math.min(i + 100, doc.size) : Math.max(-1, i - 100); i != e; i+=d) {
1718
- var line = getLine(i), first = i == head.line;
1719
- var found = scan(line, first && forward ? pos + 1 : 0, first && !forward ? pos : line.text.length);
1720
- if (found) break;
1721
- }
1722
- if (!found) found = {pos: null, match: false};
1723
- var style = found.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket";
1724
- var one = markText({line: head.line, ch: pos}, {line: head.line, ch: pos+1}, style),
1725
- two = found.pos != null && markText({line: i, ch: found.pos}, {line: i, ch: found.pos + 1}, style);
1726
- var clear = operation(function(){one.clear(); two && two.clear();});
1727
- if (autoclear) setTimeout(clear, 800);
1728
- else bracketHighlighted = clear;
1729
- }
1730
-
1731
- // Finds the line to start with when starting a parse. Tries to
1732
- // find a line with a stateAfter, so that it can start with a
1733
- // valid state. If that fails, it returns the line with the
1734
- // smallest indentation, which tends to need the least context to
1735
- // parse correctly.
1736
- function findStartLine(n) {
1737
- var minindent, minline;
1738
- for (var search = n, lim = n - 40; search > lim; --search) {
1739
- if (search == 0) return 0;
1740
- var line = getLine(search-1);
1741
- if (line.stateAfter) return search;
1742
- var indented = line.indentation(options.tabSize);
1743
- if (minline == null || minindent > indented) {
1744
- minline = search - 1;
1745
- minindent = indented;
1746
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1747
  }
1748
- return minline;
1749
- }
1750
- function getStateBefore(n) {
1751
- var start = findStartLine(n), state = start && getLine(start-1).stateAfter;
1752
- if (!state) state = startState(mode);
1753
- else state = copyState(mode, state);
1754
- doc.iter(start, n, function(line) {
1755
- line.highlight(mode, state, options.tabSize);
1756
- line.stateAfter = copyState(mode, state);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1757
  });
1758
- if (start < n) changes.push({from: start, to: n});
1759
- if (n < doc.size && !getLine(n).stateAfter) work.push(n);
1760
- return state;
1761
- }
1762
- function highlightLines(start, end) {
1763
- var state = getStateBefore(start);
1764
- doc.iter(start, end, function(line) {
1765
- line.highlight(mode, state, options.tabSize);
1766
- line.stateAfter = copyState(mode, state);
1767
  });
1768
- }
1769
- function highlightWorker() {
1770
- var end = +new Date + options.workTime;
1771
- var foundWork = work.length;
1772
- while (work.length) {
1773
- if (!getLine(showingFrom).stateAfter) var task = showingFrom;
1774
- else var task = work.pop();
1775
- if (task >= doc.size) continue;
1776
- var start = findStartLine(task), state = start && getLine(start-1).stateAfter;
1777
- if (state) state = copyState(mode, state);
1778
- else state = startState(mode);
1779
-
1780
- var unchanged = 0, compare = mode.compareStates, realChange = false,
1781
- i = start, bail = false;
1782
- doc.iter(i, doc.size, function(line) {
1783
- var hadState = line.stateAfter;
1784
- if (+new Date > end) {
1785
- work.push(i);
1786
- startWorker(options.workDelay);
1787
- if (realChange) changes.push({from: task, to: i + 1});
1788
- return (bail = true);
1789
- }
1790
- var changed = line.highlight(mode, state, options.tabSize);
1791
- if (changed) realChange = true;
1792
- line.stateAfter = copyState(mode, state);
1793
- if (compare) {
1794
- if (hadState && compare(hadState, state)) return true;
1795
- } else {
1796
- if (changed !== false || !hadState) unchanged = 0;
1797
- else if (++unchanged > 3 && (!mode.indent || mode.indent(hadState, "") == mode.indent(state, "")))
1798
- return true;
1799
- }
1800
- ++i;
1801
- });
1802
- if (bail) return;
1803
- if (realChange) changes.push({from: task, to: i + 1});
1804
- }
1805
- if (foundWork && options.onHighlightComplete)
1806
- options.onHighlightComplete(instance);
1807
- }
1808
- function startWorker(time) {
1809
- if (!work.length) return;
1810
- highlight.set(time, operation(highlightWorker));
1811
- }
1812
-
1813
- // Operations are used to wrap changes in such a way that each
1814
- // change won't have to update the cursor and display (which would
1815
- // be awkward, slow, and error-prone), but instead updates are
1816
- // batched and then all combined and executed at once.
1817
- function startOperation() {
1818
- updateInput = userSelChange = textChanged = null;
1819
- changes = []; selectionChanged = false; callbacks = [];
1820
- }
1821
- function endOperation() {
1822
- var reScroll = false, updated;
1823
- if (selectionChanged) reScroll = !scrollCursorIntoView();
1824
- if (changes.length) updated = updateDisplay(changes, true);
1825
- else {
1826
- if (selectionChanged) updateSelection();
1827
- if (gutterDirty) updateGutter();
1828
- }
1829
- if (reScroll) scrollCursorIntoView();
1830
- if (selectionChanged) {scrollEditorIntoView(); restartBlink();}
1831
-
1832
- if (focused && !leaveInputAlone &&
1833
- (updateInput === true || (updateInput !== false && selectionChanged)))
1834
- resetInput(userSelChange);
1835
-
1836
- if (selectionChanged && options.matchBrackets)
1837
- setTimeout(operation(function() {
1838
- if (bracketHighlighted) {bracketHighlighted(); bracketHighlighted = null;}
1839
- if (posEq(sel.from, sel.to)) matchBrackets(false);
1840
- }), 20);
1841
- var tc = textChanged, cbs = callbacks; // these can be reset by callbacks
1842
- if (selectionChanged && options.onCursorActivity)
1843
- options.onCursorActivity(instance);
1844
- if (tc && options.onChange && instance)
1845
- options.onChange(instance, tc);
1846
- for (var i = 0; i < cbs.length; ++i) cbs[i](instance);
1847
- if (updated && options.onUpdate) options.onUpdate(instance);
1848
- }
1849
- var nestedOperation = 0;
1850
- function operation(f) {
1851
- return function() {
1852
- if (!nestedOperation++) startOperation();
1853
- try {var result = f.apply(this, arguments);}
1854
- finally {if (!--nestedOperation) endOperation();}
1855
- return result;
1856
- };
1857
- }
1858
 
1859
- for (var ext in extensions)
1860
- if (extensions.propertyIsEnumerable(ext) &&
1861
- !instance.propertyIsEnumerable(ext))
1862
- instance[ext] = extensions[ext];
1863
- return instance;
1864
- } // (end of function CodeMirror)
 
 
1865
 
1866
- // The default configuration options.
1867
- CodeMirror.defaults = {
1868
- value: "",
1869
- mode: null,
1870
- theme: "default",
1871
- indentUnit: 2,
1872
- indentWithTabs: false,
1873
- smartIndent: true,
1874
- tabSize: 4,
1875
- keyMap: "default",
1876
- extraKeys: null,
1877
- electricChars: true,
1878
- autoClearEmptyLines: false,
1879
- onKeyEvent: null,
1880
- lineWrapping: false,
1881
- lineNumbers: false,
1882
- gutter: false,
1883
- fixedGutter: false,
1884
- firstLineNumber: 1,
1885
- readOnly: false,
1886
- onChange: null,
1887
- onCursorActivity: null,
1888
- onGutterClick: null,
1889
- onHighlightComplete: null,
1890
- onUpdate: null,
1891
- onFocus: null, onBlur: null, onScroll: null,
1892
- matchBrackets: false,
1893
- workTime: 100,
1894
- workDelay: 200,
1895
- pollInterval: 100,
1896
- undoDepth: 40,
1897
- tabindex: null,
1898
- autofocus: null
1899
  };
1900
 
1901
- var ios = /AppleWebKit/.test(navigator.userAgent) && /Mobile\/\w+/.test(navigator.userAgent);
1902
- var mac = ios || /Mac/.test(navigator.platform);
1903
- var win = /Win/.test(navigator.platform);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1904
 
1905
  // Known modes, by name and by MIME
1906
  var modes = CodeMirror.modes = {}, mimeModes = CodeMirror.mimeModes = {};
 
1907
  CodeMirror.defineMode = function(name, mode) {
1908
  if (!CodeMirror.defaults.mode && name != "null") CodeMirror.defaults.mode = name;
 
 
 
 
1909
  modes[name] = mode;
1910
  };
 
1911
  CodeMirror.defineMIME = function(mime, spec) {
1912
  mimeModes[mime] = spec;
1913
  };
 
1914
  CodeMirror.resolveMode = function(spec) {
1915
  if (typeof spec == "string" && mimeModes.hasOwnProperty(spec))
1916
  spec = mimeModes[spec];
@@ -1919,52 +2970,112 @@ var CodeMirror = (function() {
1919
  if (typeof spec == "string") return {name: spec};
1920
  else return spec || {name: "null"};
1921
  };
 
1922
  CodeMirror.getMode = function(options, spec) {
1923
- var spec = CodeMirror.resolveMode(spec);
1924
  var mfactory = modes[spec.name];
1925
- if (!mfactory) {
1926
- if (window.console) console.warn("No mode " + spec.name + " found, falling back to plain text.");
1927
- return CodeMirror.getMode(options, "text/plain");
 
 
 
 
 
 
1928
  }
1929
- return mfactory(options, spec);
1930
- };
1931
- CodeMirror.listModes = function() {
1932
- var list = [];
1933
- for (var m in modes)
1934
- if (modes.propertyIsEnumerable(m)) list.push(m);
1935
- return list;
1936
  };
1937
- CodeMirror.listMIMEs = function() {
1938
- var list = [];
1939
- for (var m in mimeModes)
1940
- if (mimeModes.propertyIsEnumerable(m)) list.push({mime: m, mode: mimeModes[m]});
1941
- return list;
 
 
 
 
 
 
1942
  };
1943
 
1944
- var extensions = CodeMirror.extensions = {};
 
1945
  CodeMirror.defineExtension = function(name, func) {
1946
- extensions[name] = func;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1947
  };
1948
 
 
 
1949
  var commands = CodeMirror.commands = {
1950
  selectAll: function(cm) {cm.setSelection({line: 0, ch: 0}, {line: cm.lineCount() - 1});},
1951
  killLine: function(cm) {
1952
  var from = cm.getCursor(true), to = cm.getCursor(false), sel = !posEq(from, to);
1953
- if (!sel && cm.getLine(from.line).length == from.ch) cm.replaceRange("", from, {line: from.line + 1, ch: 0});
1954
- else cm.replaceRange("", from, sel ? to : {line: from.line});
 
 
 
 
 
1955
  },
1956
- deleteLine: function(cm) {var l = cm.getCursor().line; cm.replaceRange("", {line: l, ch: 0}, {line: l});},
1957
  undo: function(cm) {cm.undo();},
1958
  redo: function(cm) {cm.redo();},
1959
- goDocStart: function(cm) {cm.setCursor(0, 0, true);},
1960
- goDocEnd: function(cm) {cm.setSelection({line: cm.lineCount() - 1}, null, true);},
1961
- goLineStart: function(cm) {cm.setCursor(cm.getCursor().line, 0, true);},
 
 
1962
  goLineStartSmart: function(cm) {
1963
- var cur = cm.getCursor();
1964
- var text = cm.getLine(cur.line), firstNonWS = Math.max(0, text.search(/\S/));
1965
- cm.setCursor(cur.line, cur.ch <= firstNonWS && cur.ch ? 0 : firstNonWS, true);
 
 
 
 
 
 
 
 
1966
  },
1967
- goLineEnd: function(cm) {cm.setSelection({line: cm.getCursor().line}, null, true);},
1968
  goLineUp: function(cm) {cm.moveV(-1, "line");},
1969
  goLineDown: function(cm) {cm.moveV(1, "line");},
1970
  goPageUp: function(cm) {cm.moveV(-1, "page");},
@@ -1975,14 +3086,18 @@ var CodeMirror = (function() {
1975
  goColumnRight: function(cm) {cm.moveH(1, "column");},
1976
  goWordLeft: function(cm) {cm.moveH(-1, "word");},
1977
  goWordRight: function(cm) {cm.moveH(1, "word");},
1978
- delCharLeft: function(cm) {cm.deleteH(-1, "char");},
1979
- delCharRight: function(cm) {cm.deleteH(1, "char");},
1980
- delWordLeft: function(cm) {cm.deleteH(-1, "word");},
1981
- delWordRight: function(cm) {cm.deleteH(1, "word");},
1982
  indentAuto: function(cm) {cm.indentSelection("smart");},
1983
  indentMore: function(cm) {cm.indentSelection("add");},
1984
  indentLess: function(cm) {cm.indentSelection("subtract");},
1985
- insertTab: function(cm) {cm.replaceSelection("\t", "end");},
 
 
 
 
1986
  transposeChars: function(cm) {
1987
  var cur = cm.getCursor(), line = cm.getLine(cur.line);
1988
  if (cur.ch > 0 && cur.ch < line.length - 1)
@@ -1990,17 +3105,21 @@ var CodeMirror = (function() {
1990
  {line: cur.line, ch: cur.ch - 1}, {line: cur.line, ch: cur.ch + 1});
1991
  },
1992
  newlineAndIndent: function(cm) {
1993
- cm.replaceSelection("\n", "end");
1994
- cm.indentLine(cm.getCursor().line);
 
 
1995
  },
1996
  toggleOverwrite: function(cm) {cm.toggleOverwrite();}
1997
  };
1998
 
 
 
1999
  var keyMap = CodeMirror.keyMap = {};
2000
  keyMap.basic = {
2001
  "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
2002
  "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
2003
- "Delete": "delCharRight", "Backspace": "delCharLeft", "Tab": "insertTab", "Shift-Tab": "indentAuto",
2004
  "Enter": "newlineAndIndent", "Insert": "toggleOverwrite"
2005
  };
2006
  // Note that the save and find-related commands aren't defined by
@@ -2009,7 +3128,7 @@ var CodeMirror = (function() {
2009
  "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
2010
  "Ctrl-Home": "goDocStart", "Alt-Up": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Down": "goDocEnd",
2011
  "Ctrl-Left": "goWordLeft", "Ctrl-Right": "goWordRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
2012
- "Ctrl-Backspace": "delWordLeft", "Ctrl-Delete": "delWordRight", "Ctrl-S": "save", "Ctrl-F": "find",
2013
  "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
2014
  "Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
2015
  fallthrough: "basic"
@@ -2017,8 +3136,8 @@ var CodeMirror = (function() {
2017
  keyMap.macDefault = {
2018
  "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
2019
  "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goWordLeft",
2020
- "Alt-Right": "goWordRight", "Cmd-Left": "goLineStart", "Cmd-Right": "goLineEnd", "Alt-Backspace": "delWordLeft",
2021
- "Ctrl-Alt-Backspace": "delWordRight", "Alt-Delete": "delWordRight", "Cmd-S": "save", "Cmd-F": "find",
2022
  "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
2023
  "Cmd-[": "indentLess", "Cmd-]": "indentMore",
2024
  fallthrough: ["basic", "emacsy"]
@@ -2027,20 +3146,30 @@ var CodeMirror = (function() {
2027
  keyMap.emacsy = {
2028
  "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
2029
  "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
2030
- "Ctrl-V": "goPageUp", "Shift-Ctrl-V": "goPageDown", "Ctrl-D": "delCharRight", "Ctrl-H": "delCharLeft",
2031
- "Alt-D": "delWordRight", "Alt-Backspace": "delWordLeft", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars"
2032
  };
2033
 
 
 
2034
  function getKeyMap(val) {
2035
  if (typeof val == "string") return keyMap[val];
2036
  else return val;
2037
  }
2038
- function lookupKey(name, extraMap, map, handle) {
 
2039
  function lookup(map) {
2040
  map = getKeyMap(map);
2041
  var found = map[name];
 
 
 
 
2042
  if (found != null && handle(found)) return true;
2043
- if (map.catchall) return handle(map.catchall);
 
 
 
2044
  var fallthrough = map.fallthrough;
2045
  if (fallthrough == null) return false;
2046
  if (Object.prototype.toString.call(fallthrough) != "[object Array]")
@@ -2050,75 +3179,71 @@ var CodeMirror = (function() {
2050
  }
2051
  return false;
2052
  }
2053
- if (extraMap && lookup(extraMap)) return true;
2054
- return lookup(map);
 
2055
  }
2056
  function isModifierKey(event) {
2057
  var name = keyNames[e_prop(event, "keyCode")];
2058
  return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod";
2059
  }
 
 
 
2060
 
2061
  CodeMirror.fromTextArea = function(textarea, options) {
2062
  if (!options) options = {};
2063
  options.value = textarea.value;
2064
  if (!options.tabindex && textarea.tabindex)
2065
  options.tabindex = textarea.tabindex;
2066
- if (options.autofocus == null && textarea.getAttribute("autofocus") != null)
2067
- options.autofocus = true;
 
 
 
 
 
 
 
2068
 
2069
- function save() {textarea.value = instance.getValue();}
2070
  if (textarea.form) {
2071
  // Deplorable hack to make the submit method do the right thing.
2072
- var rmSubmit = connect(textarea.form, "submit", save, true);
2073
- if (typeof textarea.form.submit == "function") {
2074
- var realSubmit = textarea.form.submit;
2075
- function wrappedSubmit() {
2076
  save();
2077
- textarea.form.submit = realSubmit;
2078
- textarea.form.submit();
2079
- textarea.form.submit = wrappedSubmit;
2080
- }
2081
- textarea.form.submit = wrappedSubmit;
2082
- }
2083
  }
2084
 
2085
  textarea.style.display = "none";
2086
- var instance = CodeMirror(function(node) {
2087
  textarea.parentNode.insertBefore(node, textarea.nextSibling);
2088
  }, options);
2089
- instance.save = save;
2090
- instance.getTextArea = function() { return textarea; };
2091
- instance.toTextArea = function() {
2092
  save();
2093
- textarea.parentNode.removeChild(instance.getWrapperElement());
2094
  textarea.style.display = "";
2095
  if (textarea.form) {
2096
- rmSubmit();
2097
  if (typeof textarea.form.submit == "function")
2098
  textarea.form.submit = realSubmit;
2099
  }
2100
  };
2101
- return instance;
2102
  };
2103
 
2104
- // Utility functions for working with state. Exported because modes
2105
- // sometimes need to do this.
2106
- function copyState(mode, state) {
2107
- if (state === true) return state;
2108
- if (mode.copyState) return mode.copyState(state);
2109
- var nstate = {};
2110
- for (var n in state) {
2111
- var val = state[n];
2112
- if (val instanceof Array) val = val.concat([]);
2113
- nstate[n] = val;
2114
- }
2115
- return nstate;
2116
- }
2117
- CodeMirror.copyState = copyState;
2118
- function startState(mode, a1, a2) {
2119
- return mode.startState ? mode.startState(a1, a2) : true;
2120
- }
2121
- CodeMirror.startState = startState;
2122
 
2123
  // The character stream used by a mode's parser.
2124
  function StringStream(string, tabSize) {
@@ -2126,10 +3251,11 @@ var CodeMirror = (function() {
2126
  this.string = string;
2127
  this.tabSize = tabSize || 8;
2128
  }
 
2129
  StringStream.prototype = {
2130
  eol: function() {return this.pos >= this.string.length;},
2131
  sol: function() {return this.pos == 0;},
2132
- peek: function() {return this.string.charAt(this.pos);},
2133
  next: function() {
2134
  if (this.pos < this.string.length)
2135
  return this.string.charAt(this.pos++);
@@ -2160,14 +3286,14 @@ var CodeMirror = (function() {
2160
  indentation: function() {return countColumn(this.string, null, this.tabSize);},
2161
  match: function(pattern, consume, caseInsensitive) {
2162
  if (typeof pattern == "string") {
2163
- function cased(str) {return caseInsensitive ? str.toLowerCase() : str;}
2164
  if (cased(this.string).indexOf(cased(pattern), this.pos) == this.pos) {
2165
  if (consume !== false) this.pos += pattern.length;
2166
  return true;
2167
  }
2168
- }
2169
- else {
2170
  var match = this.string.slice(this.pos).match(pattern);
 
2171
  if (match && consume !== false) this.pos += match[0].length;
2172
  return match;
2173
  }
@@ -2176,339 +3302,665 @@ var CodeMirror = (function() {
2176
  };
2177
  CodeMirror.StringStream = StringStream;
2178
 
2179
- function MarkedText(from, to, className, marker) {
2180
- this.from = from; this.to = to; this.style = className; this.marker = marker;
 
 
 
 
2181
  }
2182
- MarkedText.prototype = {
2183
- attach: function(line) { this.marker.set.push(line); },
2184
- detach: function(line) {
2185
- var ix = indexOf(this.marker.set, line);
2186
- if (ix > -1) this.marker.set.splice(ix, 1);
2187
- },
2188
- split: function(pos, lenBefore) {
2189
- if (this.to <= pos && this.to != null) return null;
2190
- var from = this.from < pos || this.from == null ? null : this.from - pos + lenBefore;
2191
- var to = this.to == null ? null : this.to - pos + lenBefore;
2192
- return new MarkedText(from, to, this.style, this.marker);
2193
- },
2194
- dup: function() { return new MarkedText(null, null, this.style, this.marker); },
2195
- clipTo: function(fromOpen, from, toOpen, to, diff) {
2196
- if (fromOpen && to > this.from && (to < this.to || this.to == null))
2197
- this.from = null;
2198
- else if (this.from != null && this.from >= from)
2199
- this.from = Math.max(to, this.from) + diff;
2200
- if (toOpen && (from < this.to || this.to == null) && (from > this.from || this.from == null))
2201
- this.to = null;
2202
- else if (this.to != null && this.to > from)
2203
- this.to = to < this.to ? this.to + diff : from;
2204
- },
2205
- isDead: function() { return this.from != null && this.to != null && this.from >= this.to; },
2206
- sameSet: function(x) { return this.marker == x.marker; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2207
  };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2208
 
2209
- function Bookmark(pos) {
2210
- this.from = pos; this.to = pos; this.line = null;
 
 
 
2211
  }
2212
- Bookmark.prototype = {
2213
- attach: function(line) { this.line = line; },
2214
- detach: function(line) { if (this.line == line) this.line = null; },
2215
- split: function(pos, lenBefore) {
2216
- if (pos < this.from) {
2217
- this.from = this.to = (this.from - pos) + lenBefore;
2218
- return this;
2219
- }
2220
- },
2221
- isDead: function() { return this.from > this.to; },
2222
- clipTo: function(fromOpen, from, toOpen, to, diff) {
2223
- if ((fromOpen || from < this.from) && (toOpen || to > this.to)) {
2224
- this.from = 0; this.to = -1;
2225
- } else if (this.from > from) {
2226
- this.from = this.to = Math.max(to, this.from) + diff;
2227
- }
2228
- },
2229
- sameSet: function(x) { return false; },
2230
- find: function() {
2231
- if (!this.line || !this.line.parent) return null;
2232
- return {line: lineNo(this.line), ch: this.from};
2233
- },
2234
- clear: function() {
2235
- if (this.line) {
2236
- var found = indexOf(this.line.marked, this);
2237
- if (found != -1) this.line.marked.splice(found, 1);
2238
- this.line = null;
2239
  }
2240
- }
2241
- };
 
 
 
 
2242
 
2243
  // Line objects. These hold state related to a line, including
2244
  // highlighting info (the styles array).
2245
- function Line(text, styles) {
2246
- this.styles = styles || [text, null];
2247
- this.text = text;
2248
- this.height = 1;
2249
- this.marked = this.gutterMarker = this.className = this.bgClassName = this.handlers = null;
2250
- this.stateAfter = this.parent = this.hidden = null;
2251
- }
2252
- Line.inheritMarks = function(text, orig) {
2253
- var ln = new Line(text), mk = orig && orig.marked;
2254
- if (mk) {
2255
- for (var i = 0; i < mk.length; ++i) {
2256
- if (mk[i].to == null && mk[i].style) {
2257
- var newmk = ln.marked || (ln.marked = []), mark = mk[i];
2258
- var nmark = mark.dup(); newmk.push(nmark); nmark.attach(ln);
2259
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2260
  }
 
 
 
 
 
 
2261
  }
2262
- return ln;
2263
- }
2264
- Line.prototype = {
2265
- // Replace a piece of a line, keeping the styles around it intact.
2266
- replace: function(from, to_, text) {
2267
- var st = [], mk = this.marked, to = to_ == null ? this.text.length : to_;
2268
- copyStyles(0, from, this.styles, st);
2269
- if (text) st.push(text, null);
2270
- copyStyles(to, this.text.length, this.styles, st);
2271
- this.styles = st;
2272
- this.text = this.text.slice(0, from) + text + this.text.slice(to);
2273
- this.stateAfter = null;
2274
- if (mk) {
2275
- var diff = text.length - (to - from);
2276
- for (var i = 0; i < mk.length; ++i) {
2277
- var mark = mk[i];
2278
- mark.clipTo(from == null, from || 0, to_ == null, to, diff);
2279
- if (mark.isDead()) {mark.detach(this); mk.splice(i--, 1);}
2280
- }
2281
- }
2282
- },
2283
- // Split a part off a line, keeping styles and markers intact.
2284
- split: function(pos, textBefore) {
2285
- var st = [textBefore, null], mk = this.marked;
2286
- copyStyles(pos, this.text.length, this.styles, st);
2287
- var taken = new Line(textBefore + this.text.slice(pos), st);
2288
- if (mk) {
2289
- for (var i = 0; i < mk.length; ++i) {
2290
- var mark = mk[i];
2291
- var newmark = mark.split(pos, textBefore.length);
2292
- if (newmark) {
2293
- if (!taken.marked) taken.marked = [];
2294
- taken.marked.push(newmark); newmark.attach(taken);
2295
- if (newmark == mark) mk.splice(i--, 1);
2296
  }
 
2297
  }
2298
- }
2299
- return taken;
2300
- },
2301
- append: function(line) {
2302
- var mylen = this.text.length, mk = line.marked, mymk = this.marked;
2303
- this.text += line.text;
2304
- copyStyles(0, line.text.length, line.styles, this.styles);
2305
- if (mymk) {
2306
- for (var i = 0; i < mymk.length; ++i)
2307
- if (mymk[i].to == null) mymk[i].to = mylen;
2308
- }
2309
- if (mk && mk.length) {
2310
- if (!mymk) this.marked = mymk = [];
2311
- outer: for (var i = 0; i < mk.length; ++i) {
2312
- var mark = mk[i];
2313
- if (!mark.from) {
2314
- for (var j = 0; j < mymk.length; ++j) {
2315
- var mymark = mymk[j];
2316
- if (mymark.to == mylen && mymark.sameSet(mark)) {
2317
- mymark.to = mark.to == null ? null : mark.to + mylen;
2318
- if (mymark.isDead()) {
2319
- mymark.detach(this);
2320
- mk.splice(i--, 1);
2321
- }
2322
- continue outer;
2323
- }
2324
- }
2325
  }
2326
- mymk.push(mark);
2327
- mark.attach(this);
2328
- mark.from += mylen;
2329
- if (mark.to != null) mark.to += mylen;
2330
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2331
  }
2332
- },
2333
- fixMarkEnds: function(other) {
2334
- var mk = this.marked, omk = other.marked;
2335
- if (!mk) return;
2336
- for (var i = 0; i < mk.length; ++i) {
2337
- var mark = mk[i], close = mark.to == null;
2338
- if (close && omk) {
2339
- for (var j = 0; j < omk.length; ++j)
2340
- if (omk[j].sameSet(mark)) {close = false; break;}
2341
- }
2342
- if (close) mark.to = this.text.length;
2343
  }
2344
- },
2345
- fixMarkStarts: function() {
2346
- var mk = this.marked;
2347
- if (!mk) return;
2348
- for (var i = 0; i < mk.length; ++i)
2349
- if (mk[i].from == null) mk[i].from = 0;
2350
- },
2351
- addMark: function(mark) {
2352
- mark.attach(this);
2353
- if (this.marked == null) this.marked = [];
2354
- this.marked.push(mark);
2355
- this.marked.sort(function(a, b){return (a.from || 0) - (b.from || 0);});
2356
- },
2357
- // Run the given mode's parser over a line, update the styles
2358
- // array, which contains alternating fragments of text and CSS
2359
- // classes.
2360
- highlight: function(mode, state, tabSize) {
2361
- var stream = new StringStream(this.text, tabSize), st = this.styles, pos = 0;
2362
- var changed = false, curWord = st[0], prevWord;
2363
- if (this.text == "" && mode.blankLine) mode.blankLine(state);
2364
- while (!stream.eol()) {
2365
- var style = mode.token(stream, state);
2366
- var substr = this.text.slice(stream.start, stream.pos);
2367
- stream.start = stream.pos;
2368
- if (pos && st[pos-1] == style)
2369
- st[pos-2] += substr;
2370
- else if (substr) {
2371
- if (!changed && (st[pos+1] != style || (pos && st[pos-2] != prevWord))) changed = true;
2372
- st[pos++] = substr; st[pos++] = style;
2373
- prevWord = curWord; curWord = st[pos];
2374
  }
2375
- // Give up when line is ridiculously long
2376
- if (stream.pos > 5000) {
2377
- st[pos++] = this.text.slice(stream.pos); st[pos++] = null;
2378
- break;
 
 
 
 
 
 
 
2379
  }
2380
  }
2381
- if (st.length != pos) {st.length = pos; changed = true;}
2382
- if (pos && st[pos-2] != prevWord) changed = true;
2383
- // Short lines with simple highlights return null, and are
2384
- // counted as changed by the driver because they are likely to
2385
- // highlight the same way in various contexts.
2386
- return changed || (st.length < 5 && this.text.length < 10 ? null : false);
2387
- },
2388
- // Fetch the parser token for a given character. Useful for hacks
2389
- // that want to inspect the mode state (say, for completion).
2390
- getTokenAt: function(mode, state, ch) {
2391
- var txt = this.text, stream = new StringStream(txt);
2392
- while (stream.pos < ch && !stream.eol()) {
2393
- stream.start = stream.pos;
2394
- var style = mode.token(stream, state);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2395
  }
2396
- return {start: stream.start,
2397
- end: stream.pos,
2398
- string: stream.current(),
2399
- className: style || null,
2400
- state: state};
2401
- },
2402
- indentation: function(tabSize) {return countColumn(this.text, null, tabSize);},
2403
- // Produces an HTML fragment for the line, taking selection,
2404
- // marking, and highlighting into account.
2405
- getHTML: function(makeTab, endAt) {
2406
- var html = [], first = true, col = 0;
2407
- function span(text, style) {
2408
- if (!text) return;
2409
- // Work around a bug where, in some compat modes, IE ignores leading spaces
2410
- if (first && ie && text.charAt(0) == " ") text = "\u00a0" + text.slice(1);
2411
- first = false;
2412
- if (text.indexOf("\t") == -1) {
2413
- col += text.length;
2414
- var escaped = htmlEscape(text);
2415
- } else {
2416
- var escaped = "";
2417
- for (var pos = 0;;) {
2418
- var idx = text.indexOf("\t", pos);
2419
- if (idx == -1) {
2420
- escaped += htmlEscape(text.slice(pos));
2421
- col += text.length - pos;
2422
- break;
2423
- } else {
2424
- col += idx - pos;
2425
- var tab = makeTab(col);
2426
- escaped += htmlEscape(text.slice(pos, idx)) + tab.html;
2427
- col += tab.width;
2428
- pos = idx + 1;
2429
- }
2430
  }
 
 
2431
  }
2432
- if (style) html.push('<span class="', style, '">', escaped, "</span>");
2433
- else html.push(escaped);
2434
- }
2435
- var st = this.styles, allText = this.text, marked = this.marked;
2436
- var len = allText.length;
2437
- if (endAt != null) len = Math.min(endAt, len);
2438
- function styleToClass(style) {
2439
- if (!style) return null;
2440
- return "cm-" + style.replace(/ +/g, " cm-");
2441
- }
2442
-
2443
- if (!allText && endAt == null)
2444
- span(" ");
2445
- else if (!marked || !marked.length)
2446
- for (var i = 0, ch = 0; ch < len; i+=2) {
2447
- var str = st[i], style = st[i+1], l = str.length;
2448
- if (ch + l > len) str = str.slice(0, len - ch);
2449
- ch += l;
2450
- span(str, styleToClass(style));
2451
- }
2452
- else {
2453
- var pos = 0, i = 0, text = "", style, sg = 0;
2454
- var nextChange = marked[0].from || 0, marks = [], markpos = 0;
2455
- function advanceMarks() {
2456
- var m;
2457
- while (markpos < marked.length &&
2458
- ((m = marked[markpos]).from == pos || m.from == null)) {
2459
- if (m.style != null) marks.push(m);
2460
- ++markpos;
2461
- }
2462
- nextChange = markpos < marked.length ? marked[markpos].from : Infinity;
2463
- for (var i = 0; i < marks.length; ++i) {
2464
- var to = marks[i].to || Infinity;
2465
- if (to == pos) marks.splice(i--, 1);
2466
- else nextChange = Math.min(to, nextChange);
2467
- }
2468
  }
2469
- var m = 0;
2470
- while (pos < len) {
2471
- if (nextChange == pos) advanceMarks();
2472
- var upto = Math.min(len, nextChange);
2473
- while (true) {
2474
- if (text) {
2475
- var end = pos + text.length;
2476
- var appliedStyle = style;
2477
- for (var j = 0; j < marks.length; ++j)
2478
- appliedStyle = (appliedStyle ? appliedStyle + " " : "") + marks[j].style;
2479
- span(end > upto ? text.slice(0, upto - pos) : text, appliedStyle);
2480
- if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;}
2481
- pos = end;
2482
- }
2483
- text = st[i++]; style = styleToClass(st[i++]);
2484
  }
 
 
 
2485
  }
 
2486
  }
2487
- return html.join("");
2488
- },
2489
- cleanUp: function() {
2490
- this.parent = null;
2491
- if (this.marked)
2492
- for (var i = 0, e = this.marked.length; i < e; ++i) this.marked[i].detach(this);
2493
- }
2494
- };
2495
- // Utility used by replace and split above
2496
- function copyStyles(from, to, source, dest) {
2497
- for (var i = 0, pos = 0, state = 0; pos < to; i+=2) {
2498
- var part = source[i], end = pos + part.length;
2499
- if (state == 0) {
2500
- if (end > from) dest.push(part.slice(from - pos, Math.min(part.length, to - pos)), source[i+1]);
2501
- if (end >= from) state = 1;
2502
- }
2503
- else if (state == 1) {
2504
- if (end > to) dest.push(part.slice(0, to - pos), source[i+1]);
2505
- else dest.push(part, source[i+1]);
2506
- }
2507
- pos = end;
2508
  }
2509
  }
2510
 
2511
- // Data structure that holds the sequence of lines.
 
2512
  function LeafChunk(lines) {
2513
  this.lines = lines;
2514
  this.parent = null;
@@ -2518,15 +3970,15 @@ var CodeMirror = (function() {
2518
  }
2519
  this.height = height;
2520
  }
 
2521
  LeafChunk.prototype = {
2522
  chunkSize: function() { return this.lines.length; },
2523
- remove: function(at, n, callbacks) {
2524
  for (var i = at, e = at + n; i < e; ++i) {
2525
  var line = this.lines[i];
2526
  this.height -= line.height;
2527
- line.cleanUp();
2528
- if (line.handlers)
2529
- for (var j = 0; j < line.handlers.length; ++j) callbacks.push(line.handlers[j]);
2530
  }
2531
  this.lines.splice(at, n);
2532
  },
@@ -2535,7 +3987,7 @@ var CodeMirror = (function() {
2535
  },
2536
  insertHeight: function(at, lines, height) {
2537
  this.height += height;
2538
- this.lines.splice.apply(this.lines, [at, 0].concat(lines));
2539
  for (var i = 0, e = lines.length; i < e; ++i) lines[i].parent = this;
2540
  },
2541
  iterN: function(at, n, op) {
@@ -2543,6 +3995,7 @@ var CodeMirror = (function() {
2543
  if (op(this.lines[at])) return true;
2544
  }
2545
  };
 
2546
  function BranchChunk(children) {
2547
  this.children = children;
2548
  var size = 0, height = 0;
@@ -2555,6 +4008,7 @@ var CodeMirror = (function() {
2555
  this.height = height;
2556
  this.parent = null;
2557
  }
 
2558
  BranchChunk.prototype = {
2559
  chunkSize: function() { return this.size; },
2560
  remove: function(at, n, callbacks) {
@@ -2642,7 +4096,9 @@ var CodeMirror = (function() {
2642
  }
2643
  };
2644
 
2645
- function getLineAt(chunk, n) {
 
 
2646
  while (!chunk.lines) {
2647
  for (var i = 0;; ++i) {
2648
  var child = chunk.children[i], sz = child.chunkSize();
@@ -2652,17 +4108,29 @@ var CodeMirror = (function() {
2652
  }
2653
  return chunk.lines[n];
2654
  }
 
 
 
 
 
 
2655
  function lineNo(line) {
2656
  if (line.parent == null) return null;
2657
  var cur = line.parent, no = indexOf(cur.lines, line);
2658
  for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {
2659
- for (var i = 0, e = chunk.children.length; ; ++i) {
2660
  if (chunk.children[i] == cur) break;
2661
  no += chunk.children[i].chunkSize();
2662
  }
2663
  }
2664
  return no;
2665
  }
 
 
 
 
 
 
2666
  function lineAtHeight(chunk, h) {
2667
  var n = 0;
2668
  outer: do {
@@ -2681,55 +4149,92 @@ var CodeMirror = (function() {
2681
  }
2682
  return n + i;
2683
  }
2684
- function heightAtLine(chunk, n) {
2685
- var h = 0;
2686
- outer: do {
2687
- for (var i = 0, e = chunk.children.length; i < e; ++i) {
2688
- var child = chunk.children[i], sz = child.chunkSize();
2689
- if (n < sz) { chunk = child; continue outer; }
2690
- n -= sz;
2691
- h += child.height;
 
 
 
 
 
 
 
2692
  }
2693
- return h;
2694
- } while (!chunk.lines);
2695
- for (var i = 0; i < n; ++i) h += chunk.lines[i].height;
2696
  return h;
2697
  }
2698
 
2699
- // The history object 'chunks' changes that are made close together
2700
- // and at almost the same time into bigger undoable units.
2701
- function History() {
2702
- this.time = 0;
2703
- this.done = []; this.undone = [];
2704
- }
2705
- History.prototype = {
2706
- addChange: function(start, added, old) {
2707
- this.undone.length = 0;
2708
- var time = +new Date, cur = this.done[this.done.length - 1], last = cur && cur[cur.length - 1];
2709
- var dtime = time - this.time;
2710
- if (dtime > 400 || !last) {
2711
- this.done.push([{start: start, added: added, old: old}]);
2712
- } else if (last.start > start + old.length || last.start + last.added < start - last.added + last.old.length) {
2713
- cur.push({start: start, added: added, old: old});
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2714
  } else {
2715
- var oldoff = 0;
2716
- if (start < last.start) {
2717
- for (var i = last.start - start - 1; i >= 0; --i)
2718
- last.old.unshift(old[i]);
2719
- oldoff = Math.min(0, added - old.length);
2720
- last.added += last.start - start + oldoff;
2721
- last.start = start;
2722
- } else if (last.start < start) {
2723
- oldoff = start - last.start;
2724
- added += oldoff;
2725
- }
2726
- for (var i = last.added - oldoff, e = old.length; i < e; ++i)
2727
- last.old.push(old[i]);
2728
- if (last.added < added) last.added = added;
2729
  }
2730
- this.time = time;
 
 
 
 
 
 
 
 
 
 
 
 
 
2731
  }
2732
- };
 
 
 
 
 
2733
 
2734
  function stopMethod() {e_stop(this);}
2735
  // Ensure an event has a stop method.
@@ -2753,10 +4258,14 @@ var CodeMirror = (function() {
2753
 
2754
  function e_target(e) {return e.target || e.srcElement;}
2755
  function e_button(e) {
2756
- if (e.which) return e.which;
2757
- else if (e.button & 1) return 1;
2758
- else if (e.button & 2) return 3;
2759
- else if (e.button & 4) return 2;
 
 
 
 
2760
  }
2761
 
2762
  // Allow 3rd-party code to override event properties by adding an override
@@ -2766,49 +4275,68 @@ var CodeMirror = (function() {
2766
  return overridden ? e.override[prop] : e[prop];
2767
  }
2768
 
2769
- // Event handler registration. If disconnect is true, it'll return a
2770
- // function that unregisters the handler.
2771
- function connect(node, type, handler, disconnect) {
2772
- if (typeof node.addEventListener == "function") {
2773
- node.addEventListener(type, handler, false);
2774
- if (disconnect) return function() {node.removeEventListener(type, handler, false);};
 
 
 
 
 
2775
  }
 
 
 
 
 
 
 
2776
  else {
2777
- var wrapHandler = function(event) {handler(event || window.event);};
2778
- node.attachEvent("on" + type, wrapHandler);
2779
- if (disconnect) return function() {node.detachEvent("on" + type, wrapHandler);};
 
2780
  }
2781
  }
2782
- CodeMirror.connect = connect;
2783
 
2784
- function Delayed() {this.id = null;}
2785
- Delayed.prototype = {set: function(ms, f) {clearTimeout(this.id); this.id = setTimeout(f, ms);}};
 
 
 
 
2786
 
2787
- var Pass = CodeMirror.Pass = {toString: function(){return "CodeMirror.Pass";}};
 
 
 
 
 
 
 
 
2788
 
2789
- var gecko = /gecko\/\d{7}/i.test(navigator.userAgent);
2790
- var ie = /MSIE \d/.test(navigator.userAgent);
2791
- var ie_lt9 = /MSIE [1-8]\b/.test(navigator.userAgent);
2792
- var webkit = /WebKit\//.test(navigator.userAgent);
2793
- var chrome = /Chrome\//.test(navigator.userAgent);
2794
- var khtml = /KHTML\//.test(navigator.userAgent);
2795
 
2796
- // Detect drag-and-drop
2797
- var dragAndDrop = function() {
2798
- // There is *some* kind of drag-and-drop support in IE6-8, but I
2799
- // couldn't get it to work yet.
2800
- if (ie_lt9) return false;
2801
- var div = document.createElement('div');
2802
- return "draggable" in div || "dragDrop" in div;
2803
- }();
 
 
2804
 
2805
- var lineSep = "\n";
2806
- // Feature-detect whether newlines in textareas are converted to \r\n
2807
- (function () {
2808
- var te = document.createElement("textarea");
2809
- te.value = "foo\nbar";
2810
- if (te.value.indexOf("\r") > -1) lineSep = "\r\n";
2811
- }());
2812
 
2813
  // Counts the column offset in a string, taking tabs into account.
2814
  // Used mostly to find indentation.
@@ -2823,53 +4351,17 @@ var CodeMirror = (function() {
2823
  }
2824
  return n;
2825
  }
 
2826
 
2827
- function computedStyle(elt) {
2828
- if (elt.currentStyle) return elt.currentStyle;
2829
- return window.getComputedStyle(elt, null);
2830
- }
2831
-
2832
- // Find the position of an element by following the offsetParent chain.
2833
- // If screen==true, it returns screen (rather than page) coordinates.
2834
- function eltOffset(node, screen) {
2835
- var bod = node.ownerDocument.body;
2836
- var x = 0, y = 0, skipBody = false;
2837
- for (var n = node; n; n = n.offsetParent) {
2838
- var ol = n.offsetLeft, ot = n.offsetTop;
2839
- // Firefox reports weird inverted offsets when the body has a border.
2840
- if (n == bod) { x += Math.abs(ol); y += Math.abs(ot); }
2841
- else { x += ol, y += ot; }
2842
- if (screen && computedStyle(n).position == "fixed")
2843
- skipBody = true;
2844
- }
2845
- var e = screen && !skipBody ? null : bod;
2846
- for (var n = node.parentNode; n != e; n = n.parentNode)
2847
- if (n.scrollLeft != null) { x -= n.scrollLeft; y -= n.scrollTop;}
2848
- return {left: x, top: y};
2849
- }
2850
- // Use the faster and saner getBoundingClientRect method when possible.
2851
- if (document.documentElement.getBoundingClientRect != null) eltOffset = function(node, screen) {
2852
- // Take the parts of bounding client rect that we are interested in so we are able to edit if need be,
2853
- // since the returned value cannot be changed externally (they are kept in sync as the element moves within the page)
2854
- try { var box = node.getBoundingClientRect(); box = { top: box.top, left: box.left }; }
2855
- catch(e) { box = {top: 0, left: 0}; }
2856
- if (!screen) {
2857
- // Get the toplevel scroll, working around browser differences.
2858
- if (window.pageYOffset == null) {
2859
- var t = document.documentElement || document.body.parentNode;
2860
- if (t.scrollTop == null) t = document.body;
2861
- box.top += t.scrollTop; box.left += t.scrollLeft;
2862
- } else {
2863
- box.top += window.pageYOffset; box.left += window.pageXOffset;
2864
- }
2865
- }
2866
- return box;
2867
- };
2868
-
2869
- // Get a node's text content.
2870
- function eltText(node) {
2871
- return node.textContent || node.innerText || node.nodeValue || "";
2872
  }
 
 
 
2873
  function selectInput(node) {
2874
  if (ios) { // Mobile Safari apparently has a bug where select() is broken.
2875
  node.selectionStart = 0;
@@ -2877,63 +4369,130 @@ var CodeMirror = (function() {
2877
  } else node.select();
2878
  }
2879
 
2880
- // Operations on {line, ch} objects.
2881
- function posEq(a, b) {return a.line == b.line && a.ch == b.ch;}
2882
- function posLess(a, b) {return a.line < b.line || (a.line == b.line && a.ch < b.ch);}
2883
- function copyPos(x) {return {line: x.line, ch: x.ch};}
2884
-
2885
- var escapeElement = document.createElement("pre");
2886
- function htmlEscape(str) {
2887
- escapeElement.textContent = str;
2888
- return escapeElement.innerHTML;
2889
- }
2890
- // Recent (late 2011) Opera betas insert bogus newlines at the start
2891
- // of the textContent, so we strip those.
2892
- if (htmlEscape("a") == "\na")
2893
- htmlEscape = function(str) {
2894
- escapeElement.textContent = str;
2895
- return escapeElement.innerHTML.slice(1);
2896
- };
2897
- // Some IEs don't preserve tabs through innerHTML
2898
- else if (htmlEscape("\t") != "\t")
2899
- htmlEscape = function(str) {
2900
- escapeElement.innerHTML = "";
2901
- escapeElement.appendChild(document.createTextNode(str));
2902
- return escapeElement.innerHTML;
2903
- };
2904
- CodeMirror.htmlEscape = htmlEscape;
2905
-
2906
- // Used to position the cursor after an undo/redo by finding the
2907
- // last edited character.
2908
- function editEnd(from, to) {
2909
- if (!to) return 0;
2910
- if (!from) return to.length;
2911
- for (var i = from.length, j = to.length; i >= 0 && j >= 0; --i, --j)
2912
- if (from.charAt(i) != to.charAt(j)) break;
2913
- return j + 1;
2914
- }
2915
-
2916
  function indexOf(collection, elt) {
2917
  if (collection.indexOf) return collection.indexOf(elt);
2918
  for (var i = 0, e = collection.length; i < e; ++i)
2919
  if (collection[i] == elt) return i;
2920
  return -1;
2921
  }
 
 
 
 
 
 
 
 
 
 
 
 
2922
  function isWordChar(ch) {
2923
- return /\w/.test(ch) || ch.toUpperCase() != ch.toLowerCase();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2924
  }
2925
 
2926
  // See if "".split is the broken IE version, if so, provide an
2927
  // alternative way to split lines.
2928
  var splitLines = "\n\nb".split(/\n/).length != 3 ? function(string) {
2929
- var pos = 0, nl, result = [];
2930
- while ((nl = string.indexOf("\n", pos)) > -1) {
2931
- result.push(string.slice(pos, string.charAt(nl-1) == "\r" ? nl - 1 : nl));
2932
- pos = nl + 1;
 
 
 
 
 
 
 
 
 
2933
  }
2934
- result.push(string.slice(pos));
2935
  return result;
2936
- } : function(string){return string.split(/\r?\n/);};
2937
  CodeMirror.splitLines = splitLines;
2938
 
2939
  var hasSelection = window.getSelection ? function(te) {
@@ -2946,18 +4505,22 @@ var CodeMirror = (function() {
2946
  return range.compareEndPoints("StartToEnd", range) != 0;
2947
  };
2948
 
2949
- CodeMirror.defineMode("null", function() {
2950
- return {token: function(stream) {stream.skipToEnd();}};
2951
- });
2952
- CodeMirror.defineMIME("text/plain", "null");
 
 
 
 
2953
 
2954
  var keyNames = {3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
2955
  19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
2956
  36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
2957
- 46: "Delete", 59: ";", 91: "Mod", 92: "Mod", 93: "Mod", 127: "Delete", 186: ";", 187: "=", 188: ",",
2958
- 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\", 221: "]", 222: "'", 63276: "PageUp",
2959
- 63277: "PageDown", 63275: "End", 63273: "Home", 63234: "Left", 63232: "Up", 63235: "Right",
2960
- 63233: "Down", 63302: "Insert", 63272: "Delete"};
2961
  CodeMirror.keyNames = keyNames;
2962
  (function() {
2963
  // Number keys
@@ -2968,5 +4531,256 @@ var CodeMirror = (function() {
2968
  for (var i = 1; i <= 12; i++) keyNames[i + 111] = keyNames[i + 63235] = "F" + i;
2969
  })();
2970
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2971
  return CodeMirror;
2972
  })();
1
+ // CodeMirror version 3.02
2
  //
 
 
 
 
3
  // CodeMirror is the only global var we claim
4
+ window.CodeMirror = (function() {
5
+ "use strict";
6
+
7
+ // BROWSER SNIFFING
8
+
9
+ // Crude, but necessary to handle a number of hard-to-feature-detect
10
+ // bugs and behavior differences.
11
+ var gecko = /gecko\/\d/i.test(navigator.userAgent);
12
+ var ie = /MSIE \d/.test(navigator.userAgent);
13
+ var ie_lt8 = ie && (document.documentMode == null || document.documentMode < 8);
14
+ var ie_lt9 = ie && (document.documentMode == null || document.documentMode < 9);
15
+ var webkit = /WebKit\//.test(navigator.userAgent);
16
+ var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(navigator.userAgent);
17
+ var chrome = /Chrome\//.test(navigator.userAgent);
18
+ var opera = /Opera\//.test(navigator.userAgent);
19
+ var safari = /Apple Computer/.test(navigator.vendor);
20
+ var khtml = /KHTML\//.test(navigator.userAgent);
21
+ var mac_geLion = /Mac OS X 1\d\D([7-9]|\d\d)\D/.test(navigator.userAgent);
22
+ var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(navigator.userAgent);
23
+ var phantom = /PhantomJS/.test(navigator.userAgent);
24
+
25
+ var ios = /AppleWebKit/.test(navigator.userAgent) && /Mobile\/\w+/.test(navigator.userAgent);
26
+ // This is woefully incomplete. Suggestions for alternative methods welcome.
27
+ var mobile = ios || /Android|webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(navigator.userAgent);
28
+ var mac = ios || /Mac/.test(navigator.platform);
29
+ var windows = /windows/i.test(navigator.platform);
30
+
31
+ var opera_version = opera && navigator.userAgent.match(/Version\/(\d*\.\d*)/);
32
+ if (opera_version) opera_version = Number(opera_version[1]);
33
+ // Some browsers use the wrong event properties to signal cmd/ctrl on OS X
34
+ var flipCtrlCmd = mac && (qtwebkit || opera && (opera_version == null || opera_version < 12.11));
35
+
36
+ // Optimize some code when these features are not used
37
+ var sawReadOnlySpans = false, sawCollapsedSpans = false;
38
+
39
+ // CONSTRUCTOR
40
+
41
+ function CodeMirror(place, options) {
42
+ if (!(this instanceof CodeMirror)) return new CodeMirror(place, options);
43
+
44
+ this.options = options = options || {};
45
  // Determine effective options based on given values and defaults.
46
+ for (var opt in defaults) if (!options.hasOwnProperty(opt) && defaults.hasOwnProperty(opt))
47
+ options[opt] = defaults[opt];
48
+ setGuttersForLineNumbers(options);
49
+
50
+ var display = this.display = makeDisplay(place);
51
+ display.wrapper.CodeMirror = this;
52
+ updateGutters(this);
53
+ if (options.autofocus && !mobile) focusInput(this);
54
+
55
+ this.view = makeView(new BranchChunk([new LeafChunk([makeLine("", null, textHeight(display))])]));
56
+ this.nextOpId = 0;
57
+ loadMode(this);
58
+ themeChanged(this);
59
+ if (options.lineWrapping)
60
+ this.display.wrapper.className += " CodeMirror-wrap";
61
+
62
+ // Initialize the content.
63
+ this.setValue(options.value || "");
64
+ // Override magic textarea content restore that IE sometimes does
65
+ // on our hidden textarea on reload
66
+ if (ie) setTimeout(bind(resetInput, this, true), 20);
67
+ this.view.history = makeHistory();
68
 
69
+ registerEventHandlers(this);
70
+ // IE throws unspecified error in certain cases, when
71
+ // trying to access activeElement before onload
72
+ var hasFocus; try { hasFocus = (document.activeElement == display.input); } catch(e) { }
73
+ if (hasFocus || (options.autofocus && !mobile)) setTimeout(bind(onFocus, this), 20);
74
+ else onBlur(this);
75
+
76
+ operation(this, function() {
77
+ for (var opt in optionHandlers)
78
+ if (optionHandlers.propertyIsEnumerable(opt))
79
+ optionHandlers[opt](this, options[opt], Init);
80
+ for (var i = 0; i < initHooks.length; ++i) initHooks[i](this);
81
+ })();
82
+ }
83
+
84
+ // DISPLAY CONSTRUCTOR
85
+
86
+ function makeDisplay(place) {
87
+ var d = {};
88
+ var input = d.input = elt("textarea", null, null, "position: absolute; padding: 0; width: 1px; height: 1em; outline: none;");
89
+ if (webkit) input.style.width = "1000px";
90
+ else input.setAttribute("wrap", "off");
91
+ input.setAttribute("autocorrect", "off"); input.setAttribute("autocapitalize", "off");
92
+ // Wraps and hides input textarea
93
+ d.inputDiv = elt("div", [input], null, "overflow: hidden; position: relative; width: 3px; height: 0px;");
94
+ // The actual fake scrollbars.
95
+ d.scrollbarH = elt("div", [elt("div", null, null, "height: 1px")], "CodeMirror-hscrollbar");
96
+ d.scrollbarV = elt("div", [elt("div", null, null, "width: 1px")], "CodeMirror-vscrollbar");
97
+ d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler");
98
+ // DIVs containing the selection and the actual code
99
+ d.lineDiv = elt("div");
100
+ d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1");
101
+ // Blinky cursor, and element used to ensure cursor fits at the end of a line
102
+ d.cursor = elt("div", "\u00a0", "CodeMirror-cursor");
103
+ // Secondary cursor, shown when on a 'jump' in bi-directional text
104
+ d.otherCursor = elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor");
105
+ // Used to measure text size
106
+ d.measure = elt("div", null, "CodeMirror-measure");
107
+ // Wraps everything that needs to exist inside the vertically-padded coordinate system
108
+ d.lineSpace = elt("div", [d.measure, d.selectionDiv, d.lineDiv, d.cursor, d.otherCursor],
109
+ null, "position: relative; outline: none");
110
+ // Moved around its parent to cover visible view
111
+ d.mover = elt("div", [elt("div", [d.lineSpace], "CodeMirror-lines")], null, "position: relative");
112
+ // Set to the height of the text, causes scrolling
113
+ d.sizer = elt("div", [d.mover], "CodeMirror-sizer");
114
+ // D is needed because behavior of elts with overflow: auto and padding is inconsistent across browsers
115
+ d.heightForcer = elt("div", "\u00a0", null, "position: absolute; height: " + scrollerCutOff + "px");
116
+ // Will contain the gutters, if any
117
+ d.gutters = elt("div", null, "CodeMirror-gutters");
118
+ d.lineGutter = null;
119
+ // Helper element to properly size the gutter backgrounds
120
+ var scrollerInner = elt("div", [d.sizer, d.heightForcer, d.gutters], null, "position: relative; min-height: 100%");
121
+ // Provides scrolling
122
+ d.scroller = elt("div", [scrollerInner], "CodeMirror-scroll");
123
+ d.scroller.setAttribute("tabIndex", "-1");
124
  // The element in which the editor lives.
125
+ d.wrapper = elt("div", [d.inputDiv, d.scrollbarH, d.scrollbarV,
126
+ d.scrollbarFiller, d.scroller], "CodeMirror");
127
+ // Work around IE7 z-index bug
128
+ if (ie_lt8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; }
129
+ if (place.appendChild) place.appendChild(d.wrapper); else place(d.wrapper);
130
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
131
  // Needed to hide big blue blinking cursor on Mobile Safari
132
  if (ios) input.style.width = "0px";
133
+ if (!webkit) d.scroller.draggable = true;
 
 
 
 
134
  // Needed to handle Tab key in KHTML
135
+ if (khtml) { d.inputDiv.style.height = "1px"; d.inputDiv.style.position = "absolute"; }
136
+ // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
137
+ else if (ie_lt8) d.scrollbarH.style.minWidth = d.scrollbarV.style.minWidth = "18px";
138
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
139
  // Current visible range (may be bigger than the view window).
140
+ d.viewOffset = d.showingFrom = d.showingTo = d.lastSizeC = 0;
141
+
142
+ // Used to only resize the line number gutter when necessary (when
143
+ // the amount of lines crosses a boundary that makes its width change)
144
+ d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null;
145
+ // See readInput and resetInput
146
+ d.prevInput = "";
147
+ // Set to true when a non-horizontal-scrolling widget is added. As
148
+ // an optimization, widget aligning is skipped when d is false.
149
+ d.alignWidgets = false;
150
+ // Flag that indicates whether we currently expect input to appear
151
+ // (after some event like 'keypress' or 'input') and are polling
152
+ // intensively.
153
+ d.pollingFast = false;
154
+ // Self-resetting timeout for the poller
155
+ d.poll = new Delayed();
156
+ // True when a drag from the editor is active
157
+ d.draggingText = false;
158
+
159
+ d.cachedCharWidth = d.cachedTextHeight = null;
160
+ d.measureLineCache = [];
161
+ d.measureLineCachePos = 0;
162
+
163
+ // Tracks when resetInput has punted to just putting a short
164
+ // string instead of the (large) selection.
165
+ d.inaccurateSelection = false;
166
+
167
+ // Used to adjust overwrite behaviour when a paste has been
168
+ // detected
169
+ d.pasteIncoming = false;
170
+
171
+ // Used for measuring wheel scrolling granularity
172
+ d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null;
173
+
174
+ return d;
175
+ }
176
 
177
+ // VIEW CONSTRUCTOR
178
+
179
+ function makeView(doc) {
180
+ var selPos = {line: 0, ch: 0};
181
+ return {
182
+ doc: doc,
183
+ // frontier is the point up to which the content has been parsed,
184
+ frontier: 0, highlight: new Delayed(),
185
+ sel: {from: selPos, to: selPos, head: selPos, anchor: selPos, shift: false, extend: false},
186
+ scrollTop: 0, scrollLeft: 0,
187
+ overwrite: false, focused: false,
188
+ // Tracks the maximum line length so that
189
+ // the horizontal scrollbar can be kept
190
+ // static when scrolling.
191
+ maxLine: getLine(doc, 0),
192
+ maxLineLength: 0,
193
+ maxLineChanged: false,
194
+ suppressEdits: false,
195
+ goalColumn: null,
196
+ cantEdit: false,
197
+ keyMaps: [],
198
+ overlays: [],
199
+ modeGen: 0
200
+ };
201
+ }
202
+
203
+ // STATE UPDATES
204
+
205
+ // Used to get the editor into a consistent state again when options change.
206
+
207
+ function loadMode(cm) {
208
+ var doc = cm.view.doc;
209
+ cm.view.mode = CodeMirror.getMode(cm.options, cm.options.mode);
210
+ doc.iter(0, doc.size, function(line) {
211
+ if (line.stateAfter) line.stateAfter = null;
212
+ if (line.styles) line.styles = null;
213
  });
214
+ cm.view.frontier = 0;
215
+ startWorker(cm, 100);
216
+ cm.view.modeGen++;
217
+ if (cm.curOp) regChange(cm, 0, doc.size);
218
+ }
 
 
 
 
 
 
 
 
 
 
 
219
 
220
+ function wrappingChanged(cm) {
221
+ var doc = cm.view.doc, th = textHeight(cm.display);
222
+ if (cm.options.lineWrapping) {
223
+ cm.display.wrapper.className += " CodeMirror-wrap";
224
+ var perLine = cm.display.scroller.clientWidth / charWidth(cm.display) - 3;
225
+ doc.iter(0, doc.size, function(line) {
226
+ if (line.height == 0) return;
227
+ var guess = Math.ceil(line.text.length / perLine) || 1;
228
+ if (guess != 1) updateLineHeight(line, guess * th);
229
+ });
230
+ cm.display.sizer.style.minWidth = "";
231
+ } else {
232
+ cm.display.wrapper.className = cm.display.wrapper.className.replace(" CodeMirror-wrap", "");
233
+ computeMaxLength(cm.view);
234
+ doc.iter(0, doc.size, function(line) {
235
+ if (line.height != 0) updateLineHeight(line, th);
236
+ });
237
+ }
238
+ regChange(cm, 0, doc.size);
239
+ clearCaches(cm);
240
+ setTimeout(function(){updateScrollbars(cm.display, cm.view.doc.height);}, 100);
241
+ }
242
+
243
+ function keyMapChanged(cm) {
244
+ var style = keyMap[cm.options.keyMap].style;
245
+ cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-keymap-\S+/g, "") +
246
+ (style ? " cm-keymap-" + style : "");
247
+ }
248
+
249
+ function themeChanged(cm) {
250
+ cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") +
251
+ cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-");
252
+ clearCaches(cm);
253
+ }
254
+
255
+ function guttersChanged(cm) {
256
+ updateGutters(cm);
257
+ updateDisplay(cm, true);
258
+ }
259
+
260
+ function updateGutters(cm) {
261
+ var gutters = cm.display.gutters, specs = cm.options.gutters;
262
+ removeChildren(gutters);
263
+ for (var i = 0; i < specs.length; ++i) {
264
+ var gutterClass = specs[i];
265
+ var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + gutterClass));
266
+ if (gutterClass == "CodeMirror-linenumbers") {
267
+ cm.display.lineGutter = gElt;
268
+ gElt.style.width = (cm.display.lineNumWidth || 1) + "px";
269
+ }
270
+ }
271
+ gutters.style.display = i ? "" : "none";
272
+ }
273
+
274
+ function lineLength(doc, line) {
275
+ if (line.height == 0) return 0;
276
+ var len = line.text.length, merged, cur = line;
277
+ while (merged = collapsedSpanAtStart(cur)) {
278
+ var found = merged.find();
279
+ cur = getLine(doc, found.from.line);
280
+ len += found.from.ch - found.to.ch;
281
+ }
282
+ cur = line;
283
+ while (merged = collapsedSpanAtEnd(cur)) {
284
+ var found = merged.find();
285
+ len -= cur.text.length - found.from.ch;
286
+ cur = getLine(doc, found.to.line);
287
+ len += cur.text.length - found.to.ch;
288
+ }
289
+ return len;
290
+ }
291
+
292
+ function computeMaxLength(view) {
293
+ view.maxLine = getLine(view.doc, 0);
294
+ view.maxLineLength = lineLength(view.doc, view.maxLine);
295
+ view.maxLineChanged = true;
296
+ view.doc.iter(1, view.doc.size, function(line) {
297
+ var len = lineLength(view.doc, line);
298
+ if (len > view.maxLineLength) {
299
+ view.maxLineLength = len;
300
+ view.maxLine = line;
301
+ }
302
  });
303
+ }
304
 
305
+ // Make sure the gutters options contains the element
306
+ // "CodeMirror-linenumbers" when the lineNumbers option is true.
307
+ function setGuttersForLineNumbers(options) {
308
+ var found = false;
309
+ for (var i = 0; i < options.gutters.length; ++i) {
310
+ if (options.gutters[i] == "CodeMirror-linenumbers") {
311
+ if (options.lineNumbers) found = true;
312
+ else options.gutters.splice(i--, 1);
313
+ }
314
+ }
315
+ if (!found && options.lineNumbers)
316
+ options.gutters.push("CodeMirror-linenumbers");
317
+ }
318
+
319
+ // SCROLLBARS
320
+
321
+ // Re-synchronize the fake scrollbars with the actual size of the
322
+ // content. Optionally force a scrollTop.
323
+ function updateScrollbars(d /* display */, docHeight) {
324
+ var totalHeight = docHeight + 2 * paddingTop(d);
325
+ d.sizer.style.minHeight = d.heightForcer.style.top = totalHeight + "px";
326
+ var scrollHeight = Math.max(totalHeight, d.scroller.scrollHeight);
327
+ var needsH = d.scroller.scrollWidth > d.scroller.clientWidth;
328
+ var needsV = scrollHeight > d.scroller.clientHeight;
329
+ if (needsV) {
330
+ d.scrollbarV.style.display = "block";
331
+ d.scrollbarV.style.bottom = needsH ? scrollbarWidth(d.measure) + "px" : "0";
332
+ d.scrollbarV.firstChild.style.height =
333
+ (scrollHeight - d.scroller.clientHeight + d.scrollbarV.clientHeight) + "px";
334
+ } else d.scrollbarV.style.display = "";
335
+ if (needsH) {
336
+ d.scrollbarH.style.display = "block";
337
+ d.scrollbarH.style.right = needsV ? scrollbarWidth(d.measure) + "px" : "0";
338
+ d.scrollbarH.firstChild.style.width =
339
+ (d.scroller.scrollWidth - d.scroller.clientWidth + d.scrollbarH.clientWidth) + "px";
340
+ } else d.scrollbarH.style.display = "";
341
+ if (needsH && needsV) {
342
+ d.scrollbarFiller.style.display = "block";
343
+ d.scrollbarFiller.style.height = d.scrollbarFiller.style.width = scrollbarWidth(d.measure) + "px";
344
+ } else d.scrollbarFiller.style.display = "";
345
+
346
+ if (mac_geLion && scrollbarWidth(d.measure) === 0)
347
+ d.scrollbarV.style.minWidth = d.scrollbarH.style.minHeight = mac_geMountainLion ? "18px" : "12px";
348
+ }
349
+
350
+ function visibleLines(display, doc, viewPort) {
351
+ var top = display.scroller.scrollTop, height = display.wrapper.clientHeight;
352
+ if (typeof viewPort == "number") top = viewPort;
353
+ else if (viewPort) {top = viewPort.top; height = viewPort.bottom - viewPort.top;}
354
+ top = Math.floor(top - paddingTop(display));
355
+ var bottom = Math.ceil(top + height);
356
+ return {from: lineAtHeight(doc, top), to: lineAtHeight(doc, bottom)};
357
+ }
358
+
359
+ // LINE NUMBERS
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
360
 
361
+ function alignHorizontally(cm) {
362
+ var display = cm.display;
363
+ if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) return;
364
+ var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.view.scrollLeft;
365
+ var gutterW = display.gutters.offsetWidth, l = comp + "px";
366
+ for (var n = display.lineDiv.firstChild; n; n = n.nextSibling) if (n.alignable) {
367
+ for (var i = 0, a = n.alignable; i < a.length; ++i) a[i].style.left = l;
368
  }
369
+ if (cm.options.fixedGutter)
370
+ display.gutters.style.left = (comp + gutterW) + "px";
371
+ }
372
 
373
+ function maybeUpdateLineNumberWidth(cm) {
374
+ if (!cm.options.lineNumbers) return false;
375
+ var doc = cm.view.doc, last = lineNumberFor(cm.options, doc.size - 1), display = cm.display;
376
+ if (last.length != display.lineNumChars) {
377
+ var test = display.measure.appendChild(elt("div", [elt("div", last)],
378
+ "CodeMirror-linenumber CodeMirror-gutter-elt"));
379
+ var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW;
380
+ display.lineGutter.style.width = "";
381
+ display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding);
382
+ display.lineNumWidth = display.lineNumInnerWidth + padding;
383
+ display.lineNumChars = display.lineNumInnerWidth ? last.length : -1;
384
+ display.lineGutter.style.width = display.lineNumWidth + "px";
385
+ return true;
386
  }
387
+ return false;
388
+ }
389
+
390
+ function lineNumberFor(options, i) {
391
+ return String(options.lineNumberFormatter(i + options.firstLineNumber));
392
+ }
393
+ function compensateForHScroll(display) {
394
+ return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left;
395
+ }
396
+
397
+ // DISPLAY DRAWING
398
+
399
+ function updateDisplay(cm, changes, viewPort) {
400
+ var oldFrom = cm.display.showingFrom, oldTo = cm.display.showingTo;
401
+ var updated = updateDisplayInner(cm, changes, viewPort);
402
+ if (updated) {
403
+ signalLater(cm, cm, "update", cm);
404
+ if (cm.display.showingFrom != oldFrom || cm.display.showingTo != oldTo)
405
+ signalLater(cm, cm, "viewportChange", cm, cm.display.showingFrom, cm.display.showingTo);
406
  }
407
+ updateSelection(cm);
408
+ updateScrollbars(cm.display, cm.view.doc.height);
409
+
410
+ return updated;
411
+ }
412
 
413
+ // Uses a set of changes plus the current scroll position to
414
+ // determine which DOM updates have to be made, and makes the
415
+ // updates.
416
+ function updateDisplayInner(cm, changes, viewPort) {
417
+ var display = cm.display, doc = cm.view.doc;
418
+ if (!display.wrapper.clientWidth) {
419
+ display.showingFrom = display.showingTo = display.viewOffset = 0;
420
+ return;
421
+ }
422
 
423
+ // Compute the new visible window
424
+ // If scrollTop is specified, use that to determine which lines
425
+ // to render instead of the current scrollbar position.
426
+ var visible = visibleLines(display, doc, viewPort);
427
+ // Bail out if the visible area is already rendered and nothing changed.
428
+ if (changes !== true && changes.length == 0 &&
429
+ visible.from > display.showingFrom && visible.to < display.showingTo)
430
+ return;
431
+
432
+ if (changes && maybeUpdateLineNumberWidth(cm))
433
+ changes = true;
434
+ var gutterW = display.sizer.style.marginLeft = display.gutters.offsetWidth + "px";
435
+ display.scrollbarH.style.left = cm.options.fixedGutter ? gutterW : "0";
436
+
437
+ // When merged lines are present, the line that needs to be
438
+ // redrawn might not be the one that was changed.
439
+ if (changes !== true && sawCollapsedSpans)
440
+ for (var i = 0; i < changes.length; ++i) {
441
+ var ch = changes[i], merged;
442
+ while (merged = collapsedSpanAtStart(getLine(doc, ch.from))) {
443
+ var from = merged.find().from.line;
444
+ if (ch.diff) ch.diff -= ch.from - from;
445
+ ch.from = from;
446
  }
447
+ }
448
 
449
+ // Used to determine which lines need their line numbers updated
450
+ var positionsChangedFrom = changes === true ? 0 : Infinity;
451
+ if (cm.options.lineNumbers && changes && changes !== true)
452
+ for (var i = 0; i < changes.length; ++i)
453
+ if (changes[i].diff) { positionsChangedFrom = changes[i].from; break; }
454
+
455
+ var from = Math.max(visible.from - cm.options.viewportMargin, 0);
456
+ var to = Math.min(doc.size, visible.to + cm.options.viewportMargin);
457
+ if (display.showingFrom < from && from - display.showingFrom < 20) from = display.showingFrom;
458
+ if (display.showingTo > to && display.showingTo - to < 20) to = Math.min(doc.size, display.showingTo);
459
+ if (sawCollapsedSpans) {
460
+ from = lineNo(visualLine(doc, getLine(doc, from)));
461
+ while (to < doc.size && lineIsHidden(getLine(doc, to))) ++to;
462
+ }
463
 
464
+ // Create a range of theoretically intact lines, and punch holes
465
+ // in that using the change info.
466
+ var intact = changes === true ? [] :
467
+ computeIntact([{from: display.showingFrom, to: display.showingTo}], changes);
468
+ // Clip off the parts that won't be visible
469
+ var intactLines = 0;
470
+ for (var i = 0; i < intact.length; ++i) {
471
+ var range = intact[i];
472
+ if (range.from < from) range.from = from;
473
+ if (range.to > to) range.to = to;
474
+ if (range.from >= range.to) intact.splice(i--, 1);
475
+ else intactLines += range.to - range.from;
476
+ }
477
+ if (intactLines == to - from && from == display.showingFrom && to == display.showingTo)
478
+ return;
479
+ intact.sort(function(a, b) {return a.from - b.from;});
480
+
481
+ var focused = document.activeElement;
482
+ if (intactLines < (to - from) * .7) display.lineDiv.style.display = "none";
483
+ patchDisplay(cm, from, to, intact, positionsChangedFrom);
484
+ display.lineDiv.style.display = "";
485
+ if (document.activeElement != focused && focused.offsetHeight) focused.focus();
486
+
487
+ var different = from != display.showingFrom || to != display.showingTo ||
488
+ display.lastSizeC != display.wrapper.clientHeight;
489
+ // This is just a bogus formula that detects when the editor is
490
+ // resized or the font size changes.
491
+ if (different) display.lastSizeC = display.wrapper.clientHeight;
492
+ display.showingFrom = from; display.showingTo = to;
493
+ startWorker(cm, 100);
494
+
495
+ var prevBottom = display.lineDiv.offsetTop;
496
+ for (var node = display.lineDiv.firstChild, height; node; node = node.nextSibling) if (node.lineObj) {
497
+ if (ie_lt8) {
498
+ var bot = node.offsetTop + node.offsetHeight;
499
+ height = bot - prevBottom;
500
+ prevBottom = bot;
501
+ } else {
502
+ var box = node.getBoundingClientRect();
503
+ height = box.bottom - box.top;
504
  }
505
+ var diff = node.lineObj.height - height;
506
+ if (height < 2) height = textHeight(display);
507
+ if (diff > .001 || diff < -.001) {
508
+ updateLineHeight(node.lineObj, height);
509
+ var widgets = node.lineObj.widgets;
510
+ if (widgets) for (var i = 0; i < widgets.length; ++i)
511
+ widgets[i].height = widgets[i].node.offsetHeight;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
512
  }
513
+ }
514
+ display.viewOffset = heightAtLine(cm, getLine(doc, from));
515
+ // Position the mover div to align with the current virtual scroll position
516
+ display.mover.style.top = display.viewOffset + "px";
517
+
518
+ if (visibleLines(display, doc, viewPort).to >= to)
519
+ updateDisplayInner(cm, [], viewPort);
520
+ return true;
521
+ }
522
+
523
+ function computeIntact(intact, changes) {
524
+ for (var i = 0, l = changes.length || 0; i < l; ++i) {
525
+ var change = changes[i], intact2 = [], diff = change.diff || 0;
526
+ for (var j = 0, l2 = intact.length; j < l2; ++j) {
527
+ var range = intact[j];
528
+ if (change.to <= range.from && change.diff) {
529
+ intact2.push({from: range.from + diff, to: range.to + diff});
530
+ } else if (change.to <= range.from || change.from >= range.to) {
531
+ intact2.push(range);
532
+ } else {
533
+ if (change.from > range.from)
534
+ intact2.push({from: range.from, to: change.from});
535
+ if (change.to < range.to)
536
+ intact2.push({from: change.to + diff, to: range.to + diff});
537
  }
538
  }
539
+ intact = intact2;
540
+ }
541
+ return intact;
542
+ }
543
 
544
+ function getDimensions(cm) {
545
+ var d = cm.display, left = {}, width = {};
546
+ for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) {
547
+ left[cm.options.gutters[i]] = n.offsetLeft;
548
+ width[cm.options.gutters[i]] = n.offsetWidth;
549
+ }
550
+ return {fixedPos: compensateForHScroll(d),
551
+ gutterTotalWidth: d.gutters.offsetWidth,
552
+ gutterLeft: left,
553
+ gutterWidth: width,
554
+ wrapperWidth: d.wrapper.clientWidth};
555
+ }
556
+
557
+ function patchDisplay(cm, from, to, intact, updateNumbersFrom) {
558
+ var dims = getDimensions(cm);
559
+ var display = cm.display, lineNumbers = cm.options.lineNumbers;
560
+ if (!intact.length && (!webkit || !cm.display.currentWheelTarget))
561
+ removeChildren(display.lineDiv);
562
+ var container = display.lineDiv, cur = container.firstChild;
563
+
564
+ function rm(node) {
565
+ var next = node.nextSibling;
566
+ if (webkit && mac && cm.display.currentWheelTarget == node) {
567
+ node.style.display = "none";
568
+ node.lineObj = null;
569
+ } else {
570
+ node.parentNode.removeChild(node);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
571
  }
572
+ return next;
573
  }
574
+
575
+ var nextIntact = intact.shift(), lineNo = from;
576
+ cm.view.doc.iter(from, to, function(line) {
577
+ if (nextIntact && nextIntact.to == lineNo) nextIntact = intact.shift();
578
+ if (lineIsHidden(line)) {
579
+ if (line.height != 0) updateLineHeight(line, 0);
580
+ if (line.widgets && cur.previousSibling) for (var i = 0; i < line.widgets.length; ++i)
581
+ if (line.widgets[i].showIfHidden) {
582
+ var prev = cur.previousSibling;
583
+ if (prev.nodeType == "pre") {
584
+ var wrap = elt("div", null, null, "position: relative");
585
+ prev.parentNode.replaceChild(wrap, prev);
586
+ wrap.appendChild(prev);
587
+ prev = wrap;
588
+ }
589
+ prev.appendChild(buildLineWidget(line.widgets[i], prev, dims));
590
+ }
591
+ } else if (nextIntact && nextIntact.from <= lineNo && nextIntact.to > lineNo) {
592
+ // This line is intact. Skip to the actual node. Update its
593
+ // line number if needed.
594
+ while (cur.lineObj != line) cur = rm(cur);
595
+ if (lineNumbers && updateNumbersFrom <= lineNo && cur.lineNumber)
596
+ setTextContent(cur.lineNumber, lineNumberFor(cm.options, lineNo));
597
+ cur = cur.nextSibling;
598
+ } else {
599
+ // This line needs to be generated.
600
+ var lineNode = buildLineElement(cm, line, lineNo, dims);
601
+ container.insertBefore(lineNode, cur);
602
+ lineNode.lineObj = line;
603
  }
604
+ ++lineNo;
605
+ });
606
+ while (cur) cur = rm(cur);
607
+ }
608
+
609
+ function buildLineElement(cm, line, lineNo, dims) {
610
+ var lineElement = lineContent(cm, line);
611
+ var markers = line.gutterMarkers, display = cm.display;
612
+
613
+ if (!cm.options.lineNumbers && !markers && !line.bgClass && !line.wrapClass &&
614
+ (!line.widgets || !line.widgets.length)) return lineElement;
615
+
616
+ // Lines with gutter elements or a background class need
617
+ // to be wrapped again, and have the extra elements added
618
+ // to the wrapper div
619
+
620
+ var wrap = elt("div", null, line.wrapClass, "position: relative");
621
+ if (cm.options.lineNumbers || markers) {
622
+ var gutterWrap = wrap.appendChild(elt("div", null, null, "position: absolute; left: " +
623
+ (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px"));
624
+ if (cm.options.fixedGutter) wrap.alignable = [gutterWrap];
625
+ if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"]))
626
+ wrap.lineNumber = gutterWrap.appendChild(
627
+ elt("div", lineNumberFor(cm.options, lineNo),
628
+ "CodeMirror-linenumber CodeMirror-gutter-elt",
629
+ "left: " + dims.gutterLeft["CodeMirror-linenumbers"] + "px; width: "
630
+ + display.lineNumInnerWidth + "px"));
631
+ if (markers)
632
+ for (var k = 0; k < cm.options.gutters.length; ++k) {
633
+ var id = cm.options.gutters[k], found = markers.hasOwnProperty(id) && markers[id];
634
+ if (found)
635
+ gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt", "left: " +
636
+ dims.gutterLeft[id] + "px; width: " + dims.gutterWidth[id] + "px"));
637
+ }
638
  }
639
+ // Kludge to make sure the styled element lies behind the selection (by z-index)
640
+ if (line.bgClass)
641
+ wrap.appendChild(elt("div", "\u00a0", line.bgClass + " CodeMirror-linebackground"));
642
+ wrap.appendChild(lineElement);
643
+ if (line.widgets) for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
644
+ var widget = ws[i], node = buildLineWidget(widget, wrap, dims);
645
+ if (widget.above)
646
+ wrap.insertBefore(node, cm.options.lineNumbers && line.height != 0 ? gutterWrap : lineElement);
647
+ else
648
+ wrap.appendChild(node);
649
+ }
650
+ if (ie_lt8) wrap.style.zIndex = 2;
651
+ return wrap;
652
+ }
653
 
654
+ function buildLineWidget(widget, wrap, dims) {
655
+ var node = elt("div", [widget.node], "CodeMirror-linewidget");
656
+ node.widget = widget;
657
+ if (widget.noHScroll) {
658
+ (wrap.alignable || (wrap.alignable = [])).push(node);
659
+ var width = dims.wrapperWidth;
660
+ node.style.left = dims.fixedPos + "px";
661
+ if (!widget.coverGutter) {
662
+ width -= dims.gutterTotalWidth;
663
+ node.style.paddingLeft = dims.gutterTotalWidth + "px";
664
  }
665
+ node.style.width = width + "px";
666
+ }
667
+ if (widget.coverGutter) {
668
+ node.style.zIndex = 5;
669
+ node.style.position = "relative";
670
+ if (!widget.noHScroll) node.style.marginLeft = -dims.gutterTotalWidth + "px";
671
+ }
672
+ return node;
673
+ }
674
+
675
+ // SELECTION / CURSOR
676
+
677
+ function updateSelection(cm) {
678
+ var display = cm.display;
679
+ var collapsed = posEq(cm.view.sel.from, cm.view.sel.to);
680
+ if (collapsed || cm.options.showCursorWhenSelecting)
681
+ updateSelectionCursor(cm);
682
+ else
683
+ display.cursor.style.display = display.otherCursor.style.display = "none";
684
+ if (!collapsed)
685
+ updateSelectionRange(cm);
686
+ else
687
+ display.selectionDiv.style.display = "none";
688
+
689
+ // Move the hidden textarea near the cursor to prevent scrolling artifacts
690
+ var headPos = cursorCoords(cm, cm.view.sel.head, "div");
691
+ var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect();
692
+ display.inputDiv.style.top = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
693
+ headPos.top + lineOff.top - wrapOff.top)) + "px";
694
+ display.inputDiv.style.left = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
695
+ headPos.left + lineOff.left - wrapOff.left)) + "px";
696
+ }
697
+
698
+ // No selection, plain cursor
699
+ function updateSelectionCursor(cm) {
700
+ var display = cm.display, pos = cursorCoords(cm, cm.view.sel.head, "div");
701
+ display.cursor.style.left = pos.left + "px";
702
+ display.cursor.style.top = pos.top + "px";
703
+ display.cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px";
704
+ display.cursor.style.display = "";
705
+
706
+ if (pos.other) {
707
+ display.otherCursor.style.display = "";
708
+ display.otherCursor.style.left = pos.other.left + "px";
709
+ display.otherCursor.style.top = pos.other.top + "px";
710
+ display.otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px";
711
+ } else { display.otherCursor.style.display = "none"; }
712
+ }
713
+
714
+ // Highlight selection
715
+ function updateSelectionRange(cm) {
716
+ var display = cm.display, doc = cm.view.doc, sel = cm.view.sel;
717
+ var fragment = document.createDocumentFragment();
718
+ var clientWidth = display.lineSpace.offsetWidth, pl = paddingLeft(cm.display);
719
+
720
+ function add(left, top, width, bottom) {
721
+ if (top < 0) top = 0;
722
+ fragment.appendChild(elt("div", null, "CodeMirror-selected", "position: absolute; left: " + left +
723
+ "px; top: " + top + "px; width: " + (width == null ? clientWidth - left : width) +
724
+ "px; height: " + (bottom - top) + "px"));
725
+ }
726
+
727
+ function drawForLine(line, fromArg, toArg, retTop) {
728
+ var lineObj = getLine(doc, line);
729
+ var lineLen = lineObj.text.length, rVal = retTop ? Infinity : -Infinity;
730
+ function coords(ch) {
731
+ return charCoords(cm, {line: line, ch: ch}, "div", lineObj);
732
  }
733
+
734
+ iterateBidiSections(getOrder(lineObj), fromArg || 0, toArg == null ? lineLen : toArg, function(from, to, dir) {
735
+ var leftPos = coords(dir == "rtl" ? to - 1 : from);
736
+ var rightPos = coords(dir == "rtl" ? from : to - 1);
737
+ var left = leftPos.left, right = rightPos.right;
738
+ if (rightPos.top - leftPos.top > 3) { // Different lines, draw top part
739
+ add(left, leftPos.top, null, leftPos.bottom);
740
+ left = pl;
741
+ if (leftPos.bottom < rightPos.top) add(left, leftPos.bottom, null, rightPos.top);
742
+ }
743
+ if (toArg == null && to == lineLen) right = clientWidth;
744
+ if (fromArg == null && from == 0) left = pl;
745
+ rVal = retTop ? Math.min(rightPos.top, rVal) : Math.max(rightPos.bottom, rVal);
746
+ if (left < pl + 1) left = pl;
747
+ add(left, rightPos.top, right - left, rightPos.bottom);
748
+ });
749
+ return rVal;
750
  }
751
+
752
+ if (sel.from.line == sel.to.line) {
753
+ drawForLine(sel.from.line, sel.from.ch, sel.to.ch);
754
+ } else {
755
+ var fromObj = getLine(doc, sel.from.line);
756
+ var cur = fromObj, merged, path = [sel.from.line, sel.from.ch], singleLine;
757
+ while (merged = collapsedSpanAtEnd(cur)) {
758
+ var found = merged.find();
759
+ path.push(found.from.ch, found.to.line, found.to.ch);
760
+ if (found.to.line == sel.to.line) {
761
+ path.push(sel.to.ch);
762
+ singleLine = true;
763
+ break;
764
  }
765
+ cur = getLine(doc, found.to.line);
766
+ }
767
+
768
+ // This is a single, merged line
769
+ if (singleLine) {
770
+ for (var i = 0; i < path.length; i += 3)
771
+ drawForLine(path[i], path[i+1], path[i+2]);
 
 
 
 
 
 
 
772
  } else {
773
+ var middleTop, middleBot, toObj = getLine(doc, sel.to.line);
774
+ if (sel.from.ch)
775
+ // Draw the first line of selection.
776
+ middleTop = drawForLine(sel.from.line, sel.from.ch, null, false);
777
+ else
778
+ // Simply include it in the middle block.
779
+ middleTop = heightAtLine(cm, fromObj) - display.viewOffset;
780
+
781
+ if (!sel.to.ch)
782
+ middleBot = heightAtLine(cm, toObj) - display.viewOffset;
783
+ else
784
+ middleBot = drawForLine(sel.to.line, collapsedSpanAtStart(toObj) ? null : 0, sel.to.ch, true);
785
+
786
+ if (middleTop < middleBot) add(pl, middleTop, null, middleBot);
787
+ }
788
+ }
789
+
790
+ removeChildrenAndAdd(display.selectionDiv, fragment);
791
+ display.selectionDiv.style.display = "";
792
+ }
793
+
794
+ // Cursor-blinking
795
+ function restartBlink(cm) {
796
+ var display = cm.display;
797
+ clearInterval(display.blinker);
798
+ var on = true;
799
+ display.cursor.style.visibility = display.otherCursor.style.visibility = "";
800
+ display.blinker = setInterval(function() {
801
+ if (!display.cursor.offsetHeight) return;
802
+ display.cursor.style.visibility = display.otherCursor.style.visibility = (on = !on) ? "" : "hidden";
803
+ }, cm.options.cursorBlinkRate);
804
+ }
805
+
806
+ // HIGHLIGHT WORKER
807
+
808
+ function startWorker(cm, time) {
809
+ if (cm.view.mode.startState && cm.view.frontier < cm.display.showingTo)
810
+ cm.view.highlight.set(time, bind(highlightWorker, cm));
811
+ }
812
+
813
+ function highlightWorker(cm) {
814
+ var view = cm.view, doc = view.doc;
815
+ if (view.frontier >= cm.display.showingTo) return;
816
+ var end = +new Date + cm.options.workTime;
817
+ var state = copyState(view.mode, getStateBefore(cm, view.frontier));
818
+ var changed = [], prevChange;
819
+ doc.iter(view.frontier, Math.min(doc.size, cm.display.showingTo + 500), function(line) {
820
+ if (view.frontier >= cm.display.showingFrom) { // Visible
821
+ var oldStyles = line.styles;
822
+ line.styles = highlightLine(cm, line, state);
823
+ var ischange = !oldStyles || oldStyles.length != line.styles.length;
824
+ for (var i = 0; !ischange && i < oldStyles.length; ++i)
825
+ ischange = oldStyles[i] != line.styles[i];
826
+ if (ischange) {
827
+ if (prevChange && prevChange.end == view.frontier) prevChange.end++;
828
+ else changed.push(prevChange = {start: view.frontier, end: view.frontier + 1});
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
829
  }
830
+ line.stateAfter = copyState(view.mode, state);
 
 
 
 
831
  } else {
832
+ processLine(cm, line, state);
833
+ line.stateAfter = view.frontier % 5 == 0 ? copyState(view.mode, state) : null;
 
 
 
 
 
 
834
  }
835
+ ++view.frontier;
836
+ if (+new Date > end) {
837
+ startWorker(cm, cm.options.workDelay);
838
+ return true;
839
+ }
840
+ });
841
+ if (changed.length)
842
+ operation(cm, function() {
843
+ for (var i = 0; i < changed.length; ++i)
844
+ regChange(this, changed[i].start, changed[i].end);
845
+ })();
846
+ }
847
+
848
+ // Finds the line to start with when starting a parse. Tries to
849
+ // find a line with a stateAfter, so that it can start with a
850
+ // valid state. If that fails, it returns the line with the
851
+ // smallest indentation, which tends to need the least context to
852
+ // parse correctly.
853
+ function findStartLine(cm, n) {
854
+ var minindent, minline, doc = cm.view.doc;
855
+ for (var search = n, lim = n - 100; search > lim; --search) {
856
+ if (search == 0) return 0;
857
+ var line = getLine(doc, search-1);
858
+ if (line.stateAfter) return search;
859
+ var indented = countColumn(line.text, null, cm.options.tabSize);
860
+ if (minline == null || minindent > indented) {
861
+ minline = search - 1;
862
+ minindent = indented;
863
+ }
864
+ }
865
+ return minline;
866
+ }
867
+
868
+ function getStateBefore(cm, n) {
869
+ var view = cm.view;
870
+ if (!view.mode.startState) return true;
871
+ var pos = findStartLine(cm, n), state = pos && getLine(view.doc, pos-1).stateAfter;
872
+ if (!state) state = startState(view.mode);
873
+ else state = copyState(view.mode, state);
874
+ view.doc.iter(pos, n, function(line) {
875
+ processLine(cm, line, state);
876
+ var save = pos == n - 1 || pos % 5 == 0 || pos >= view.showingFrom && pos < view.showingTo;
877
+ line.stateAfter = save ? copyState(view.mode, state) : null;
878
+ ++pos;
879
+ });
880
+ return state;
881
+ }
882
+
883
+ // POSITION MEASUREMENT
884
+
885
+ function paddingTop(display) {return display.lineSpace.offsetTop;}
886
+ function paddingLeft(display) {
887
+ var e = removeChildrenAndAdd(display.measure, elt("pre")).appendChild(elt("span", "x"));
888
+ return e.offsetLeft;
889
+ }
890
+
891
+ function measureChar(cm, line, ch, data) {
892
+ var dir = -1;
893
+ data = data || measureLine(cm, line);
894
+
895
+ for (var pos = ch;; pos += dir) {
896
+ var r = data[pos];
897
+ if (r) break;
898
+ if (dir < 0 && pos == 0) dir = 1;
899
+ }
900
+ return {left: pos < ch ? r.right : r.left,
901
+ right: pos > ch ? r.left : r.right,
902
+ top: r.top, bottom: r.bottom};
903
+ }
904
+
905
+ function measureLine(cm, line) {
906
+ // First look in the cache
907
+ var display = cm.display, cache = cm.display.measureLineCache;
908
+ for (var i = 0; i < cache.length; ++i) {
909
+ var memo = cache[i];
910
+ if (memo.text == line.text && memo.markedSpans == line.markedSpans &&
911
+ display.scroller.clientWidth == memo.width)
912
+ return memo.measure;
913
+ }
914
+
915
+ var measure = measureLineInner(cm, line);
916
+ // Store result in the cache
917
+ var memo = {text: line.text, width: display.scroller.clientWidth,
918
+ markedSpans: line.markedSpans, measure: measure};
919
+ if (cache.length == 16) cache[++display.measureLineCachePos % 16] = memo;
920
+ else cache.push(memo);
921
+ return measure;
922
+ }
923
+
924
+ function measureLineInner(cm, line) {
925
+ var display = cm.display, measure = emptyArray(line.text.length);
926
+ var pre = lineContent(cm, line, measure);
927
+
928
+ // IE does not cache element positions of inline elements between
929
+ // calls to getBoundingClientRect. This makes the loop below,
930
+ // which gathers the positions of all the characters on the line,
931
+ // do an amount of layout work quadratic to the number of
932
+ // characters. When line wrapping is off, we try to improve things
933
+ // by first subdividing the line into a bunch of inline blocks, so
934
+ // that IE can reuse most of the layout information from caches
935
+ // for those blocks. This does interfere with line wrapping, so it
936
+ // doesn't work when wrapping is on, but in that case the
937
+ // situation is slightly better, since IE does cache line-wrapping
938
+ // information and only recomputes per-line.
939
+ if (ie && !ie_lt8 && !cm.options.lineWrapping && pre.childNodes.length > 100) {
940
+ var fragment = document.createDocumentFragment();
941
+ var chunk = 10, n = pre.childNodes.length;
942
+ for (var i = 0, chunks = Math.ceil(n / chunk); i < chunks; ++i) {
943
+ var wrap = elt("div", null, null, "display: inline-block");
944
+ for (var j = 0; j < chunk && n; ++j) {
945
+ wrap.appendChild(pre.firstChild);
946
+ --n;
947
  }
948
+ fragment.appendChild(wrap);
949
  }
950
+ pre.appendChild(fragment);
951
+ }
952
 
953
+ removeChildrenAndAdd(display.measure, pre);
954
+
955
+ var outer = display.lineDiv.getBoundingClientRect();
956
+ var vranges = [], data = emptyArray(line.text.length), maxBot = pre.offsetHeight;
957
+ for (var i = 0, cur; i < measure.length; ++i) if (cur = measure[i]) {
958
+ var size = cur.getBoundingClientRect();
959
+ var top = Math.max(0, size.top - outer.top), bot = Math.min(size.bottom - outer.top, maxBot);
960
+ for (var j = 0; j < vranges.length; j += 2) {
961
+ var rtop = vranges[j], rbot = vranges[j+1];
962
+ if (rtop > bot || rbot < top) continue;
963
+ if (rtop <= top && rbot >= bot ||
964
+ top <= rtop && bot >= rbot ||
965
+ Math.min(bot, rbot) - Math.max(top, rtop) >= (bot - top) >> 1) {
966
+ vranges[j] = Math.min(top, rtop);
967
+ vranges[j+1] = Math.max(bot, rbot);
968
+ break;
969
+ }
970
+ }
971
+ if (j == vranges.length) vranges.push(top, bot);
972
+ data[i] = {left: size.left - outer.left, right: size.right - outer.left, top: j};
973
+ }
974
+ for (var i = 0, cur; i < data.length; ++i) if (cur = data[i]) {
975
+ var vr = cur.top;
976
+ cur.top = vranges[vr]; cur.bottom = vranges[vr+1];
977
+ }
978
+ return data;
979
+ }
980
 
981
+ function clearCaches(cm) {
982
+ cm.display.measureLineCache.length = cm.display.measureLineCachePos = 0;
983
+ cm.display.cachedCharWidth = cm.display.cachedTextHeight = null;
984
+ cm.view.maxLineChanged = true;
985
+ }
986
 
987
+ // Context is one of "line", "div" (display.lineDiv), "local"/null (editor), or "page"
988
+ function intoCoordSystem(cm, lineObj, rect, context) {
989
+ if (lineObj.widgets) for (var i = 0; i < lineObj.widgets.length; ++i) if (lineObj.widgets[i].above) {
990
+ var size = widgetHeight(lineObj.widgets[i]);
991
+ rect.top += size; rect.bottom += size;
992
  }
993
+ if (context == "line") return rect;
994
+ if (!context) context = "local";
995
+ var yOff = heightAtLine(cm, lineObj);
996
+ if (context != "local") yOff -= cm.display.viewOffset;
997
+ if (context == "page") {
998
+ var lOff = cm.display.lineSpace.getBoundingClientRect();
999
+ yOff += lOff.top + (window.pageYOffset || (document.documentElement || document.body).scrollTop);
1000
+ var xOff = lOff.left + (window.pageXOffset || (document.documentElement || document.body).scrollLeft);
1001
+ rect.left += xOff; rect.right += xOff;
1002
+ }
1003
+ rect.top += yOff; rect.bottom += yOff;
1004
+ return rect;
1005
+ }
1006
 
1007
+ function charCoords(cm, pos, context, lineObj) {
1008
+ if (!lineObj) lineObj = getLine(cm.view.doc, pos.line);
1009
+ return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch), context);
1010
+ }
1011
+
1012
+ function cursorCoords(cm, pos, context, lineObj, measurement) {
1013
+ lineObj = lineObj || getLine(cm.view.doc, pos.line);
1014
+ if (!measurement) measurement = measureLine(cm, lineObj);
1015
+ function get(ch, right) {
1016
+ var m = measureChar(cm, lineObj, ch, measurement);
1017
+ if (right) m.left = m.right; else m.right = m.left;
1018
+ return intoCoordSystem(cm, lineObj, m, context);
1019
+ }
1020
+ var order = getOrder(lineObj), ch = pos.ch;
1021
+ if (!order) return get(ch);
1022
+ var main, other, linedir = order[0].level;
1023
+ for (var i = 0; i < order.length; ++i) {
1024
+ var part = order[i], rtl = part.level % 2, nb, here;
1025
+ if (part.from < ch && part.to > ch) return get(ch, rtl);
1026
+ var left = rtl ? part.to : part.from, right = rtl ? part.from : part.to;
1027
+ if (left == ch) {
1028
+ // Opera and IE return bogus offsets and widths for edges
1029
+ // where the direction flips, but only for the side with the
1030
+ // lower level. So we try to use the side with the higher
1031
+ // level.
1032
+ if (i && part.level < (nb = order[i-1]).level) here = get(nb.level % 2 ? nb.from : nb.to - 1, true);
1033
+ else here = get(rtl && part.from != part.to ? ch - 1 : ch);
1034
+ if (rtl == linedir) main = here; else other = here;
1035
+ } else if (right == ch) {
1036
+ var nb = i < order.length - 1 && order[i+1];
1037
+ if (!rtl && nb && nb.from == nb.to) continue;
1038
+ if (nb && part.level < nb.level) here = get(nb.level % 2 ? nb.to - 1 : nb.from);
1039
+ else here = get(rtl ? ch : ch - 1, true);
1040
+ if (rtl == linedir) main = here; else other = here;
1041
  }
 
 
 
 
 
 
1042
  }
1043
+ if (linedir && !ch) other = get(order[0].to - 1);
1044
+ if (!main) return other;
1045
+ if (other) main.other = other;
1046
+ return main;
1047
+ }
1048
+
1049
+ // Coords must be lineSpace-local
1050
+ function coordsChar(cm, x, y) {
1051
+ var doc = cm.view.doc;
1052
+ y += cm.display.viewOffset;
1053
+ if (y < 0) return {line: 0, ch: 0, outside: true};
1054
+ var lineNo = lineAtHeight(doc, y);
1055
+ if (lineNo >= doc.size) return {line: doc.size - 1, ch: getLine(doc, doc.size - 1).text.length};
1056
+ if (x < 0) x = 0;
1057
+
1058
+ for (;;) {
1059
+ var lineObj = getLine(doc, lineNo);
1060
+ var found = coordsCharInner(cm, lineObj, lineNo, x, y);
1061
+ var merged = collapsedSpanAtEnd(lineObj);
1062
+ var mergedPos = merged && merged.find();
1063
+ if (merged && found.ch >= mergedPos.from.ch)
1064
+ lineNo = mergedPos.to.line;
1065
+ else
1066
+ return found;
1067
  }
1068
+ }
1069
+
1070
+ function coordsCharInner(cm, lineObj, lineNo, x, y) {
1071
+ var innerOff = y - heightAtLine(cm, lineObj);
1072
+ var wrongLine = false, cWidth = cm.display.wrapper.clientWidth;
1073
+ var measurement = measureLine(cm, lineObj);
1074
+
1075
+ function getX(ch) {
1076
+ var sp = cursorCoords(cm, {line: lineNo, ch: ch}, "line",
1077
+ lineObj, measurement);
1078
+ wrongLine = true;
1079
+ if (innerOff > sp.bottom) return Math.max(0, sp.left - cWidth);
1080
+ else if (innerOff < sp.top) return sp.left + cWidth;
1081
+ else wrongLine = false;
1082
+ return sp.left;
1083
  }
1084
 
1085
+ var bidi = getOrder(lineObj), dist = lineObj.text.length;
1086
+ var from = lineLeft(lineObj), to = lineRight(lineObj);
1087
+ var fromX = paddingLeft(cm.display), toX = getX(to);
1088
+
1089
+ if (x > toX) return {line: lineNo, ch: to, outside: wrongLine};
1090
+ // Do a binary search between these bounds.
1091
+ for (;;) {
1092
+ if (bidi ? to == from || to == moveVisually(lineObj, from, 1) : to - from <= 1) {
1093
+ var after = x - fromX < toX - x, ch = after ? from : to;
1094
+ while (isExtendingChar.test(lineObj.text.charAt(ch))) ++ch;
1095
+ return {line: lineNo, ch: ch, after: after, outside: wrongLine};
1096
+ }
1097
+ var step = Math.ceil(dist / 2), middle = from + step;
1098
+ if (bidi) {
1099
+ middle = from;
1100
+ for (var i = 0; i < step; ++i) middle = moveVisually(lineObj, middle, 1);
1101
+ }
1102
+ var middleX = getX(middle);
1103
+ if (middleX > x) {to = middle; toX = middleX; if (wrongLine) toX += 1000; dist -= step;}
1104
+ else {from = middle; fromX = middleX; dist = step;}
1105
  }
1106
+ }
1107
+
1108
+ var measureText;
1109
+ function textHeight(display) {
1110
+ if (display.cachedTextHeight != null) return display.cachedTextHeight;
1111
+ if (measureText == null) {
1112
+ measureText = elt("pre");
1113
+ // Measure a bunch of lines, for browsers that compute
1114
+ // fractional heights.
1115
+ for (var i = 0; i < 49; ++i) {
1116
+ measureText.appendChild(document.createTextNode("x"));
1117
+ measureText.appendChild(elt("br"));
1118
+ }
1119
+ measureText.appendChild(document.createTextNode("x"));
1120
  }
1121
+ removeChildrenAndAdd(display.measure, measureText);
1122
+ var height = measureText.offsetHeight / 50;
1123
+ if (height > 3) display.cachedTextHeight = height;
1124
+ removeChildren(display.measure);
1125
+ return height || 1;
1126
+ }
1127
 
1128
+ function charWidth(display) {
1129
+ if (display.cachedCharWidth != null) return display.cachedCharWidth;
1130
+ var anchor = elt("span", "x");
1131
+ var pre = elt("pre", [anchor]);
1132
+ removeChildrenAndAdd(display.measure, pre);
1133
+ var width = anchor.offsetWidth;
1134
+ if (width > 2) display.cachedCharWidth = width;
1135
+ return width || 10;
1136
+ }
1137
+
1138
+ // OPERATIONS
1139
+
1140
+ // Operations are used to wrap changes in such a way that each
1141
+ // change won't have to update the cursor and display (which would
1142
+ // be awkward, slow, and error-prone), but instead updates are
1143
+ // batched and then all combined and executed at once.
1144
+
1145
+ function startOperation(cm) {
1146
+ if (cm.curOp) ++cm.curOp.depth;
1147
+ else cm.curOp = {
1148
+ // Nested operations delay update until the outermost one
1149
+ // finishes.
1150
+ depth: 1,
1151
+ // An array of ranges of lines that have to be updated. See
1152
+ // updateDisplay.
1153
+ changes: [],
1154
+ delayedCallbacks: [],
1155
+ updateInput: null,
1156
+ userSelChange: null,
1157
+ textChanged: null,
1158
+ selectionChanged: false,
1159
+ updateMaxLine: false,
1160
+ id: ++cm.nextOpId
1161
+ };
1162
+ }
1163
+
1164
+ function endOperation(cm) {
1165
+ var op = cm.curOp;
1166
+ if (--op.depth) return;
1167
+ cm.curOp = null;
1168
+ var view = cm.view, display = cm.display;
1169
+ if (op.updateMaxLine) computeMaxLength(view);
1170
+ if (view.maxLineChanged && !cm.options.lineWrapping) {
1171
+ var width = measureChar(cm, view.maxLine, view.maxLine.text.length).right;
1172
+ display.sizer.style.minWidth = (width + 3 + scrollerCutOff) + "px";
1173
+ view.maxLineChanged = false;
1174
+ var maxScrollLeft = Math.max(0, display.sizer.offsetLeft + display.sizer.offsetWidth - display.scroller.clientWidth);
1175
+ if (maxScrollLeft < view.scrollLeft)
1176
+ setScrollLeft(cm, Math.min(display.scroller.scrollLeft, maxScrollLeft), true);
1177
  }
1178
+ var newScrollPos, updated;
1179
+ if (op.selectionChanged) {
1180
+ var coords = cursorCoords(cm, view.sel.head);
1181
+ newScrollPos = calculateScrollPos(cm, coords.left, coords.top, coords.left, coords.bottom);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1182
  }
1183
+ if (op.changes.length || newScrollPos && newScrollPos.scrollTop != null)
1184
+ updated = updateDisplay(cm, op.changes, newScrollPos && newScrollPos.scrollTop);
1185
+ if (!updated && op.selectionChanged) updateSelection(cm);
1186
+ if (newScrollPos) scrollCursorIntoView(cm);
1187
+ if (op.selectionChanged) restartBlink(cm);
1188
+
1189
+ if (view.focused && op.updateInput)
1190
+ resetInput(cm, op.userSelChange);
1191
+
1192
+ if (op.textChanged)
1193
+ signal(cm, "change", cm, op.textChanged);
1194
+ if (op.selectionChanged) signal(cm, "cursorActivity", cm);
1195
+ for (var i = 0; i < op.delayedCallbacks.length; ++i) op.delayedCallbacks[i](cm);
1196
+ }
1197
+
1198
+ // Wraps a function in an operation. Returns the wrapped function.
1199
+ function operation(cm1, f) {
1200
+ return function() {
1201
+ var cm = cm1 || this;
1202
+ startOperation(cm);
1203
+ try {var result = f.apply(cm, arguments);}
1204
+ finally {endOperation(cm);}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1205
  return result;
1206
+ };
1207
+ }
1208
+
1209
+ function regChange(cm, from, to, lendiff) {
1210
+ cm.curOp.changes.push({from: from, to: to, diff: lendiff});
1211
+ }
1212
+
1213
+ // INPUT HANDLING
1214
+
1215
+ function slowPoll(cm) {
1216
+ if (cm.view.pollingFast) return;
1217
+ cm.display.poll.set(cm.options.pollInterval, function() {
1218
+ readInput(cm);
1219
+ if (cm.view.focused) slowPoll(cm);
1220
+ });
1221
+ }
1222
+
1223
+ function fastPoll(cm) {
1224
+ var missed = false;
1225
+ cm.display.pollingFast = true;
1226
+ function p() {
1227
+ var changed = readInput(cm);
1228
+ if (!changed && !missed) {missed = true; cm.display.poll.set(60, p);}
1229
+ else {cm.display.pollingFast = false; slowPoll(cm);}
1230
  }
1231
+ cm.display.poll.set(20, p);
1232
+ }
1233
+
1234
+ // prevInput is a hack to work with IME. If we reset the textarea
1235
+ // on every change, that breaks IME. So we look for changes
1236
+ // compared to the previous content instead. (Modern browsers have
1237
+ // events that indicate IME taking place, but these are not widely
1238
+ // supported or compatible enough yet to rely on.)
1239
+ function readInput(cm) {
1240
+ var input = cm.display.input, prevInput = cm.display.prevInput, view = cm.view, sel = view.sel;
1241
+ if (!view.focused || hasSelection(input) || isReadOnly(cm)) return false;
1242
+ var text = input.value;
1243
+ if (text == prevInput && posEq(sel.from, sel.to)) return false;
1244
+ startOperation(cm);
1245
+ view.sel.shift = false;
1246
+ var same = 0, l = Math.min(prevInput.length, text.length);
1247
+ while (same < l && prevInput[same] == text[same]) ++same;
1248
+ var from = sel.from, to = sel.to;
1249
+ if (same < prevInput.length)
1250
+ from = {line: from.line, ch: from.ch - (prevInput.length - same)};
1251
+ else if (view.overwrite && posEq(from, to) && !cm.display.pasteIncoming)
1252
+ to = {line: to.line, ch: Math.min(getLine(cm.view.doc, to.line).text.length, to.ch + (text.length - same))};
1253
+ var updateInput = cm.curOp.updateInput;
1254
+ updateDoc(cm, from, to, splitLines(text.slice(same)), "end",
1255
+ cm.display.pasteIncoming ? "paste" : "input", {from: from, to: to});
1256
+ cm.curOp.updateInput = updateInput;
1257
+ if (text.length > 1000) input.value = cm.display.prevInput = "";
1258
+ else cm.display.prevInput = text;
1259
+ endOperation(cm);
1260
+ cm.display.pasteIncoming = false;
1261
+ return true;
1262
+ }
1263
 
1264
+ function resetInput(cm, user) {
1265
+ var view = cm.view, minimal, selected;
1266
+ if (!posEq(view.sel.from, view.sel.to)) {
1267
+ cm.display.prevInput = "";
1268
+ minimal = hasCopyEvent &&
1269
+ (view.sel.to.line - view.sel.from.line > 100 || (selected = cm.getSelection()).length > 1000);
1270
+ if (minimal) cm.display.input.value = "-";
1271
+ else cm.display.input.value = selected || cm.getSelection();
1272
+ if (view.focused) selectInput(cm.display.input);
1273
+ } else if (user) cm.display.prevInput = cm.display.input.value = "";
1274
+ cm.display.inaccurateSelection = minimal;
1275
+ }
1276
+
1277
+ function focusInput(cm) {
1278
+ if (cm.options.readOnly != "nocursor" && (ie || document.activeElement != cm.display.input))
1279
+ cm.display.input.focus();
1280
+ }
1281
+
1282
+ function isReadOnly(cm) {
1283
+ return cm.options.readOnly || cm.view.cantEdit;
1284
+ }
1285
+
1286
+ // EVENT HANDLERS
1287
+
1288
+ function registerEventHandlers(cm) {
1289
+ var d = cm.display;
1290
+ on(d.scroller, "mousedown", operation(cm, onMouseDown));
1291
+ on(d.scroller, "dblclick", operation(cm, e_preventDefault));
1292
+ on(d.lineSpace, "selectstart", function(e) {
1293
+ if (!eventInWidget(d, e)) e_preventDefault(e);
1294
+ });
1295
+ // Gecko browsers fire contextmenu *after* opening the menu, at
1296
+ // which point we can't mess with it anymore. Context menu is
1297
+ // handled in onMouseDown for Gecko.
1298
+ if (!gecko) on(d.scroller, "contextmenu", function(e) {onContextMenu(cm, e);});
1299
+
1300
+ on(d.scroller, "scroll", function() {
1301
+ setScrollTop(cm, d.scroller.scrollTop);
1302
+ setScrollLeft(cm, d.scroller.scrollLeft, true);
1303
+ signal(cm, "scroll", cm);
1304
+ });
1305
+ on(d.scrollbarV, "scroll", function() {
1306
+ setScrollTop(cm, d.scrollbarV.scrollTop);
1307
+ });
1308
+ on(d.scrollbarH, "scroll", function() {
1309
+ setScrollLeft(cm, d.scrollbarH.scrollLeft);
1310
+ });
1311
+
1312
+ on(d.scroller, "mousewheel", function(e){onScrollWheel(cm, e);});
1313
+ on(d.scroller, "DOMMouseScroll", function(e){onScrollWheel(cm, e);});
1314
+
1315
+ function reFocus() { if (cm.view.focused) setTimeout(bind(focusInput, cm), 0); }
1316
+ on(d.scrollbarH, "mousedown", reFocus);
1317
+ on(d.scrollbarV, "mousedown", reFocus);
1318
+ // Prevent wrapper from ever scrolling
1319
+ on(d.wrapper, "scroll", function() { d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; });
1320
+
1321
+ if (!window.registered) window.registered = 0;
1322
+ ++window.registered;
1323
+ function onResize() {
1324
+ // Might be a text scaling operation, clear size caches.
1325
+ d.cachedCharWidth = d.cachedTextHeight = null;
1326
+ clearCaches(cm);
1327
+ updateDisplay(cm, true);
1328
  }
1329
+ on(window, "resize", onResize);
1330
+ // Above handler holds on to the editor and its data structures.
1331
+ // Here we poll to unregister it when the editor is no longer in
1332
+ // the document, so that it can be garbage-collected.
1333
+ setTimeout(function unregister() {
1334
+ for (var p = d.wrapper.parentNode; p && p != document.body; p = p.parentNode) {}
1335
+ if (p) setTimeout(unregister, 5000);
1336
+ else {--window.registered; off(window, "resize", onResize);}
1337
+ }, 5000);
1338
+
1339
+ on(d.input, "keyup", operation(cm, function(e) {
1340
+ if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
1341
+ if (e_prop(e, "keyCode") == 16) cm.view.sel.shift = false;
1342
+ }));
1343
+ on(d.input, "input", bind(fastPoll, cm));
1344
+ on(d.input, "keydown", operation(cm, onKeyDown));
1345
+ on(d.input, "keypress", operation(cm, onKeyPress));
1346
+ on(d.input, "focus", bind(onFocus, cm));
1347
+ on(d.input, "blur", bind(onBlur, cm));
1348
+
1349
+ function drag_(e) {
1350
+ if (cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e))) return;
1351
+ e_stop(e);
1352
+ }
1353
+ if (cm.options.dragDrop) {
1354
+ on(d.scroller, "dragstart", function(e){onDragStart(cm, e);});
1355
+ on(d.scroller, "dragenter", drag_);
1356
+ on(d.scroller, "dragover", drag_);
1357
+ on(d.scroller, "drop", operation(cm, onDrop));
1358
+ }
1359
+ on(d.scroller, "paste", function(e){
1360
+ if (eventInWidget(d, e)) return;
1361
+ focusInput(cm);
1362
+ fastPoll(cm);
1363
+ });
1364
+ on(d.input, "paste", function() {
1365
+ d.pasteIncoming = true;
1366
+ fastPoll(cm);
1367
+ });
1368
+
1369
+ function prepareCopy() {
1370
+ if (d.inaccurateSelection) {
1371
+ d.prevInput = "";
1372
+ d.inaccurateSelection = false;
1373
+ d.input.value = cm.getSelection();
1374
+ selectInput(d.input);
1375
  }
1376
+ }
1377
+ on(d.input, "cut", prepareCopy);
1378
+ on(d.input, "copy", prepareCopy);
1379
+
1380
+ // Needed to handle Tab key in KHTML
1381
+ if (khtml) on(d.sizer, "mouseup", function() {
1382
+ if (document.activeElement == d.input) d.input.blur();
1383
+ focusInput(cm);
1384
+ });
1385
+ }
1386
+
1387
+ function eventInWidget(display, e) {
1388
+ for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {
1389
+ if (!n) return true;
1390
+ if (/\bCodeMirror-(?:line)?widget\b/.test(n.className) ||
1391
+ n.parentNode == display.sizer && n != display.mover) return true;
1392
+ }
1393
+ }
1394
+
1395
+ function posFromMouse(cm, e, liberal) {
1396
+ var display = cm.display;
1397
+ if (!liberal) {
1398
+ var target = e_target(e);
1399
+ if (target == display.scrollbarH || target == display.scrollbarH.firstChild ||
1400
+ target == display.scrollbarV || target == display.scrollbarV.firstChild ||
1401
+ target == display.scrollbarFiller) return null;
1402
+ }
1403
+ var x, y, space = display.lineSpace.getBoundingClientRect();
1404
+ // Fails unpredictably on IE[67] when mouse is dragged around quickly.
1405
+ try { x = e.clientX; y = e.clientY; } catch (e) { return null; }
1406
+ return coordsChar(cm, x - space.left, y - space.top);
1407
+ }
1408
+
1409
+ var lastClick, lastDoubleClick;
1410
+ function onMouseDown(e) {
1411
+ var cm = this, display = cm.display, view = cm.view, sel = view.sel, doc = view.doc;
1412
+ sel.shift = e_prop(e, "shiftKey");
1413
+
1414
+ if (eventInWidget(display, e)) {
1415
+ if (!webkit) {
1416
+ display.scroller.draggable = false;
1417
+ setTimeout(function(){display.scroller.draggable = true;}, 100);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1418
  }
1419
+ return;
1420
+ }
1421
+ if (clickInGutter(cm, e)) return;
1422
+ var start = posFromMouse(cm, e);
1423
+
1424
+ switch (e_button(e)) {
1425
+ case 3:
1426
+ if (gecko) onContextMenu.call(cm, cm, e);
1427
+ return;
1428
+ case 2:
1429
+ if (start) extendSelection(cm, start);
1430
+ setTimeout(bind(focusInput, cm), 20);
1431
+ e_preventDefault(e);
1432
+ return;
1433
+ }
1434
+ // For button 1, if it was clicked inside the editor
1435
+ // (posFromMouse returning non-null), we have to adjust the
1436
+ // selection.
1437
+ if (!start) {if (e_target(e) == display.scroller) e_preventDefault(e); return;}
1438
 
1439
+ if (!view.focused) onFocus(cm);
1440
+
1441
+ var now = +new Date, type = "single";
1442
+ if (lastDoubleClick && lastDoubleClick.time > now - 400 && posEq(lastDoubleClick.pos, start)) {
1443
+ type = "triple";
1444
+ e_preventDefault(e);
1445
+ setTimeout(bind(focusInput, cm), 20);
1446
+ selectLine(cm, start.line);
1447
+ } else if (lastClick && lastClick.time > now - 400 && posEq(lastClick.pos, start)) {
1448
+ type = "double";
1449
+ lastDoubleClick = {time: now, pos: start};
1450
+ e_preventDefault(e);
1451
+ var word = findWordAt(getLine(doc, start.line).text, start);
1452
+ extendSelection(cm, word.from, word.to);
1453
+ } else { lastClick = {time: now, pos: start}; }
1454
+
1455
+ var last = start;
1456
+ if (cm.options.dragDrop && dragAndDrop && !isReadOnly(cm) && !posEq(sel.from, sel.to) &&
1457
+ !posLess(start, sel.from) && !posLess(sel.to, start) && type == "single") {
1458
+ var dragEnd = operation(cm, function(e2) {
1459
+ if (webkit) display.scroller.draggable = false;
1460
+ view.draggingText = false;
1461
+ off(document, "mouseup", dragEnd);
1462
+ off(display.scroller, "drop", dragEnd);
1463
+ if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) {
1464
+ e_preventDefault(e2);
1465
+ extendSelection(cm, start);
1466
+ focusInput(cm);
1467
  }
1468
+ });
1469
+ // Let the drag handler handle this.
1470
+ if (webkit) display.scroller.draggable = true;
1471
+ view.draggingText = dragEnd;
1472
+ // IE's approach to draggable
1473
+ if (display.scroller.dragDrop) display.scroller.dragDrop();
1474
+ on(document, "mouseup", dragEnd);
1475
+ on(display.scroller, "drop", dragEnd);
1476
+ return;
1477
+ }
1478
+ e_preventDefault(e);
1479
+ if (type == "single") extendSelection(cm, clipPos(doc, start));
1480
+
1481
+ var startstart = sel.from, startend = sel.to;
1482
+
1483
+ function doSelect(cur) {
1484
+ if (type == "single") {
1485
+ extendSelection(cm, clipPos(doc, start), cur);
1486
+ return;
1487
  }
1488
 
1489
+ startstart = clipPos(doc, startstart);
1490
+ startend = clipPos(doc, startend);
1491
+ if (type == "double") {
1492
+ var word = findWordAt(getLine(doc, cur.line).text, cur);
1493
+ if (posLess(cur, startstart)) extendSelection(cm, word.from, startend);
1494
+ else extendSelection(cm, startstart, word.to);
1495
+ } else if (type == "triple") {
1496
+ if (posLess(cur, startstart)) extendSelection(cm, startend, clipPos(doc, {line: cur.line, ch: 0}));
1497
+ else extendSelection(cm, startstart, clipPos(doc, {line: cur.line + 1, ch: 0}));
1498
  }
 
 
 
1499
  }
1500
 
1501
+ var editorSize = display.wrapper.getBoundingClientRect();
1502
+ // Used to ensure timeout re-tries don't fire when another extend
1503
+ // happened in the meantime (clearTimeout isn't reliable -- at
1504
+ // least on Chrome, the timeouts still happen even when cleared,
1505
+ // if the clear happens after their scheduled firing time).
1506
+ var counter = 0;
1507
+
1508
+ function extend(e) {
1509
+ var curCount = ++counter;
1510
+ var cur = posFromMouse(cm, e, true);
1511
+ if (!cur) return;
1512
+ if (!posEq(cur, last)) {
1513
+ if (!view.focused) onFocus(cm);
1514
+ last = cur;
1515
+ doSelect(cur);
1516
+ var visible = visibleLines(display, doc);
1517
+ if (cur.line >= visible.to || cur.line < visible.from)
1518
+ setTimeout(operation(cm, function(){if (counter == curCount) extend(e);}), 150);
1519
+ } else {
1520
+ var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0;
1521
+ if (outside) setTimeout(operation(cm, function() {
1522
+ if (counter != curCount) return;
1523
+ display.scroller.scrollTop += outside;
1524
+ extend(e);
1525
+ }), 50);
1526
+ }
1527
+ }
1528
+
1529
+ function done(e) {
1530
+ counter = Infinity;
1531
+ var cur = posFromMouse(cm, e);
1532
+ if (cur) doSelect(cur);
1533
+ e_preventDefault(e);
1534
+ focusInput(cm);
1535
+ off(document, "mousemove", move);
1536
+ off(document, "mouseup", up);
1537
+ }
1538
+
1539
+ var move = operation(cm, function(e) {
1540
+ if (!ie && !e_button(e)) done(e);
1541
+ else extend(e);
1542
+ });
1543
+ var up = operation(cm, done);
1544
+ on(document, "mousemove", move);
1545
+ on(document, "mouseup", up);
1546
+ }
1547
+
1548
+ function onDrop(e) {
1549
+ var cm = this;
1550
+ if (eventInWidget(cm.display, e) || (cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e))))
1551
+ return;
1552
+ e_preventDefault(e);
1553
+ var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files;
1554
+ if (!pos || isReadOnly(cm)) return;
1555
+ if (files && files.length && window.FileReader && window.File) {
1556
+ var n = files.length, text = Array(n), read = 0;
1557
+ var loadFile = function(file, i) {
1558
+ var reader = new FileReader;
1559
+ reader.onload = function() {
1560
+ text[i] = reader.result;
1561
+ if (++read == n) {
1562
+ pos = clipPos(cm.view.doc, pos);
1563
+ operation(cm, function() {
1564
+ var end = replaceRange(cm, text.join(""), pos, pos, "paste");
1565
+ setSelection(cm, pos, end);
1566
+ })();
1567
  }
1568
+ };
1569
+ reader.readAsText(file);
1570
+ };
1571
+ for (var i = 0; i < n; ++i) loadFile(files[i], i);
1572
+ } else {
1573
+ // Don't do a replace if the drop happened inside of the selected text.
1574
+ if (cm.view.draggingText && !(posLess(pos, cm.view.sel.from) || posLess(cm.view.sel.to, pos))) {
1575
+ cm.view.draggingText(e);
1576
+ // Ensure the editor is re-focused
1577
+ setTimeout(bind(focusInput, cm), 20);
1578
+ return;
1579
+ }
1580
+ try {
1581
+ var text = e.dataTransfer.getData("Text");
1582
+ if (text) {
1583
+ var curFrom = cm.view.sel.from, curTo = cm.view.sel.to;
1584
+ setSelection(cm, pos, pos);
1585
+ if (cm.view.draggingText) replaceRange(cm, "", curFrom, curTo, "paste");
1586
+ cm.replaceSelection(text, null, "paste");
1587
+ focusInput(cm);
1588
+ onFocus(cm);
1589
  }
 
1590
  }
1591
+ catch(e){}
1592
  }
1593
+ }
1594
 
1595
+ function clickInGutter(cm, e) {
1596
+ var display = cm.display;
1597
+ try { var mX = e.clientX, mY = e.clientY; }
1598
+ catch(e) { return false; }
1599
+
1600
+ if (mX >= Math.floor(display.gutters.getBoundingClientRect().right)) return false;
1601
+ e_preventDefault(e);
1602
+ if (!hasHandler(cm, "gutterClick")) return true;
1603
+
1604
+ var lineBox = display.lineDiv.getBoundingClientRect();
1605
+ if (mY > lineBox.bottom) return true;
1606
+ mY -= lineBox.top - display.viewOffset;
1607
+
1608
+ for (var i = 0; i < cm.options.gutters.length; ++i) {
1609
+ var g = display.gutters.childNodes[i];
1610
+ if (g && g.getBoundingClientRect().right >= mX) {
1611
+ var line = lineAtHeight(cm.view.doc, mY);
1612
+ var gutter = cm.options.gutters[i];
1613
+ signalLater(cm, cm, "gutterClick", cm, line, gutter, e);
1614
+ break;
1615
+ }
1616
+ }
1617
+ return true;
1618
+ }
1619
+
1620
+ function onDragStart(cm, e) {
1621
+ if (eventInWidget(cm.display, e)) return;
1622
+
1623
+ var txt = cm.getSelection();
1624
+ e.dataTransfer.setData("Text", txt);
1625
+
1626
+ // Use dummy image instead of default browsers image.
1627
+ // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
1628
+ if (e.dataTransfer.setDragImage && !safari) {
1629
+ var img = elt("img", null, null, "position: fixed; left: 0; top: 0;");
1630
+ if (opera) {
1631
+ img.width = img.height = 1;
1632
+ cm.display.wrapper.appendChild(img);
1633
+ // Force a relayout, or Opera won't use our image for some obscure reason
1634
+ img._top = img.offsetTop;
1635
+ }
1636
+ e.dataTransfer.setDragImage(img, 0, 0);
1637
+ if (opera) img.parentNode.removeChild(img);
1638
+ }
1639
+ }
1640
+
1641
+ function setScrollTop(cm, val) {
1642
+ if (Math.abs(cm.view.scrollTop - val) < 2) return;
1643
+ cm.view.scrollTop = val;
1644
+ if (!gecko) updateDisplay(cm, [], val);
1645
+ if (cm.display.scroller.scrollTop != val) cm.display.scroller.scrollTop = val;
1646
+ if (cm.display.scrollbarV.scrollTop != val) cm.display.scrollbarV.scrollTop = val;
1647
+ if (gecko) updateDisplay(cm, []);
1648
+ }
1649
+ function setScrollLeft(cm, val, isScroller) {
1650
+ if (isScroller ? val == cm.view.scrollLeft : Math.abs(cm.view.scrollLeft - val) < 2) return;
1651
+ val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth);
1652
+ cm.view.scrollLeft = val;
1653
+ alignHorizontally(cm);
1654
+ if (cm.display.scroller.scrollLeft != val) cm.display.scroller.scrollLeft = val;
1655
+ if (cm.display.scrollbarH.scrollLeft != val) cm.display.scrollbarH.scrollLeft = val;
1656
+ }
1657
+
1658
+ // Since the delta values reported on mouse wheel events are
1659
+ // unstandardized between browsers and even browser versions, and
1660
+ // generally horribly unpredictable, this code starts by measuring
1661
+ // the scroll effect that the first few mouse wheel events have,
1662
+ // and, from that, detects the way it can convert deltas to pixel
1663
+ // offsets afterwards.
1664
+ //
1665
+ // The reason we want to know the amount a wheel event will scroll
1666
+ // is that it gives us a chance to update the display before the
1667
+ // actual scrolling happens, reducing flickering.
1668
+
1669
+ var wheelSamples = 0, wheelPixelsPerUnit = null;
1670
+ // Fill in a browser-detected starting value on browsers where we
1671
+ // know one. These don't have to be accurate -- the result of them
1672
+ // being wrong would just be a slight flicker on the first wheel
1673
+ // scroll (if it is large enough).
1674
+ if (ie) wheelPixelsPerUnit = -.53;
1675
+ else if (gecko) wheelPixelsPerUnit = 15;
1676
+ else if (chrome) wheelPixelsPerUnit = -.7;
1677
+ else if (safari) wheelPixelsPerUnit = -1/3;
1678
+
1679
+ function onScrollWheel(cm, e) {
1680
+ var dx = e.wheelDeltaX, dy = e.wheelDeltaY;
1681
+ if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) dx = e.detail;
1682
+ if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) dy = e.detail;
1683
+ else if (dy == null) dy = e.wheelDelta;
1684
+
1685
+ // Webkit browsers on OS X abort momentum scrolls when the target
1686
+ // of the scroll event is removed from the scrollable element.
1687
+ // This hack (see related code in patchDisplay) makes sure the
1688
+ // element is kept around.
1689
+ if (dy && mac && webkit) {
1690
+ for (var cur = e.target; cur != scroll; cur = cur.parentNode) {
1691
+ if (cur.lineObj) {
1692
+ cm.display.currentWheelTarget = cur;
1693
+ break;
1694
  }
1695
+ }
1696
+ }
1697
+
1698
+ var display = cm.display, scroll = display.scroller;
1699
+ // On some browsers, horizontal scrolling will cause redraws to
1700
+ // happen before the gutter has been realigned, causing it to
1701
+ // wriggle around in a most unseemly way. When we have an
1702
+ // estimated pixels/delta value, we just handle horizontal
1703
+ // scrolling entirely here. It'll be slightly off from native, but
1704
+ // better than glitching out.
1705
+ if (dx && !gecko && !opera && wheelPixelsPerUnit != null) {
1706
+ if (dy)
1707
+ setScrollTop(cm, Math.max(0, Math.min(scroll.scrollTop + dy * wheelPixelsPerUnit, scroll.scrollHeight - scroll.clientHeight)));
1708
+ setScrollLeft(cm, Math.max(0, Math.min(scroll.scrollLeft + dx * wheelPixelsPerUnit, scroll.scrollWidth - scroll.clientWidth)));
1709
+ e_preventDefault(e);
1710
+ display.wheelStartX = null; // Abort measurement, if in progress
1711
+ return;
1712
+ }
1713
+
1714
+ if (dy && wheelPixelsPerUnit != null) {
1715
+ var pixels = dy * wheelPixelsPerUnit;
1716
+ var top = cm.view.scrollTop, bot = top + display.wrapper.clientHeight;
1717
+ if (pixels < 0) top = Math.max(0, top + pixels - 50);
1718
+ else bot = Math.min(cm.view.doc.height, bot + pixels + 50);
1719
+ updateDisplay(cm, [], {top: top, bottom: bot});
1720
+ }
1721
+
1722
+ if (wheelSamples < 20) {
1723
+ if (display.wheelStartX == null) {
1724
+ display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop;
1725
+ display.wheelDX = dx; display.wheelDY = dy;
1726
+ setTimeout(function() {
1727
+ if (display.wheelStartX == null) return;
1728
+ var movedX = scroll.scrollLeft - display.wheelStartX;
1729
+ var movedY = scroll.scrollTop - display.wheelStartY;
1730
+ var sample = (movedY && display.wheelDY && movedY / display.wheelDY) ||
1731
+ (movedX && display.wheelDX && movedX / display.wheelDX);
1732
+ display.wheelStartX = display.wheelStartY = null;
1733
+ if (!sample) return;
1734
+ wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1);
1735
+ ++wheelSamples;
1736
+ }, 200);
1737
+ } else {
1738
+ display.wheelDX += dx; display.wheelDY += dy;
1739
+ }
1740
+ }
1741
+ }
1742
+
1743
+ function doHandleBinding(cm, bound, dropShift) {
1744
+ if (typeof bound == "string") {
1745
+ bound = commands[bound];
1746
+ if (!bound) return false;
1747
+ }
1748
+ // Ensure previous input has been read, so that the handler sees a
1749
+ // consistent view of the document
1750
+ if (cm.display.pollingFast && readInput(cm)) cm.display.pollingFast = false;
1751
+ var view = cm.view, prevShift = view.sel.shift;
1752
+ try {
1753
+ if (isReadOnly(cm)) view.suppressEdits = true;
1754
+ if (dropShift) view.sel.shift = false;
1755
+ bound(cm);
1756
+ } catch(e) {
1757
+ if (e != Pass) throw e;
1758
+ return false;
1759
+ } finally {
1760
+ view.sel.shift = prevShift;
1761
+ view.suppressEdits = false;
1762
+ }
1763
+ return true;
1764
+ }
1765
+
1766
+ function allKeyMaps(cm) {
1767
+ var maps = cm.view.keyMaps.slice(0);
1768
+ maps.push(cm.options.keyMap);
1769
+ if (cm.options.extraKeys) maps.unshift(cm.options.extraKeys);
1770
+ return maps;
1771
+ }
1772
+
1773
+ var maybeTransition;
1774
+ function handleKeyBinding(cm, e) {
1775
+ // Handle auto keymap transitions
1776
+ var startMap = getKeyMap(cm.options.keyMap), next = startMap.auto;
1777
+ clearTimeout(maybeTransition);
1778
+ if (next && !isModifierKey(e)) maybeTransition = setTimeout(function() {
1779
+ if (getKeyMap(cm.options.keyMap) == startMap)
1780
+ cm.options.keyMap = (next.call ? next.call(null, cm) : next);
1781
+ }, 50);
1782
+
1783
+ var name = keyNames[e_prop(e, "keyCode")], handled = false;
1784
+ if (name == null || e.altGraphKey) return false;
1785
+ if (e_prop(e, "altKey")) name = "Alt-" + name;
1786
+ if (e_prop(e, flipCtrlCmd ? "metaKey" : "ctrlKey")) name = "Ctrl-" + name;
1787
+ if (e_prop(e, flipCtrlCmd ? "ctrlKey" : "metaKey")) name = "Cmd-" + name;
1788
+
1789
+ var stopped = false;
1790
+ function stop() { stopped = true; }
1791
+ var keymaps = allKeyMaps(cm);
1792
+
1793
+ if (e_prop(e, "shiftKey")) {
1794
+ handled = lookupKey("Shift-" + name, keymaps,
1795
+ function(b) {return doHandleBinding(cm, b, true);}, stop)
1796
+ || lookupKey(name, keymaps, function(b) {
1797
+ if (typeof b == "string" && /^go[A-Z]/.test(b)) return doHandleBinding(cm, b);
1798
+ }, stop);
1799
+ } else {
1800
+ handled = lookupKey(name, keymaps,
1801
+ function(b) { return doHandleBinding(cm, b); }, stop);
1802
+ }
1803
+ if (stopped) handled = false;
1804
+ if (handled) {
1805
+ e_preventDefault(e);
1806
+ restartBlink(cm);
1807
+ if (ie_lt9) { e.oldKeyCode = e.keyCode; e.keyCode = 0; }
1808
+ }
1809
+ return handled;
1810
+ }
1811
+
1812
+ function handleCharBinding(cm, e, ch) {
1813
+ var handled = lookupKey("'" + ch + "'", allKeyMaps(cm),
1814
+ function(b) { return doHandleBinding(cm, b, true); });
1815
+ if (handled) {
1816
+ e_preventDefault(e);
1817
+ restartBlink(cm);
1818
+ }
1819
+ return handled;
1820
+ }
1821
+
1822
+ var lastStoppedKey = null;
1823
+ function onKeyDown(e) {
1824
+ var cm = this;
1825
+ if (!cm.view.focused) onFocus(cm);
1826
+ if (ie && e.keyCode == 27) { e.returnValue = false; }
1827
+ if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
1828
+ var code = e_prop(e, "keyCode");
1829
+ // IE does strange things with escape.
1830
+ cm.view.sel.shift = code == 16 || e_prop(e, "shiftKey");
1831
+ // First give onKeyEvent option a chance to handle this.
1832
+ var handled = handleKeyBinding(cm, e);
1833
+ if (opera) {
1834
+ lastStoppedKey = handled ? code : null;
1835
+ // Opera has no cut event... we try to at least catch the key combo
1836
+ if (!handled && code == 88 && !hasCopyEvent && e_prop(e, mac ? "metaKey" : "ctrlKey"))
1837
+ cm.replaceSelection("");
1838
+ }
1839
+ }
1840
+
1841
+ function onKeyPress(e) {
1842
+ var cm = this;
1843
+ if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
1844
+ var keyCode = e_prop(e, "keyCode"), charCode = e_prop(e, "charCode");
1845
+ if (opera && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;}
1846
+ if (((opera && (!e.which || e.which < 10)) || khtml) && handleKeyBinding(cm, e)) return;
1847
+ var ch = String.fromCharCode(charCode == null ? keyCode : charCode);
1848
+ if (this.options.electricChars && this.view.mode.electricChars &&
1849
+ this.options.smartIndent && !isReadOnly(this) &&
1850
+ this.view.mode.electricChars.indexOf(ch) > -1)
1851
+ setTimeout(operation(cm, function() {indentLine(cm, cm.view.sel.to.line, "smart");}), 75);
1852
+ if (handleCharBinding(cm, e, ch)) return;
1853
+ fastPoll(cm);
1854
+ }
1855
+
1856
+ function onFocus(cm) {
1857
+ if (cm.options.readOnly == "nocursor") return;
1858
+ if (!cm.view.focused) {
1859
+ signal(cm, "focus", cm);
1860
+ cm.view.focused = true;
1861
+ if (cm.display.scroller.className.search(/\bCodeMirror-focused\b/) == -1)
1862
+ cm.display.scroller.className += " CodeMirror-focused";
1863
+ resetInput(cm, true);
1864
+ }
1865
+ slowPoll(cm);
1866
+ restartBlink(cm);
1867
+ }
1868
+ function onBlur(cm) {
1869
+ if (cm.view.focused) {
1870
+ signal(cm, "blur", cm);
1871
+ cm.view.focused = false;
1872
+ cm.display.scroller.className = cm.display.scroller.className.replace(" CodeMirror-focused", "");
1873
+ }
1874
+ clearInterval(cm.display.blinker);
1875
+ setTimeout(function() {if (!cm.view.focused) cm.view.sel.shift = false;}, 150);
1876
+ }
1877
+
1878
+ var detectingSelectAll;
1879
+ function onContextMenu(cm, e) {
1880
+ var display = cm.display;
1881
+ if (eventInWidget(display, e)) return;
1882
+
1883
+ var sel = cm.view.sel;
1884
+ var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop;
1885
+ if (!pos || opera) return; // Opera is difficult.
1886
+ if (posEq(sel.from, sel.to) || posLess(pos, sel.from) || !posLess(pos, sel.to))
1887
+ operation(cm, setSelection)(cm, pos, pos);
1888
+
1889
+ var oldCSS = display.input.style.cssText;
1890
+ display.inputDiv.style.position = "absolute";
1891
+ display.input.style.cssText = "position: fixed; width: 30px; height: 30px; top: " + (e.clientY - 5) +
1892
+ "px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: white; outline: none;" +
1893
+ "border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);";
1894
+ focusInput(cm);
1895
+ resetInput(cm, true);
1896
+ // Adds "Select all" to context menu in FF
1897
+ if (posEq(sel.from, sel.to)) display.input.value = display.prevInput = " ";
1898
+
1899
+ function rehide() {
1900
+ display.inputDiv.style.position = "relative";
1901
+ display.input.style.cssText = oldCSS;
1902
+ if (ie_lt9) display.scrollbarV.scrollTop = display.scroller.scrollTop = scrollPos;
1903
+ slowPoll(cm);
1904
+
1905
+ // Try to detect the user choosing select-all
1906
+ if (display.input.selectionStart != null) {
1907
+ clearTimeout(detectingSelectAll);
1908
+ var extval = display.input.value = " " + (posEq(sel.from, sel.to) ? "" : display.input.value), i = 0;
1909
+ display.prevInput = " ";
1910
+ display.input.selectionStart = 1; display.input.selectionEnd = extval.length;
1911
+ detectingSelectAll = setTimeout(function poll(){
1912
+ if (display.prevInput == " " && display.input.selectionStart == 0)
1913
+ operation(cm, commands.selectAll)(cm);
1914
+ else if (i++ < 10) detectingSelectAll = setTimeout(poll, 500);
1915
+ else resetInput(cm);
1916
+ }, 200);
1917
+ }
1918
+ }
1919
+
1920
+ if (gecko) {
1921
+ e_stop(e);
1922
+ on(window, "mouseup", function mouseup() {
1923
+ off(window, "mouseup", mouseup);
1924
+ setTimeout(rehide, 20);
1925
  });
1926
+ } else {
1927
+ setTimeout(rehide, 50);
1928
  }
1929
+ }
1930
 
1931
+ // UPDATING
1932
+
1933
+ // Replace the range from from to to by the strings in newText.
1934
+ // Afterwards, set the selection to selFrom, selTo.
1935
+ function updateDoc(cm, from, to, newText, selUpdate, origin) {
1936
+ // Possibly split or suppress the update based on the presence
1937
+ // of read-only spans in its range.
1938
+ var split = sawReadOnlySpans &&
1939
+ removeReadOnlyRanges(cm.view.doc, from, to);
1940
+ if (split) {
1941
+ for (var i = split.length - 1; i >= 1; --i)
1942
+ updateDocInner(cm, split[i].from, split[i].to, [""], origin);
1943
+ if (split.length)
1944
+ return updateDocInner(cm, split[0].from, split[0].to, newText, selUpdate, origin);
1945
+ } else {
1946
+ return updateDocInner(cm, from, to, newText, selUpdate, origin);
1947
+ }
1948
+ }
1949
+
1950
+ function updateDocInner(cm, from, to, newText, selUpdate, origin) {
1951
+ if (cm.view.suppressEdits) return;
1952
+
1953
+ var view = cm.view, doc = view.doc, old = [];
1954
+ doc.iter(from.line, to.line + 1, function(line) {
1955
+ old.push(newHL(line.text, line.markedSpans));
1956
+ });
1957
+ var startSelFrom = view.sel.from, startSelTo = view.sel.to;
1958
+ var lines = updateMarkedSpans(hlSpans(old[0]), hlSpans(lst(old)), from.ch, to.ch, newText);
1959
+ var retval = updateDocNoUndo(cm, from, to, lines, selUpdate, origin);
1960
+ if (view.history) addChange(cm, from.line, newText.length, old, origin,
1961
+ startSelFrom, startSelTo, view.sel.from, view.sel.to);
1962
+ return retval;
1963
+ }
1964
+
1965
+ function unredoHelper(cm, type) {
1966
+ var doc = cm.view.doc, hist = cm.view.history;
1967
+ var set = (type == "undo" ? hist.done : hist.undone).pop();
1968
+ if (!set) return;
1969
+ var anti = {events: [], fromBefore: set.fromAfter, toBefore: set.toAfter,
1970
+ fromAfter: set.fromBefore, toAfter: set.toBefore};
1971
+ for (var i = set.events.length - 1; i >= 0; i -= 1) {
1972
+ hist.dirtyCounter += type == "undo" ? -1 : 1;
1973
+ var change = set.events[i];
1974
+ var replaced = [], end = change.start + change.added;
1975
+ doc.iter(change.start, end, function(line) { replaced.push(newHL(line.text, line.markedSpans)); });
1976
+ anti.events.push({start: change.start, added: change.old.length, old: replaced});
1977
+ var selPos = i ? null : {from: set.fromBefore, to: set.toBefore};
1978
+ updateDocNoUndo(cm, {line: change.start, ch: 0}, {line: end - 1, ch: getLine(doc, end-1).text.length},
1979
+ change.old, selPos, type);
1980
+ }
1981
+ (type == "undo" ? hist.undone : hist.done).push(anti);
1982
+ }
1983
+
1984
+ function updateDocNoUndo(cm, from, to, lines, selUpdate, origin) {
1985
+ var view = cm.view, doc = view.doc, display = cm.display;
1986
+ if (view.suppressEdits) return;
1987
+
1988
+ var nlines = to.line - from.line, firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line);
1989
+ var recomputeMaxLength = false, checkWidthStart = from.line;
1990
+ if (!cm.options.lineWrapping) {
1991
+ checkWidthStart = lineNo(visualLine(doc, firstLine));
1992
+ doc.iter(checkWidthStart, to.line + 1, function(line) {
1993
+ if (line == view.maxLine) {
1994
+ recomputeMaxLength = true;
1995
+ return true;
1996
  }
 
1997
  });
1998
+ }
1999
+
2000
+ var lastHL = lst(lines), th = textHeight(display);
2001
+
2002
+ // First adjust the line structure
2003
+ if (from.ch == 0 && to.ch == 0 && hlText(lastHL) == "") {
2004
+ // This is a whole-line replace. Treated specially to make
2005
+ // sure line objects move the way they are supposed to.
2006
+ var added = [];
2007
+ for (var i = 0, e = lines.length - 1; i < e; ++i)
2008
+ added.push(makeLine(hlText(lines[i]), hlSpans(lines[i]), th));
2009
+ updateLine(cm, lastLine, lastLine.text, hlSpans(lastHL));
2010
+ if (nlines) doc.remove(from.line, nlines, cm);
2011
+ if (added.length) doc.insert(from.line, added);
2012
+ } else if (firstLine == lastLine) {
2013
+ if (lines.length == 1) {
2014
+ updateLine(cm, firstLine, firstLine.text.slice(0, from.ch) + hlText(lines[0]) +
2015
+ firstLine.text.slice(to.ch), hlSpans(lines[0]));
 
 
 
 
 
 
 
 
 
 
2016
  } else {
2017
+ for (var added = [], i = 1, e = lines.length - 1; i < e; ++i)
2018
+ added.push(makeLine(hlText(lines[i]), hlSpans(lines[i]), th));
2019
+ added.push(makeLine(hlText(lastHL) + firstLine.text.slice(to.ch), hlSpans(lastHL), th));
2020
+ updateLine(cm, firstLine, firstLine.text.slice(0, from.ch) + hlText(lines[0]), hlSpans(lines[0]));
2021
+ doc.insert(from.line + 1, added);
2022
+ }
2023
+ } else if (lines.length == 1) {
2024
+ updateLine(cm, firstLine, firstLine.text.slice(0, from.ch) + hlText(lines[0]) +
2025
+ lastLine.text.slice(to.ch), hlSpans(lines[0]));
2026
+ doc.remove(from.line + 1, nlines, cm);
2027
+ } else {
2028
+ var added = [];
2029
+ updateLine(cm, firstLine, firstLine.text.slice(0, from.ch) + hlText(lines[0]), hlSpans(lines[0]));
2030
+ updateLine(cm, lastLine, hlText(lastHL) + lastLine.text.slice(to.ch), hlSpans(lastHL));
2031
+ for (var i = 1, e = lines.length - 1; i < e; ++i)
2032
+ added.push(makeLine(hlText(lines[i]), hlSpans(lines[i]), th));
2033
+ if (nlines > 1) doc.remove(from.line + 1, nlines - 1, cm);
2034
+ doc.insert(from.line + 1, added);
2035
+ }
2036
+
2037
+ if (cm.options.lineWrapping) {
2038
+ var perLine = Math.max(5, display.scroller.clientWidth / charWidth(display) - 3);
2039
+ doc.iter(from.line, from.line + lines.length, function(line) {
2040
+ if (line.height == 0) return;
2041
+ var guess = (Math.ceil(line.text.length / perLine) || 1) * th;
2042
+ if (guess != line.height) updateLineHeight(line, guess);
2043
+ });
2044
+ } else {
2045
+ doc.iter(checkWidthStart, from.line + lines.length, function(line) {
2046
+ var len = lineLength(doc, line);
2047
+ if (len > view.maxLineLength) {
2048
+ view.maxLine = line;
2049
+ view.maxLineLength = len;
2050
+ view.maxLineChanged = true;
2051
+ recomputeMaxLength = false;
2052
  }
2053
+ });
2054
+ if (recomputeMaxLength) cm.curOp.updateMaxLine = true;
2055
+ }
2056
+
2057
+ // Adjust frontier, schedule worker
2058
+ view.frontier = Math.min(view.frontier, from.line);
2059
+ startWorker(cm, 400);
2060
+
2061
+ var lendiff = lines.length - nlines - 1;
2062
+ // Remember that these lines changed, for updating the display
2063
+ regChange(cm, from.line, to.line + 1, lendiff);
2064
+ if (hasHandler(cm, "change")) {
2065
+ // Normalize lines to contain only strings, since that's what
2066
+ // the change event handler expects
2067
+ for (var i = 0; i < lines.length; ++i)
2068
+ if (typeof lines[i] != "string") lines[i] = lines[i].text;
2069
+ var changeObj = {from: from, to: to, text: lines, origin: origin};
2070
+ if (cm.curOp.textChanged) {
2071
+ for (var cur = cm.curOp.textChanged; cur.next; cur = cur.next) {}
2072
+ cur.next = changeObj;
2073
+ } else cm.curOp.textChanged = changeObj;
2074
+ }
2075
+
2076
+ // Update the selection
2077
+ var newSelFrom, newSelTo, end = {line: from.line + lines.length - 1,
2078
+ ch: hlText(lastHL).length + (lines.length == 1 ? from.ch : 0)};
2079
+ if (selUpdate && typeof selUpdate != "string") {
2080
+ if (selUpdate.from) { newSelFrom = selUpdate.from; newSelTo = selUpdate.to; }
2081
+ else newSelFrom = newSelTo = selUpdate;
2082
+ } else if (selUpdate == "end") {
2083
+ newSelFrom = newSelTo = end;
2084
+ } else if (selUpdate == "start") {
2085
+ newSelFrom = newSelTo = from;
2086
+ } else if (selUpdate == "around") {
2087
+ newSelFrom = from; newSelTo = end;
2088
+ } else {
2089
+ var adjustPos = function(pos) {
2090
+ if (posLess(pos, from)) return pos;
2091
+ if (!posLess(to, pos)) return end;
2092
+ var line = pos.line + lendiff;
2093
+ var ch = pos.ch;
2094
+ if (pos.line == to.line)
2095
+ ch += hlText(lastHL).length - (to.ch - (to.line == from.line ? from.ch : 0));
2096
+ return {line: line, ch: ch};
2097
+ };
2098
+ newSelFrom = adjustPos(view.sel.from);
2099
+ newSelTo = adjustPos(view.sel.to);
2100
+ }
2101
+ setSelection(cm, newSelFrom, newSelTo, null, true);
2102
+ return end;
2103
+ }
2104
+
2105
+ function replaceRange(cm, code, from, to, origin) {
2106
+ if (!to) to = from;
2107
+ if (posLess(to, from)) { var tmp = to; to = from; from = tmp; }
2108
+ return updateDoc(cm, from, to, splitLines(code), null, origin);
2109
+ }
2110
+
2111
+ // SELECTION
2112
+
2113
+ function posEq(a, b) {return a.line == b.line && a.ch == b.ch;}
2114
+ function posLess(a, b) {return a.line < b.line || (a.line == b.line && a.ch < b.ch);}
2115
+ function copyPos(x) {return {line: x.line, ch: x.ch};}
2116
+
2117
+ function clipLine(doc, n) {return Math.max(0, Math.min(n, doc.size-1));}
2118
+ function clipPos(doc, pos) {
2119
+ if (pos.line < 0) return {line: 0, ch: 0};
2120
+ if (pos.line >= doc.size) return {line: doc.size-1, ch: getLine(doc, doc.size-1).text.length};
2121
+ var ch = pos.ch, linelen = getLine(doc, pos.line).text.length;
2122
+ if (ch == null || ch > linelen) return {line: pos.line, ch: linelen};
2123
+ else if (ch < 0) return {line: pos.line, ch: 0};
2124
+ else return pos;
2125
+ }
2126
+ function isLine(doc, l) {return l >= 0 && l < doc.size;}
2127
+
2128
+ // If shift is held, this will move the selection anchor. Otherwise,
2129
+ // it'll set the whole selection.
2130
+ function extendSelection(cm, pos, other, bias) {
2131
+ var sel = cm.view.sel;
2132
+ if (sel.shift || sel.extend) {
2133
+ var anchor = sel.anchor;
2134
+ if (other) {
2135
+ var posBefore = posLess(pos, anchor);
2136
+ if (posBefore != posLess(other, anchor)) {
2137
+ anchor = pos;
2138
+ pos = other;
2139
+ } else if (posBefore != posLess(pos, other)) {
2140
+ pos = other;
2141
  }
2142
+ }
2143
+ setSelection(cm, anchor, pos, bias);
2144
+ } else {
2145
+ setSelection(cm, pos, other || pos, bias);
2146
+ }
2147
+ cm.curOp.userSelChange = true;
2148
+ }
2149
+
2150
+ // Update the selection. Last two args are only used by
2151
+ // updateDoc, since they have to be expressed in the line
2152
+ // numbers before the update.
2153
+ function setSelection(cm, anchor, head, bias, checkAtomic) {
2154
+ cm.view.goalColumn = null;
2155
+ var sel = cm.view.sel;
2156
+ // Skip over atomic spans.
2157
+ if (checkAtomic || !posEq(anchor, sel.anchor))
2158
+ anchor = skipAtomic(cm, anchor, bias, checkAtomic != "push");
2159
+ if (checkAtomic || !posEq(head, sel.head))
2160
+ head = skipAtomic(cm, head, bias, checkAtomic != "push");
2161
+
2162
+ if (posEq(sel.anchor, anchor) && posEq(sel.head, head)) return;
2163
+
2164
+ sel.anchor = anchor; sel.head = head;
2165
+ var inv = posLess(head, anchor);
2166
+ sel.from = inv ? head : anchor;
2167
+ sel.to = inv ? anchor : head;
2168
+
2169
+ cm.curOp.updateInput = true;
2170
+ cm.curOp.selectionChanged = true;
2171
+ }
2172
+
2173
+ function reCheckSelection(cm) {
2174
+ setSelection(cm, cm.view.sel.from, cm.view.sel.to, null, "push");
2175
+ }
2176
+
2177
+ function skipAtomic(cm, pos, bias, mayClear) {
2178
+ var doc = cm.view.doc, flipped = false, curPos = pos;
2179
+ var dir = bias || 1;
2180
+ cm.view.cantEdit = false;
2181
+ search: for (;;) {
2182
+ var line = getLine(doc, curPos.line), toClear;
2183
+ if (line.markedSpans) {
2184
+ for (var i = 0; i < line.markedSpans.length; ++i) {
2185
+ var sp = line.markedSpans[i], m = sp.marker;
2186
+ if ((sp.from == null || (m.inclusiveLeft ? sp.from <= curPos.ch : sp.from < curPos.ch)) &&
2187
+ (sp.to == null || (m.inclusiveRight ? sp.to >= curPos.ch : sp.to > curPos.ch))) {
2188
+ if (mayClear && m.clearOnEnter) {
2189
+ (toClear || (toClear = [])).push(m);
2190
+ continue;
2191
+ } else if (!m.atomic) continue;
2192
+ var newPos = m.find()[dir < 0 ? "from" : "to"];
2193
+ if (posEq(newPos, curPos)) {
2194
+ newPos.ch += dir;
2195
+ if (newPos.ch < 0) {
2196
+ if (newPos.line) newPos = clipPos(doc, {line: newPos.line - 1});
2197
+ else newPos = null;
2198
+ } else if (newPos.ch > line.text.length) {
2199
+ if (newPos.line < doc.size - 1) newPos = {line: newPos.line + 1, ch: 0};
2200
+ else newPos = null;
2201
+ }
2202
+ if (!newPos) {
2203
+ if (flipped) {
2204
+ // Driven in a corner -- no valid cursor position found at all
2205
+ // -- try again *with* clearing, if we didn't already
2206
+ if (!mayClear) return skipAtomic(cm, pos, bias, true);
2207
+ // Otherwise, turn off editing until further notice, and return the start of the doc
2208
+ cm.view.cantEdit = true;
2209
+ return {line: 0, ch: 0};
2210
+ }
2211
+ flipped = true; newPos = pos; dir = -dir;
2212
  }
2213
+ }
2214
+ curPos = newPos;
2215
+ continue search;
2216
+ }
2217
  }
2218
+ if (toClear) for (var i = 0; i < toClear.length; ++i) toClear[i].clear();
2219
  }
2220
+ return curPos;
2221
+ }
2222
+ }
2223
 
2224
+ // SCROLLING
2225
+
2226
+ function scrollCursorIntoView(cm) {
2227
+ var view = cm.view;
2228
+ var coords = scrollPosIntoView(cm, view.sel.head);
2229
+ if (!view.focused) return;
2230
+ var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null;
2231
+ if (coords.top + box.top < 0) doScroll = true;
2232
+ else if (coords.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false;
2233
+ if (doScroll != null && !phantom) {
2234
+ var hidden = display.cursor.style.display == "none";
2235
+ if (hidden) {
2236
+ display.cursor.style.display = "";
2237
+ display.cursor.style.left = coords.left + "px";
2238
+ display.cursor.style.top = (coords.top - display.viewOffset) + "px";
2239
+ }
2240
+ display.cursor.scrollIntoView(doScroll);
2241
+ if (hidden) display.cursor.style.display = "none";
2242
  }
2243
+ }
2244
+
2245
+ function scrollPosIntoView(cm, pos) {
2246
+ for (;;) {
2247
+ var changed = false, coords = cursorCoords(cm, pos);
2248
+ var scrollPos = calculateScrollPos(cm, coords.left, coords.top, coords.left, coords.bottom);
2249
+ var startTop = cm.view.scrollTop, startLeft = cm.view.scrollLeft;
2250
+ if (scrollPos.scrollTop != null) {
2251
+ setScrollTop(cm, scrollPos.scrollTop);
2252
+ if (Math.abs(cm.view.scrollTop - startTop) > 1) changed = true;
 
 
2253
  }
2254
+ if (scrollPos.scrollLeft != null) {
2255
+ setScrollLeft(cm, scrollPos.scrollLeft);
2256
+ if (Math.abs(cm.view.scrollLeft - startLeft) > 1) changed = true;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2257
  }
2258
+ if (!changed) return coords;
2259
+ }
2260
+ }
2261
+
2262
+ function scrollIntoView(cm, x1, y1, x2, y2) {
2263
+ var scrollPos = calculateScrollPos(cm, x1, y1, x2, y2);
2264
+ if (scrollPos.scrollTop != null) setScrollTop(cm, scrollPos.scrollTop);
2265
+ if (scrollPos.scrollLeft != null) setScrollLeft(cm, scrollPos.scrollLeft);
2266
+ }
2267
+
2268
+ function calculateScrollPos(cm, x1, y1, x2, y2) {
2269
+ var display = cm.display, pt = paddingTop(display);
2270
+ y1 += pt; y2 += pt;
2271
+ var screen = display.scroller.clientHeight - scrollerCutOff, screentop = display.scroller.scrollTop, result = {};
2272
+ var docBottom = cm.view.doc.height + 2 * pt;
2273
+ var atTop = y1 < pt + 10, atBottom = y2 + pt > docBottom - 10;
2274
+ if (y1 < screentop) result.scrollTop = atTop ? 0 : Math.max(0, y1);
2275
+ else if (y2 > screentop + screen) result.scrollTop = (atBottom ? docBottom : y2) - screen;
2276
+
2277
+ var screenw = display.scroller.clientWidth - scrollerCutOff, screenleft = display.scroller.scrollLeft;
2278
+ x1 += display.gutters.offsetWidth; x2 += display.gutters.offsetWidth;
2279
+ var gutterw = display.gutters.offsetWidth;
2280
+ var atLeft = x1 < gutterw + 10;
2281
+ if (x1 < screenleft + gutterw || atLeft) {
2282
+ if (atLeft) x1 = 0;
2283
+ result.scrollLeft = Math.max(0, x1 - 10 - gutterw);
2284
+ } else if (x2 > screenw + screenleft - 3) {
2285
+ result.scrollLeft = x2 + 10 - screenw;
2286
+ }
2287
+ return result;
2288
+ }
2289
+
2290
+ // API UTILITIES
2291
+
2292
+ function indentLine(cm, n, how, aggressive) {
2293
+ var doc = cm.view.doc;
2294
+ if (!how) how = "add";
2295
+ if (how == "smart") {
2296
+ if (!cm.view.mode.indent) how = "prev";
2297
+ else var state = getStateBefore(cm, n);
2298
+ }
2299
+
2300
+ var tabSize = cm.options.tabSize;
2301
+ var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize);
2302
+ var curSpaceString = line.text.match(/^\s*/)[0], indentation;
2303
+ if (how == "smart") {
2304
+ indentation = cm.view.mode.indent(state, line.text.slice(curSpaceString.length), line.text);
2305
+ if (indentation == Pass) {
2306
+ if (!aggressive) return;
2307
+ how = "prev";
2308
  }
2309
+ }
2310
+ if (how == "prev") {
2311
+ if (n) indentation = countColumn(getLine(doc, n-1).text, null, tabSize);
2312
+ else indentation = 0;
2313
+ }
2314
+ else if (how == "add") indentation = curSpace + cm.options.indentUnit;
2315
+ else if (how == "subtract") indentation = curSpace - cm.options.indentUnit;
2316
+ indentation = Math.max(0, indentation);
2317
+
2318
+ var indentString = "", pos = 0;
2319
+ if (cm.options.indentWithTabs)
2320
+ for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";}
2321
+ if (pos < indentation) indentString += spaceStr(indentation - pos);
2322
+
2323
+ if (indentString != curSpaceString)
2324
+ replaceRange(cm, indentString, {line: n, ch: 0}, {line: n, ch: curSpaceString.length}, "input");
2325
+ line.stateAfter = null;
2326
+ }
2327
+
2328
+ function changeLine(cm, handle, op) {
2329
+ var no = handle, line = handle, doc = cm.view.doc;
2330
+ if (typeof handle == "number") line = getLine(doc, clipLine(doc, handle));
2331
+ else no = lineNo(handle);
2332
+ if (no == null) return null;
2333
+ if (op(line, no)) regChange(cm, no, no + 1);
2334
+ else return null;
2335
+ return line;
2336
+ }
2337
+
2338
+ function findPosH(cm, dir, unit, visually) {
2339
+ var doc = cm.view.doc, end = cm.view.sel.head, line = end.line, ch = end.ch;
2340
+ var lineObj = getLine(doc, line);
2341
+ function findNextLine() {
2342
+ var l = line + dir;
2343
+ if (l < 0 || l == doc.size) return false;
2344
+ line = l;
2345
+ return lineObj = getLine(doc, l);
2346
+ }
2347
+ function moveOnce(boundToLine) {
2348
+ var next = (visually ? moveVisually : moveLogically)(lineObj, ch, dir, true);
2349
+ if (next == null) {
2350
+ if (!boundToLine && findNextLine()) {
2351
+ if (visually) ch = (dir < 0 ? lineRight : lineLeft)(lineObj);
2352
+ else ch = dir < 0 ? lineObj.text.length : 0;
2353
+ } else return false;
2354
+ } else ch = next;
2355
+ return true;
2356
+ }
2357
+ if (unit == "char") moveOnce();
2358
+ else if (unit == "column") moveOnce(true);
2359
+ else if (unit == "word") {
2360
+ var sawWord = false;
2361
+ for (;;) {
2362
+ if (dir < 0) if (!moveOnce()) break;
2363
+ if (isWordChar(lineObj.text.charAt(ch))) sawWord = true;
2364
+ else if (sawWord) {if (dir < 0) {dir = 1; moveOnce();} break;}
2365
+ if (dir > 0) if (!moveOnce()) break;
2366
+ }
2367
+ }
2368
+ return skipAtomic(cm, {line: line, ch: ch}, dir, true);
2369
+ }
2370
+
2371
+ function findWordAt(line, pos) {
2372
+ var start = pos.ch, end = pos.ch;
2373
+ if (line) {
2374
+ if (pos.after === false || end == line.length) --start; else ++end;
2375
+ var startChar = line.charAt(start);
2376
+ var check = isWordChar(startChar) ? isWordChar :
2377
+ /\s/.test(startChar) ? function(ch) {return /\s/.test(ch);} :
2378
+ function(ch) {return !/\s/.test(ch) && !isWordChar(ch);};
2379
+ while (start > 0 && check(line.charAt(start - 1))) --start;
2380
+ while (end < line.length && check(line.charAt(end))) ++end;
2381
+ }
2382
+ return {from: {line: pos.line, ch: start}, to: {line: pos.line, ch: end}};
2383
+ }
2384
+
2385
+ function selectLine(cm, line) {
2386
+ extendSelection(cm, {line: line, ch: 0}, clipPos(cm.view.doc, {line: line + 1, ch: 0}));
2387
+ }
2388
+
2389
+ // PROTOTYPE
2390
+
2391
+ // The publicly visible API. Note that operation(null, f) means
2392
+ // 'wrap f in an operation, performed on its `this` parameter'
2393
+
2394
+ CodeMirror.prototype = {
2395
+ getValue: function(lineSep) {
2396
+ var text = [], doc = this.view.doc;
2397
+ doc.iter(0, doc.size, function(line) { text.push(line.text); });
2398
+ return text.join(lineSep || "\n");
2399
+ },
2400
+
2401
+ setValue: operation(null, function(code) {
2402
+ var doc = this.view.doc, top = {line: 0, ch: 0}, lastLen = getLine(doc, doc.size-1).text.length;
2403
+ updateDocInner(this, top, {line: doc.size - 1, ch: lastLen}, splitLines(code), top, top, "setValue");
2404
+ }),
2405
+
2406
+ getSelection: function(lineSep) { return this.getRange(this.view.sel.from, this.view.sel.to, lineSep); },
2407
+
2408
+ replaceSelection: operation(null, function(code, collapse, origin) {
2409
+ var sel = this.view.sel;
2410
+ updateDoc(this, sel.from, sel.to, splitLines(code), collapse || "around", origin);
2411
+ }),
2412
+
2413
+ focus: function(){window.focus(); focusInput(this); onFocus(this); fastPoll(this);},
2414
+
2415
+ setOption: function(option, value) {
2416
+ var options = this.options, old = options[option];
2417
+ if (options[option] == value && option != "mode") return;
2418
+ options[option] = value;
2419
+ if (optionHandlers.hasOwnProperty(option))
2420
+ operation(this, optionHandlers[option])(this, value, old);
2421
+ },
2422
+
2423
+ getOption: function(option) {return this.options[option];},
2424
+
2425
+ getMode: function() {return this.view.mode;},
2426
+
2427
+ addKeyMap: function(map) {
2428
+ this.view.keyMaps.push(map);
2429
+ },
2430
+
2431
+ removeKeyMap: function(map) {
2432
+ var maps = this.view.keyMaps;
2433
+ for (var i = 0; i < maps.length; ++i)
2434
+ if ((typeof map == "string" ? maps[i].name : maps[i]) == map) {
2435
+ maps.splice(i, 1);
2436
+ return true;
2437
  }
2438
+ },
2439
+
2440
+ addOverlay: operation(null, function(spec, options) {
2441
+ var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec);
2442
+ if (mode.startState) throw new Error("Overlays may not be stateful.");
2443
+ this.view.overlays.push({mode: mode, modeSpec: spec, opaque: options && options.opaque});
2444
+ this.view.modeGen++;
2445
+ regChange(this, 0, this.view.doc.size);
2446
+ }),
2447
+ removeOverlay: operation(null, function(spec) {
2448
+ var overlays = this.view.overlays;
2449
+ for (var i = 0; i < overlays.length; ++i) {
2450
+ if (overlays[i].modeSpec == spec) {
2451
+ overlays.splice(i, 1);
2452
+ this.view.modeGen++;
2453
+ regChange(this, 0, this.view.doc.size);
2454
+ return;
2455
+ }
2456
+ }
2457
+ }),
2458
+
2459
+ undo: operation(null, function() {unredoHelper(this, "undo");}),
2460
+ redo: operation(null, function() {unredoHelper(this, "redo");}),
2461
+
2462
+ indentLine: operation(null, function(n, dir, aggressive) {
2463
+ if (typeof dir != "string") {
2464
+ if (dir == null) dir = this.options.smartIndent ? "smart" : "prev";
2465
+ else dir = dir ? "add" : "subtract";
2466
  }
2467
+ if (isLine(this.view.doc, n)) indentLine(this, n, dir, aggressive);
2468
+ }),
2469
+
2470
+ indentSelection: operation(null, function(how) {
2471
+ var sel = this.view.sel;
2472
+ if (posEq(sel.from, sel.to)) return indentLine(this, sel.from.line, how);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2473
  var e = sel.to.line - (sel.to.ch ? 0 : 1);
2474
+ for (var i = sel.from.line; i <= e; ++i) indentLine(this, i, how);
2475
+ }),
2476
+
2477
+ historySize: function() {
2478
+ var hist = this.view.history;
2479
+ return {undo: hist.done.length, redo: hist.undone.length};
2480
+ },
2481
+
2482
+ clearHistory: function() {this.view.history = makeHistory();},
2483
+
2484
+ markClean: function() {
2485
+ this.view.history.dirtyCounter = 0;
2486
+ this.view.history.lastOp = this.view.history.lastOrigin = null;
2487
+ },
2488
+
2489
+ isClean: function () {return this.view.history.dirtyCounter == 0;},
2490
+
2491
+ getHistory: function() {
2492
+ var hist = this.view.history;
2493
+ function cp(arr) {
2494
+ for (var i = 0, nw = [], nwelt; i < arr.length; ++i) {
2495
+ var set = arr[i];
2496
+ nw.push({events: nwelt = [], fromBefore: set.fromBefore, toBefore: set.toBefore,
2497
+ fromAfter: set.fromAfter, toAfter: set.toAfter});
2498
+ for (var j = 0, elt = set.events; j < elt.length; ++j) {
2499
+ var old = [], cur = elt[j];
2500
+ nwelt.push({start: cur.start, added: cur.added, old: old});
2501
+ for (var k = 0; k < cur.old.length; ++k) old.push(hlText(cur.old[k]));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2502
  }
2503
  }
2504
+ return nw;
2505
  }
2506
+ return {done: cp(hist.done), undone: cp(hist.undone)};
2507
+ },
2508
+
2509
+ setHistory: function(histData) {
2510
+ var hist = this.view.history = makeHistory();
2511
+ hist.done = histData.done;
2512
+ hist.undone = histData.undone;
2513
+ },
2514
+
2515
+ // Fetch the parser token for a given character. Useful for hacks
2516
+ // that want to inspect the mode state (say, for completion).
2517
+ getTokenAt: function(pos) {
2518
+ var doc = this.view.doc;
2519
+ pos = clipPos(doc, pos);
2520
+ var state = getStateBefore(this, pos.line), mode = this.view.mode;
2521
+ var line = getLine(doc, pos.line);
2522
+ var stream = new StringStream(line.text, this.options.tabSize);
2523
+ while (stream.pos < pos.ch && !stream.eol()) {
2524
+ stream.start = stream.pos;
2525
+ var style = mode.token(stream, state);
2526
+ }
2527
+ return {start: stream.start,
2528
+ end: stream.pos,
2529
+ string: stream.current(),
2530
+ className: style || null, // Deprecated, use 'type' instead
2531
+ type: style || null,
2532
+ state: state};
2533
+ },
2534
+
2535
+ getStateAfter: function(line) {
2536
+ var doc = this.view.doc;
2537
+ line = clipLine(doc, line == null ? doc.size - 1: line);
2538
+ return getStateBefore(this, line + 1);
2539
+ },
2540
 
2541
+ cursorCoords: function(start, mode) {
2542
+ var pos, sel = this.view.sel;
2543
+ if (start == null) pos = sel.head;
2544
+ else if (typeof start == "object") pos = clipPos(this.view.doc, start);
2545
+ else pos = start ? sel.from : sel.to;
2546
+ return cursorCoords(this, pos, mode || "page");
2547
+ },
2548
+
2549
+ charCoords: function(pos, mode) {
2550
+ return charCoords(this, clipPos(this.view.doc, pos), mode || "page");
2551
+ },
2552
+
2553
+ coordsChar: function(coords) {
2554
+ var off = this.display.lineSpace.getBoundingClientRect();
2555
+ return coordsChar(this, coords.left - off.left, coords.top - off.top);
2556
+ },
2557
+
2558
+ defaultTextHeight: function() { return textHeight(this.display); },
2559
+
2560
+ markText: operation(null, function(from, to, options) {
2561
+ return markText(this, clipPos(this.view.doc, from), clipPos(this.view.doc, to),
2562
+ options, "range");
2563
+ }),
2564
+
2565
+ setBookmark: operation(null, function(pos, widget) {
2566
+ pos = clipPos(this.view.doc, pos);
2567
+ return markText(this, pos, pos, widget ? {replacedWith: widget} : {}, "bookmark");
2568
+ }),
2569
+
2570
+ findMarksAt: function(pos) {
2571
+ var doc = this.view.doc;
2572
+ pos = clipPos(doc, pos);
2573
+ var markers = [], spans = getLine(doc, pos.line).markedSpans;
2574
+ if (spans) for (var i = 0; i < spans.length; ++i) {
2575
+ var span = spans[i];
2576
+ if ((span.from == null || span.from <= pos.ch) &&
2577
+ (span.to == null || span.to >= pos.ch))
2578
+ markers.push(span.marker);
2579
  }
2580
  return markers;
2581
+ },
2582
 
2583
+ setGutterMarker: operation(null, function(line, gutterID, value) {
2584
+ return changeLine(this, line, function(line) {
2585
+ var markers = line.gutterMarkers || (line.gutterMarkers = {});
2586
+ markers[gutterID] = value;
2587
+ if (!value && isEmpty(markers)) line.gutterMarkers = null;
2588
+ return true;
2589
+ });
2590
+ }),
2591
+
2592
+ clearGutter: operation(null, function(gutterID) {
2593
+ var i = 0, cm = this, doc = cm.view.doc;
2594
+ doc.iter(0, doc.size, function(line) {
2595
+ if (line.gutterMarkers && line.gutterMarkers[gutterID]) {
2596
+ line.gutterMarkers[gutterID] = null;
2597
+ regChange(cm, i, i + 1);
2598
+ if (isEmpty(line.gutterMarkers)) line.gutterMarkers = null;
 
 
 
 
 
 
 
 
 
 
 
2599
  }
2600
+ ++i;
2601
  });
2602
+ }),
2603
+
2604
+ addLineClass: operation(null, function(handle, where, cls) {
2605
+ return changeLine(this, handle, function(line) {
2606
+ var prop = where == "text" ? "textClass" : where == "background" ? "bgClass" : "wrapClass";
2607
+ if (!line[prop]) line[prop] = cls;
2608
+ else if (new RegExp("\\b" + cls + "\\b").test(line[prop])) return false;
2609
+ else line[prop] += " " + cls;
2610
+ return true;
2611
+ });
2612
+ }),
2613
+
2614
+ removeLineClass: operation(null, function(handle, where, cls) {
2615
+ return changeLine(this, handle, function(line) {
2616
+ var prop = where == "text" ? "textClass" : where == "background" ? "bgClass" : "wrapClass";
2617
+ var cur = line[prop];
2618
+ if (!cur) return false;
2619
+ else if (cls == null) line[prop] = null;
2620
+ else {
2621
+ var upd = cur.replace(new RegExp("^" + cls + "\\b\\s*|\\s*\\b" + cls + "\\b"), "");
2622
+ if (upd == cur) return false;
2623
+ line[prop] = upd || null;
2624
  }
2625
+ return true;
2626
  });
2627
+ }),
2628
+
2629
+ addLineWidget: operation(null, function(handle, node, options) {
2630
+ return addLineWidget(this, handle, node, options);
2631
+ }),
2632
 
2633
+ removeLineWidget: function(widget) { widget.clear(); },
2634
+
2635
+ lineInfo: function(line) {
2636
  if (typeof line == "number") {
2637
+ if (!isLine(this.view.doc, line)) return null;
2638
  var n = line;
2639
+ line = getLine(this.view.doc, line);
2640
  if (!line) return null;
2641
+ } else {
 
2642
  var n = lineNo(line);
2643
  if (n == null) return null;
2644
  }
2645
+ return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers,
2646
+ textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass,
2647
+ widgets: line.widgets};
2648
+ },
2649
 
2650
+ getViewport: function() { return {from: this.display.showingFrom, to: this.display.showingTo};},
2651
+
2652
+ addWidget: function(pos, node, scroll, vert, horiz) {
2653
+ var display = this.display;
2654
+ pos = cursorCoords(this, clipPos(this.view.doc, pos));
2655
+ var top = pos.top, left = pos.left;
2656
+ node.style.position = "absolute";
2657
+ display.sizer.appendChild(node);
2658
+ if (vert == "over") top = pos.top;
2659
+ else if (vert == "near") {
2660
+ var vspace = Math.max(display.wrapper.clientHeight, this.view.doc.height),
2661
+ hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth);
2662
+ if (pos.bottom + node.offsetHeight > vspace && pos.top > node.offsetHeight)
2663
+ top = pos.top - node.offsetHeight;
2664
+ if (left + node.offsetWidth > hspace)
2665
+ left = hspace - node.offsetWidth;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2666
  }
2667
+ node.style.top = (top + paddingTop(display)) + "px";
2668
+ node.style.left = node.style.right = "";
2669
+ if (horiz == "right") {
2670
+ left = display.sizer.clientWidth - node.offsetWidth;
2671
+ node.style.right = "0px";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2672
  } else {
2673
+ if (horiz == "left") left = 0;
2674
+ else if (horiz == "middle") left = (display.sizer.clientWidth - node.offsetWidth) / 2;
2675
+ node.style.left = left + "px";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2676
  }
2677
+ if (scroll)
2678
+ scrollIntoView(this, left, top, left + node.offsetWidth, top + node.offsetHeight);
2679
+ },
2680
+
2681
+ lineCount: function() {return this.view.doc.size;},
2682
+
2683
+ clipPos: function(pos) {return clipPos(this.view.doc, pos);},
2684
+
2685
+ getCursor: function(start) {
2686
+ var sel = this.view.sel, pos;
2687
+ if (start == null || start == "head") pos = sel.head;
2688
+ else if (start == "anchor") pos = sel.anchor;
2689
+ else if (start == "end" || start === false) pos = sel.to;
2690
+ else pos = sel.from;
2691
+ return copyPos(pos);
2692
+ },
2693
+
2694
+ somethingSelected: function() {return !posEq(this.view.sel.from, this.view.sel.to);},
2695
+
2696
+ setCursor: operation(null, function(line, ch, extend) {
2697
+ var pos = clipPos(this.view.doc, typeof line == "number" ? {line: line, ch: ch || 0} : line);
2698
+ if (extend) extendSelection(this, pos);
2699
+ else setSelection(this, pos, pos);
2700
+ }),
2701
+
2702
+ setSelection: operation(null, function(anchor, head) {
2703
+ var doc = this.view.doc;
2704
+ setSelection(this, clipPos(doc, anchor), clipPos(doc, head || anchor));
2705
+ }),
2706
+
2707
+ extendSelection: operation(null, function(from, to) {
2708
+ var doc = this.view.doc;
2709
+ extendSelection(this, clipPos(doc, from), to && clipPos(doc, to));
2710
+ }),
2711
+
2712
+ setExtending: function(val) {this.view.sel.extend = val;},
2713
+
2714
+ getLine: function(line) {var l = this.getLineHandle(line); return l && l.text;},
2715
+
2716
+ getLineHandle: function(line) {
2717
+ var doc = this.view.doc;
2718
+ if (isLine(doc, line)) return getLine(doc, line);
2719
+ },
2720
+
2721
+ getLineNumber: function(line) {return lineNo(line);},
2722
+
2723
+ setLine: operation(null, function(line, text) {
2724
+ if (isLine(this.view.doc, line))
2725
+ replaceRange(this, text, {line: line, ch: 0}, {line: line, ch: getLine(this.view.doc, line).text.length});
2726
+ }),
2727
+
2728
+ removeLine: operation(null, function(line) {
2729
+ if (isLine(this.view.doc, line))
2730
+ replaceRange(this, "", {line: line, ch: 0}, clipPos(this.view.doc, {line: line+1, ch: 0}));
2731
+ }),
2732
+
2733
+ replaceRange: operation(null, function(code, from, to) {
2734
+ var doc = this.view.doc;
2735
+ from = clipPos(doc, from);
2736
+ to = to ? clipPos(doc, to) : from;
2737
+ return replaceRange(this, code, from, to);
2738
+ }),
2739
+
2740
+ getRange: function(from, to, lineSep) {
2741
+ var doc = this.view.doc;
2742
+ from = clipPos(doc, from); to = clipPos(doc, to);
2743
+ var l1 = from.line, l2 = to.line;
2744
+ if (l1 == l2) return getLine(doc, l1).text.slice(from.ch, to.ch);
2745
+ var code = [getLine(doc, l1).text.slice(from.ch)];
2746
+ doc.iter(l1 + 1, l2, function(line) { code.push(line.text); });
2747
+ code.push(getLine(doc, l2).text.slice(0, to.ch));
2748
+ return code.join(lineSep || "\n");
2749
+ },
2750
+
2751
+ triggerOnKeyDown: operation(null, onKeyDown),
2752
+
2753
+ execCommand: function(cmd) {return commands[cmd](this);},
2754
+
2755
+ // Stuff used by commands, probably not much use to outside code.
2756
+ moveH: operation(null, function(dir, unit) {
2757
+ var sel = this.view.sel, pos = dir < 0 ? sel.from : sel.to;
2758
+ if (sel.shift || sel.extend || posEq(sel.from, sel.to))
2759
+ pos = findPosH(this, dir, unit, this.options.rtlMoveVisually);
2760
+ extendSelection(this, pos, pos, dir);
2761
+ }),
2762
+
2763
+ deleteH: operation(null, function(dir, unit) {
2764
+ var sel = this.view.sel;
2765
+ if (!posEq(sel.from, sel.to)) replaceRange(this, "", sel.from, sel.to, "delete");
2766
+ else replaceRange(this, "", sel.from, findPosH(this, dir, unit, false), "delete");
2767
+ this.curOp.userSelChange = true;
2768
+ }),
2769
+
2770
+ moveV: operation(null, function(dir, unit) {
2771
+ var view = this.view, doc = view.doc, display = this.display;
2772
+ var cur = view.sel.head, pos = cursorCoords(this, cur, "div");
2773
+ var x = pos.left, y;
2774
+ if (view.goalColumn != null) x = view.goalColumn;
2775
+ if (unit == "page") {
2776
+ var pageSize = Math.min(display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight);
2777
+ y = pos.top + dir * pageSize;
2778
+ } else if (unit == "line") {
2779
+ y = dir > 0 ? pos.bottom + 3 : pos.top - 3;
2780
  }
2781
+ do {
2782
+ var target = coordsChar(this, x, y);
2783
+ y += dir * 5;
2784
+ } while (target.outside && (dir < 0 ? y > 0 : y < doc.height));
2785
+
2786
+ if (unit == "page") display.scrollbarV.scrollTop += charCoords(this, target, "div").top - pos.top;
2787
+ extendSelection(this, target, target, dir);
2788
+ view.goalColumn = x;
2789
+ }),
2790
+
2791
+ toggleOverwrite: function() {
2792
+ if (this.view.overwrite = !this.view.overwrite)
2793
+ this.display.cursor.className += " CodeMirror-overwrite";
2794
+ else
2795
+ this.display.cursor.className = this.display.cursor.className.replace(" CodeMirror-overwrite", "");
2796
+ },
2797
+
2798
+ posFromIndex: function(off) {
2799
+ var lineNo = 0, ch, doc = this.view.doc;
2800
+ doc.iter(0, doc.size, function(line) {
2801
+ var sz = line.text.length + 1;
2802
+ if (sz > off) { ch = off; return true; }
2803
+ off -= sz;
2804
+ ++lineNo;
2805
  });
2806
+ return clipPos(doc, {line: lineNo, ch: ch});
2807
+ },
2808
+ indexFromPos: function (coords) {
2809
+ coords = clipPos(this.view.doc, coords);
2810
+ var index = coords.ch;
2811
+ this.view.doc.iter(0, coords.line, function (line) {
2812
+ index += line.text.length + 1;
 
 
2813
  });
2814
+ return index;
2815
+ },
2816
+
2817
+ scrollTo: function(x, y) {
2818
+ if (x != null) this.display.scrollbarH.scrollLeft = this.display.scroller.scrollLeft = x;
2819
+ if (y != null) this.display.scrollbarV.scrollTop = this.display.scroller.scrollTop = y;
2820
+ updateDisplay(this, []);
2821
+ },
2822
+ getScrollInfo: function() {
2823
+ var scroller = this.display.scroller, co = scrollerCutOff;
2824
+ return {left: scroller.scrollLeft, top: scroller.scrollTop,
2825
+ height: scroller.scrollHeight - co, width: scroller.scrollWidth - co,
2826
+ clientHeight: scroller.clientHeight - co, clientWidth: scroller.clientWidth - co};
2827
+ },
2828
+
2829
+ scrollIntoView: function(pos) {
2830
+ if (typeof pos == "number") pos = {line: pos, ch: 0};
2831
+ if (!pos || pos.line != null) {
2832
+ pos = pos ? clipPos(this.view.doc, pos) : this.view.sel.head;
2833
+ scrollPosIntoView(this, pos);
2834
+ } else {
2835
+ scrollIntoView(this, pos.left, pos.top, pos.right, pos.bottom);
2836
+ }
2837
+ },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2838
 
2839
+ setSize: function(width, height) {
2840
+ function interpret(val) {
2841
+ return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val;
2842
+ }
2843
+ if (width != null) this.display.wrapper.style.width = interpret(width);
2844
+ if (height != null) this.display.wrapper.style.height = interpret(height);
2845
+ this.refresh();
2846
+ },
2847
 
2848
+ on: function(type, f) {on(this, type, f);},
2849
+ off: function(type, f) {off(this, type, f);},
2850
+
2851
+ operation: function(f){return operation(this, f)();},
2852
+
2853
+ refresh: function() {
2854
+ clearCaches(this);
2855
+ var sTop = this.view.scrollTop, sLeft = this.view.scrollLeft;
2856
+ if (this.display.scroller.scrollHeight > sTop)
2857
+ this.display.scrollbarV.scrollTop = this.display.scroller.scrollTop = sTop;
2858
+ if (this.display.scroller.scrollWidth > sLeft)
2859
+ this.display.scrollbarH.scrollLeft = this.display.scroller.scrollLeft = sLeft;
2860
+ updateDisplay(this, true);
2861
+ },
2862
+
2863
+ getInputField: function(){return this.display.input;},
2864
+ getWrapperElement: function(){return this.display.wrapper;},
2865
+ getScrollerElement: function(){return this.display.scroller;},
2866
+ getGutterElement: function(){return this.display.gutters;}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2867
  };
2868
 
2869
+ // OPTION DEFAULTS
2870
+
2871
+ var optionHandlers = CodeMirror.optionHandlers = {};
2872
+
2873
+ // The default configuration options.
2874
+ var defaults = CodeMirror.defaults = {};
2875
+
2876
+ function option(name, deflt, handle, notOnInit) {
2877
+ CodeMirror.defaults[name] = deflt;
2878
+ if (handle) optionHandlers[name] =
2879
+ notOnInit ? function(cm, val, old) {if (old != Init) handle(cm, val, old);} : handle;
2880
+ }
2881
+
2882
+ var Init = CodeMirror.Init = {toString: function(){return "CodeMirror.Init";}};
2883
+
2884
+ // These two are, on init, called from the constructor because they
2885
+ // have to be initialized before the editor can start at all.
2886
+ option("value", "", function(cm, val) {cm.setValue(val);}, true);
2887
+ option("mode", null, loadMode, true);
2888
+
2889
+ option("indentUnit", 2, loadMode, true);
2890
+ option("indentWithTabs", false);
2891
+ option("smartIndent", true);
2892
+ option("tabSize", 4, function(cm) {
2893
+ loadMode(cm);
2894
+ clearCaches(cm);
2895
+ updateDisplay(cm, true);
2896
+ }, true);
2897
+ option("electricChars", true);
2898
+ option("rtlMoveVisually", !windows);
2899
+
2900
+ option("theme", "default", function(cm) {
2901
+ themeChanged(cm);
2902
+ guttersChanged(cm);
2903
+ }, true);
2904
+ option("keyMap", "default", keyMapChanged);
2905
+ option("extraKeys", null);
2906
+
2907
+ option("onKeyEvent", null);
2908
+ option("onDragEvent", null);
2909
+
2910
+ option("lineWrapping", false, wrappingChanged, true);
2911
+ option("gutters", [], function(cm) {
2912
+ setGuttersForLineNumbers(cm.options);
2913
+ guttersChanged(cm);
2914
+ }, true);
2915
+ option("fixedGutter", true, function(cm, val) {
2916
+ cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0";
2917
+ cm.refresh();
2918
+ }, true);
2919
+ option("lineNumbers", false, function(cm) {
2920
+ setGuttersForLineNumbers(cm.options);
2921
+ guttersChanged(cm);
2922
+ }, true);
2923
+ option("firstLineNumber", 1, guttersChanged, true);
2924
+ option("lineNumberFormatter", function(integer) {return integer;}, guttersChanged, true);
2925
+ option("showCursorWhenSelecting", false, updateSelection, true);
2926
+
2927
+ option("readOnly", false, function(cm, val) {
2928
+ if (val == "nocursor") {onBlur(cm); cm.display.input.blur();}
2929
+ else if (!val) resetInput(cm, true);
2930
+ });
2931
+ option("dragDrop", true);
2932
+
2933
+ option("cursorBlinkRate", 530);
2934
+ option("cursorHeight", 1);
2935
+ option("workTime", 100);
2936
+ option("workDelay", 100);
2937
+ option("flattenSpans", true);
2938
+ option("pollInterval", 100);
2939
+ option("undoDepth", 40);
2940
+ option("viewportMargin", 10, function(cm){cm.refresh();}, true);
2941
+
2942
+ option("tabindex", null, function(cm, val) {
2943
+ cm.display.input.tabIndex = val || "";
2944
+ });
2945
+ option("autofocus", null);
2946
+
2947
+ // MODE DEFINITION AND QUERYING
2948
 
2949
  // Known modes, by name and by MIME
2950
  var modes = CodeMirror.modes = {}, mimeModes = CodeMirror.mimeModes = {};
2951
+
2952
  CodeMirror.defineMode = function(name, mode) {
2953
  if (!CodeMirror.defaults.mode && name != "null") CodeMirror.defaults.mode = name;
2954
+ if (arguments.length > 2) {
2955
+ mode.dependencies = [];
2956
+ for (var i = 2; i < arguments.length; ++i) mode.dependencies.push(arguments[i]);
2957
+ }
2958
  modes[name] = mode;
2959
  };
2960
+
2961
  CodeMirror.defineMIME = function(mime, spec) {
2962
  mimeModes[mime] = spec;
2963
  };
2964
+
2965
  CodeMirror.resolveMode = function(spec) {
2966
  if (typeof spec == "string" && mimeModes.hasOwnProperty(spec))
2967
  spec = mimeModes[spec];
2970
  if (typeof spec == "string") return {name: spec};
2971
  else return spec || {name: "null"};
2972
  };
2973
+
2974
  CodeMirror.getMode = function(options, spec) {
2975
+ spec = CodeMirror.resolveMode(spec);
2976
  var mfactory = modes[spec.name];
2977
+ if (!mfactory) return CodeMirror.getMode(options, "text/plain");
2978
+ var modeObj = mfactory(options, spec);
2979
+ if (modeExtensions.hasOwnProperty(spec.name)) {
2980
+ var exts = modeExtensions[spec.name];
2981
+ for (var prop in exts) {
2982
+ if (!exts.hasOwnProperty(prop)) continue;
2983
+ if (modeObj.hasOwnProperty(prop)) modeObj["_" + prop] = modeObj[prop];
2984
+ modeObj[prop] = exts[prop];
2985
+ }
2986
  }
2987
+ modeObj.name = spec.name;
2988
+ return modeObj;
 
 
 
 
 
2989
  };
2990
+
2991
+ CodeMirror.defineMode("null", function() {
2992
+ return {token: function(stream) {stream.skipToEnd();}};
2993
+ });
2994
+ CodeMirror.defineMIME("text/plain", "null");
2995
+
2996
+ var modeExtensions = CodeMirror.modeExtensions = {};
2997
+ CodeMirror.extendMode = function(mode, properties) {
2998
+ var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});
2999
+ for (var prop in properties) if (properties.hasOwnProperty(prop))
3000
+ exts[prop] = properties[prop];
3001
  };
3002
 
3003
+ // EXTENSIONS
3004
+
3005
  CodeMirror.defineExtension = function(name, func) {
3006
+ CodeMirror.prototype[name] = func;
3007
+ };
3008
+
3009
+ CodeMirror.defineOption = option;
3010
+
3011
+ var initHooks = [];
3012
+ CodeMirror.defineInitHook = function(f) {initHooks.push(f);};
3013
+
3014
+ // MODE STATE HANDLING
3015
+
3016
+ // Utility functions for working with state. Exported because modes
3017
+ // sometimes need to do this.
3018
+ function copyState(mode, state) {
3019
+ if (state === true) return state;
3020
+ if (mode.copyState) return mode.copyState(state);
3021
+ var nstate = {};
3022
+ for (var n in state) {
3023
+ var val = state[n];
3024
+ if (val instanceof Array) val = val.concat([]);
3025
+ nstate[n] = val;
3026
+ }
3027
+ return nstate;
3028
+ }
3029
+ CodeMirror.copyState = copyState;
3030
+
3031
+ function startState(mode, a1, a2) {
3032
+ return mode.startState ? mode.startState(a1, a2) : true;
3033
+ }
3034
+ CodeMirror.startState = startState;
3035
+
3036
+ CodeMirror.innerMode = function(mode, state) {
3037
+ while (mode.innerMode) {
3038
+ var info = mode.innerMode(state);
3039
+ state = info.state;
3040
+ mode = info.mode;
3041
+ }
3042
+ return info || {mode: mode, state: state};
3043
  };
3044
 
3045
+ // STANDARD COMMANDS
3046
+
3047
  var commands = CodeMirror.commands = {
3048
  selectAll: function(cm) {cm.setSelection({line: 0, ch: 0}, {line: cm.lineCount() - 1});},
3049
  killLine: function(cm) {
3050
  var from = cm.getCursor(true), to = cm.getCursor(false), sel = !posEq(from, to);
3051
+ if (!sel && cm.getLine(from.line).length == from.ch)
3052
+ cm.replaceRange("", from, {line: from.line + 1, ch: 0}, "delete");
3053
+ else cm.replaceRange("", from, sel ? to : {line: from.line}, "delete");
3054
+ },
3055
+ deleteLine: function(cm) {
3056
+ var l = cm.getCursor().line;
3057
+ cm.replaceRange("", {line: l, ch: 0}, {line: l}, "delete");
3058
  },
 
3059
  undo: function(cm) {cm.undo();},
3060
  redo: function(cm) {cm.redo();},
3061
+ goDocStart: function(cm) {cm.extendSelection({line: 0, ch: 0});},
3062
+ goDocEnd: function(cm) {cm.extendSelection({line: cm.lineCount() - 1});},
3063
+ goLineStart: function(cm) {
3064
+ cm.extendSelection(lineStart(cm, cm.getCursor().line));
3065
+ },
3066
  goLineStartSmart: function(cm) {
3067
+ var cur = cm.getCursor(), start = lineStart(cm, cur.line);
3068
+ var line = cm.getLineHandle(start.line);
3069
+ var order = getOrder(line);
3070
+ if (!order || order[0].level == 0) {
3071
+ var firstNonWS = Math.max(0, line.text.search(/\S/));
3072
+ var inWS = cur.line == start.line && cur.ch <= firstNonWS && cur.ch;
3073
+ cm.extendSelection({line: start.line, ch: inWS ? 0 : firstNonWS});
3074
+ } else cm.extendSelection(start);
3075
+ },
3076
+ goLineEnd: function(cm) {
3077
+ cm.extendSelection(lineEnd(cm, cm.getCursor().line));
3078
  },
 
3079
  goLineUp: function(cm) {cm.moveV(-1, "line");},
3080
  goLineDown: function(cm) {cm.moveV(1, "line");},
3081
  goPageUp: function(cm) {cm.moveV(-1, "page");},
3086
  goColumnRight: function(cm) {cm.moveH(1, "column");},
3087
  goWordLeft: function(cm) {cm.moveH(-1, "word");},
3088
  goWordRight: function(cm) {cm.moveH(1, "word");},
3089
+ delCharBefore: function(cm) {cm.deleteH(-1, "char");},
3090
+ delCharAfter: function(cm) {cm.deleteH(1, "char");},
3091
+ delWordBefore: function(cm) {cm.deleteH(-1, "word");},
3092
+ delWordAfter: function(cm) {cm.deleteH(1, "word");},
3093
  indentAuto: function(cm) {cm.indentSelection("smart");},
3094
  indentMore: function(cm) {cm.indentSelection("add");},
3095
  indentLess: function(cm) {cm.indentSelection("subtract");},
3096
+ insertTab: function(cm) {cm.replaceSelection("\t", "end", "input");},
3097
+ defaultTab: function(cm) {
3098
+ if (cm.somethingSelected()) cm.indentSelection("add");
3099
+ else cm.replaceSelection("\t", "end", "input");
3100
+ },
3101
  transposeChars: function(cm) {
3102
  var cur = cm.getCursor(), line = cm.getLine(cur.line);
3103
  if (cur.ch > 0 && cur.ch < line.length - 1)
3105
  {line: cur.line, ch: cur.ch - 1}, {line: cur.line, ch: cur.ch + 1});
3106
  },
3107
  newlineAndIndent: function(cm) {
3108
+ operation(cm, function() {
3109
+ cm.replaceSelection("\n", "end", "input");
3110
+ cm.indentLine(cm.getCursor().line, null, true);
3111
+ })();
3112
  },
3113
  toggleOverwrite: function(cm) {cm.toggleOverwrite();}
3114
  };
3115
 
3116
+ // STANDARD KEYMAPS
3117
+
3118
  var keyMap = CodeMirror.keyMap = {};
3119
  keyMap.basic = {
3120
  "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
3121
  "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
3122
+ "Delete": "delCharAfter", "Backspace": "delCharBefore", "Tab": "defaultTab", "Shift-Tab": "indentAuto",
3123
  "Enter": "newlineAndIndent", "Insert": "toggleOverwrite"
3124
  };
3125
  // Note that the save and find-related commands aren't defined by
3128
  "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
3129
  "Ctrl-Home": "goDocStart", "Alt-Up": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Down": "goDocEnd",
3130
  "Ctrl-Left": "goWordLeft", "Ctrl-Right": "goWordRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
3131
+ "Ctrl-Backspace": "delWordBefore", "Ctrl-Delete": "delWordAfter", "Ctrl-S": "save", "Ctrl-F": "find",
3132
  "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
3133
  "Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
3134
  fallthrough: "basic"
3136
  keyMap.macDefault = {
3137
  "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
3138
  "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goWordLeft",
3139
+ "Alt-Right": "goWordRight", "Cmd-Left": "goLineStart", "Cmd-Right": "goLineEnd", "Alt-Backspace": "delWordBefore",
3140
+ "Ctrl-Alt-Backspace": "delWordAfter", "Alt-Delete": "delWordAfter", "Cmd-S": "save", "Cmd-F": "find",
3141
  "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
3142
  "Cmd-[": "indentLess", "Cmd-]": "indentMore",
3143
  fallthrough: ["basic", "emacsy"]
3146
  keyMap.emacsy = {
3147
  "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
3148
  "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
3149
+ "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore",
3150
+ "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars"
3151
  };
3152
 
3153
+ // KEYMAP DISPATCH
3154
+
3155
  function getKeyMap(val) {
3156
  if (typeof val == "string") return keyMap[val];
3157
  else return val;
3158
  }
3159
+
3160
+ function lookupKey(name, maps, handle, stop) {
3161
  function lookup(map) {
3162
  map = getKeyMap(map);
3163
  var found = map[name];
3164
+ if (found === false) {
3165
+ if (stop) stop();
3166
+ return true;
3167
+ }
3168
  if (found != null && handle(found)) return true;
3169
+ if (map.nofallthrough) {
3170
+ if (stop) stop();
3171
+ return true;
3172
+ }
3173
  var fallthrough = map.fallthrough;
3174
  if (fallthrough == null) return false;
3175
  if (Object.prototype.toString.call(fallthrough) != "[object Array]")
3179
  }
3180
  return false;
3181
  }
3182
+
3183
+ for (var i = 0; i < maps.length; ++i)
3184
+ if (lookup(maps[i])) return true;
3185
  }
3186
  function isModifierKey(event) {
3187
  var name = keyNames[e_prop(event, "keyCode")];
3188
  return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod";
3189
  }
3190
+ CodeMirror.isModifierKey = isModifierKey;
3191
+
3192
+ // FROMTEXTAREA
3193
 
3194
  CodeMirror.fromTextArea = function(textarea, options) {
3195
  if (!options) options = {};
3196
  options.value = textarea.value;
3197
  if (!options.tabindex && textarea.tabindex)
3198
  options.tabindex = textarea.tabindex;
3199
+ // Set autofocus to true if this textarea is focused, or if it has
3200
+ // autofocus and no other element is focused.
3201
+ if (options.autofocus == null) {
3202
+ var hasFocus = document.body;
3203
+ // doc.activeElement occasionally throws on IE
3204
+ try { hasFocus = document.activeElement; } catch(e) {}
3205
+ options.autofocus = hasFocus == textarea ||
3206
+ textarea.getAttribute("autofocus") != null && hasFocus == document.body;
3207
+ }
3208
 
3209
+ function save() {textarea.value = cm.getValue();}
3210
  if (textarea.form) {
3211
  // Deplorable hack to make the submit method do the right thing.
3212
+ on(textarea.form, "submit", save);
3213
+ var form = textarea.form, realSubmit = form.submit;
3214
+ try {
3215
+ form.submit = function wrappedSubmit() {
3216
  save();
3217
+ form.submit = realSubmit;
3218
+ form.submit();
3219
+ form.submit = wrappedSubmit;
3220
+ };
3221
+ } catch(e) {}
 
3222
  }
3223
 
3224
  textarea.style.display = "none";
3225
+ var cm = CodeMirror(function(node) {
3226
  textarea.parentNode.insertBefore(node, textarea.nextSibling);
3227
  }, options);
3228
+ cm.save = save;
3229
+ cm.getTextArea = function() { return textarea; };
3230
+ cm.toTextArea = function() {
3231
  save();
3232
+ textarea.parentNode.removeChild(cm.getWrapperElement());
3233
  textarea.style.display = "";
3234
  if (textarea.form) {
3235
+ off(textarea.form, "submit", save);
3236
  if (typeof textarea.form.submit == "function")
3237
  textarea.form.submit = realSubmit;
3238
  }
3239
  };
3240
+ return cm;
3241
  };
3242
 
3243
+ // STRING STREAM
3244
+
3245
+ // Fed to the mode parsers, provides helper functions to make
3246
+ // parsers more succinct.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3247
 
3248
  // The character stream used by a mode's parser.
3249
  function StringStream(string, tabSize) {
3251
  this.string = string;
3252
  this.tabSize = tabSize || 8;
3253
  }
3254
+
3255
  StringStream.prototype = {
3256
  eol: function() {return this.pos >= this.string.length;},
3257
  sol: function() {return this.pos == 0;},
3258
+ peek: function() {return this.string.charAt(this.pos) || undefined;},
3259
  next: function() {
3260
  if (this.pos < this.string.length)
3261
  return this.string.charAt(this.pos++);
3286
  indentation: function() {return countColumn(this.string, null, this.tabSize);},
3287
  match: function(pattern, consume, caseInsensitive) {
3288
  if (typeof pattern == "string") {
3289
+ var cased = function(str) {return caseInsensitive ? str.toLowerCase() : str;};
3290
  if (cased(this.string).indexOf(cased(pattern), this.pos) == this.pos) {
3291
  if (consume !== false) this.pos += pattern.length;
3292
  return true;
3293
  }
3294
+ } else {
 
3295
  var match = this.string.slice(this.pos).match(pattern);
3296
+ if (match && match.index > 0) return null;
3297
  if (match && consume !== false) this.pos += match[0].length;
3298
  return match;
3299
  }
3302
  };
3303
  CodeMirror.StringStream = StringStream;
3304
 
3305
+ // TEXTMARKERS
3306
+
3307
+ function TextMarker(cm, type) {
3308
+ this.lines = [];
3309
+ this.type = type;
3310
+ this.cm = cm;
3311
  }
3312
+ CodeMirror.TextMarker = TextMarker;
3313
+
3314
+ TextMarker.prototype.clear = function() {
3315
+ if (this.explicitlyCleared) return;
3316
+ startOperation(this.cm);
3317
+ var view = this.cm.view, min = null, max = null;
3318
+ for (var i = 0; i < this.lines.length; ++i) {
3319
+ var line = this.lines[i];
3320
+ var span = getMarkedSpanFor(line.markedSpans, this);
3321
+ if (span.to != null) max = lineNo(line);
3322
+ line.markedSpans = removeMarkedSpan(line.markedSpans, span);
3323
+ if (span.from != null)
3324
+ min = lineNo(line);
3325
+ else if (this.collapsed && !lineIsHidden(line))
3326
+ updateLineHeight(line, textHeight(this.cm.display));
3327
+ }
3328
+ if (this.collapsed && !this.cm.options.lineWrapping) for (var i = 0; i < this.lines.length; ++i) {
3329
+ var visual = visualLine(view.doc, this.lines[i]), len = lineLength(view.doc, visual);
3330
+ if (len > view.maxLineLength) {
3331
+ view.maxLine = visual;
3332
+ view.maxLineLength = len;
3333
+ view.maxLineChanged = true;
3334
+ }
3335
+ }
3336
+
3337
+ if (min != null) regChange(this.cm, min, max + 1);
3338
+ this.lines.length = 0;
3339
+ this.explicitlyCleared = true;
3340
+ if (this.collapsed && this.cm.view.cantEdit) {
3341
+ this.cm.view.cantEdit = false;
3342
+ reCheckSelection(this.cm);
3343
+ }
3344
+ endOperation(this.cm);
3345
+ signalLater(this.cm, this, "clear");
3346
+ };
3347
+
3348
+ TextMarker.prototype.find = function() {
3349
+ var from, to;
3350
+ for (var i = 0; i < this.lines.length; ++i) {
3351
+ var line = this.lines[i];
3352
+ var span = getMarkedSpanFor(line.markedSpans, this);
3353
+ if (span.from != null || span.to != null) {
3354
+ var found = lineNo(line);
3355
+ if (span.from != null) from = {line: found, ch: span.from};
3356
+ if (span.to != null) to = {line: found, ch: span.to};
3357
+ }
3358
+ }
3359
+ if (this.type == "bookmark") return from;
3360
+ return from && {from: from, to: to};
3361
+ };
3362
+
3363
+ TextMarker.prototype.getOptions = function(copyWidget) {
3364
+ var repl = this.replacedWith;
3365
+ return {className: this.className,
3366
+ inclusiveLeft: this.inclusiveLeft, inclusiveRight: this.inclusiveRight,
3367
+ atomic: this.atomic,
3368
+ collapsed: this.collapsed,
3369
+ clearOnEnter: this.clearOnEnter,
3370
+ replacedWith: copyWidget ? repl && repl.cloneNode(true) : repl,
3371
+ readOnly: this.readOnly,
3372
+ startStyle: this.startStyle, endStyle: this.endStyle};
3373
+ };
3374
+
3375
+ function markText(cm, from, to, options, type) {
3376
+ var doc = cm.view.doc;
3377
+ var marker = new TextMarker(cm, type);
3378
+ if (type == "range" && !posLess(from, to)) return marker;
3379
+ if (options) for (var opt in options) if (options.hasOwnProperty(opt))
3380
+ marker[opt] = options[opt];
3381
+ if (marker.replacedWith) {
3382
+ marker.collapsed = true;
3383
+ marker.replacedWith = elt("span", [marker.replacedWith], "CodeMirror-widget");
3384
+ }
3385
+ if (marker.collapsed) sawCollapsedSpans = true;
3386
+
3387
+ var curLine = from.line, size = 0, collapsedAtStart, collapsedAtEnd;
3388
+ doc.iter(curLine, to.line + 1, function(line) {
3389
+ if (marker.collapsed && !cm.options.lineWrapping && visualLine(doc, line) == cm.view.maxLine)
3390
+ cm.curOp.updateMaxLine = true;
3391
+ var span = {from: null, to: null, marker: marker};
3392
+ size += line.text.length;
3393
+ if (curLine == from.line) {span.from = from.ch; size -= from.ch;}
3394
+ if (curLine == to.line) {span.to = to.ch; size -= line.text.length - to.ch;}
3395
+ if (marker.collapsed) {
3396
+ if (curLine == to.line) collapsedAtEnd = collapsedSpanAt(line, to.ch);
3397
+ if (curLine == from.line) collapsedAtStart = collapsedSpanAt(line, from.ch);
3398
+ else updateLineHeight(line, 0);
3399
+ }
3400
+ addMarkedSpan(line, span);
3401
+ ++curLine;
3402
+ });
3403
+ if (marker.collapsed) doc.iter(from.line, to.line + 1, function(line) {
3404
+ if (lineIsHidden(line)) updateLineHeight(line, 0);
3405
+ });
3406
+
3407
+ if (marker.readOnly) {
3408
+ sawReadOnlySpans = true;
3409
+ if (cm.view.history.done.length || cm.view.history.undone.length)
3410
+ cm.clearHistory();
3411
+ }
3412
+ if (marker.collapsed) {
3413
+ if (collapsedAtStart != collapsedAtEnd)
3414
+ throw new Error("Inserting collapsed marker overlapping an existing one");
3415
+ marker.size = size;
3416
+ marker.atomic = true;
3417
+ }
3418
+ if (marker.className || marker.startStyle || marker.endStyle || marker.collapsed)
3419
+ regChange(cm, from.line, to.line + 1);
3420
+ if (marker.atomic) reCheckSelection(cm);
3421
+ return marker;
3422
+ }
3423
+
3424
+ // TEXTMARKER SPANS
3425
+
3426
+ function getMarkedSpanFor(spans, marker) {
3427
+ if (spans) for (var i = 0; i < spans.length; ++i) {
3428
+ var span = spans[i];
3429
+ if (span.marker == marker) return span;
3430
+ }
3431
+ }
3432
+ function removeMarkedSpan(spans, span) {
3433
+ for (var r, i = 0; i < spans.length; ++i)
3434
+ if (spans[i] != span) (r || (r = [])).push(spans[i]);
3435
+ return r;
3436
+ }
3437
+ function addMarkedSpan(line, span) {
3438
+ line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span];
3439
+ span.marker.lines.push(line);
3440
+ }
3441
+
3442
+ function markedSpansBefore(old, startCh) {
3443
+ if (old) for (var i = 0, nw; i < old.length; ++i) {
3444
+ var span = old[i], marker = span.marker;
3445
+ var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh);
3446
+ if (startsBefore || marker.type == "bookmark" && span.from == startCh) {
3447
+ var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh);
3448
+ (nw || (nw = [])).push({from: span.from,
3449
+ to: endsAfter ? null : span.to,
3450
+ marker: marker});
3451
+ }
3452
+ }
3453
+ return nw;
3454
+ }
3455
+
3456
+ function markedSpansAfter(old, startCh, endCh) {
3457
+ if (old) for (var i = 0, nw; i < old.length; ++i) {
3458
+ var span = old[i], marker = span.marker;
3459
+ var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh);
3460
+ if (endsAfter || marker.type == "bookmark" && span.from == endCh && span.from != startCh) {
3461
+ var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh);
3462
+ (nw || (nw = [])).push({from: startsBefore ? null : span.from - endCh,
3463
+ to: span.to == null ? null : span.to - endCh,
3464
+ marker: marker});
3465
+ }
3466
+ }
3467
+ return nw;
3468
+ }
3469
+
3470
+ function updateMarkedSpans(oldFirst, oldLast, startCh, endCh, newText) {
3471
+ if (!oldFirst && !oldLast) return newText;
3472
+ // Get the spans that 'stick out' on both sides
3473
+ var first = markedSpansBefore(oldFirst, startCh);
3474
+ var last = markedSpansAfter(oldLast, startCh, endCh);
3475
+
3476
+ // Next, merge those two ends
3477
+ var sameLine = newText.length == 1, offset = lst(newText).length + (sameLine ? startCh : 0);
3478
+ if (first) {
3479
+ // Fix up .to properties of first
3480
+ for (var i = 0; i < first.length; ++i) {
3481
+ var span = first[i];
3482
+ if (span.to == null) {
3483
+ var found = getMarkedSpanFor(last, span.marker);
3484
+ if (!found) span.to = startCh;
3485
+ else if (sameLine) span.to = found.to == null ? null : found.to + offset;
3486
+ }
3487
+ }
3488
+ }
3489
+ if (last) {
3490
+ // Fix up .from in last (or move them into first in case of sameLine)
3491
+ for (var i = 0; i < last.length; ++i) {
3492
+ var span = last[i];
3493
+ if (span.to != null) span.to += offset;
3494
+ if (span.from == null) {
3495
+ var found = getMarkedSpanFor(first, span.marker);
3496
+ if (!found) {
3497
+ span.from = offset;
3498
+ if (sameLine) (first || (first = [])).push(span);
3499
+ }
3500
+ } else {
3501
+ span.from += offset;
3502
+ if (sameLine) (first || (first = [])).push(span);
3503
+ }
3504
+ }
3505
+ }
3506
+
3507
+ var newMarkers = [newHL(newText[0], first)];
3508
+ if (!sameLine) {
3509
+ // Fill gap with whole-line-spans
3510
+ var gap = newText.length - 2, gapMarkers;
3511
+ if (gap > 0 && first)
3512
+ for (var i = 0; i < first.length; ++i)
3513
+ if (first[i].to == null)
3514
+ (gapMarkers || (gapMarkers = [])).push({from: null, to: null, marker: first[i].marker});
3515
+ for (var i = 0; i < gap; ++i)
3516
+ newMarkers.push(newHL(newText[i+1], gapMarkers));
3517
+ newMarkers.push(newHL(lst(newText), last));
3518
+ }
3519
+ return newMarkers;
3520
+ }
3521
+
3522
+ function removeReadOnlyRanges(doc, from, to) {
3523
+ var markers = null;
3524
+ doc.iter(from.line, to.line + 1, function(line) {
3525
+ if (line.markedSpans) for (var i = 0; i < line.markedSpans.length; ++i) {
3526
+ var mark = line.markedSpans[i].marker;
3527
+ if (mark.readOnly && (!markers || indexOf(markers, mark) == -1))
3528
+ (markers || (markers = [])).push(mark);
3529
+ }
3530
+ });
3531
+ if (!markers) return null;
3532
+ var parts = [{from: from, to: to}];
3533
+ for (var i = 0; i < markers.length; ++i) {
3534
+ var m = markers[i].find();
3535
+ for (var j = 0; j < parts.length; ++j) {
3536
+ var p = parts[j];
3537
+ if (!posLess(m.from, p.to) || posLess(m.to, p.from)) continue;
3538
+ var newParts = [j, 1];
3539
+ if (posLess(p.from, m.from)) newParts.push({from: p.from, to: m.from});
3540
+ if (posLess(m.to, p.to)) newParts.push({from: m.to, to: p.to});
3541
+ parts.splice.apply(parts, newParts);
3542
+ j += newParts.length - 1;
3543
+ }
3544
+ }
3545
+ return parts;
3546
+ }
3547
+
3548
+ function collapsedSpanAt(line, ch) {
3549
+ var sps = sawCollapsedSpans && line.markedSpans, found;
3550
+ if (sps) for (var sp, i = 0; i < sps.length; ++i) {
3551
+ sp = sps[i];
3552
+ if (!sp.marker.collapsed) continue;
3553
+ if ((sp.from == null || sp.from < ch) &&
3554
+ (sp.to == null || sp.to > ch) &&
3555
+ (!found || found.width < sp.marker.width))
3556
+ found = sp.marker;
3557
+ }
3558
+ return found;
3559
+ }
3560
+ function collapsedSpanAtStart(line) { return collapsedSpanAt(line, -1); }
3561
+ function collapsedSpanAtEnd(line) { return collapsedSpanAt(line, line.text.length + 1); }
3562
+
3563
+ function visualLine(doc, line) {
3564
+ var merged;
3565
+ while (merged = collapsedSpanAtStart(line))
3566
+ line = getLine(doc, merged.find().from.line);
3567
+ return line;
3568
+ }
3569
+
3570
+ function lineIsHidden(line) {
3571
+ var sps = sawCollapsedSpans && line.markedSpans;
3572
+ if (sps) for (var sp, i = 0; i < sps.length; ++i) {
3573
+ sp = sps[i];
3574
+ if (!sp.marker.collapsed) continue;
3575
+ if (sp.from == null) return true;
3576
+ if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(line, sp))
3577
+ return true;
3578
+ }
3579
+ }
3580
+ function lineIsHiddenInner(line, span) {
3581
+ if (span.to == null) {
3582
+ var end = span.marker.find().to, endLine = getLine(lineDoc(line), end.line);
3583
+ return lineIsHiddenInner(endLine, getMarkedSpanFor(endLine.markedSpans, span.marker));
3584
+ }
3585
+ if (span.marker.inclusiveRight && span.to == line.text.length)
3586
+ return true;
3587
+ for (var sp, i = 0; i < line.markedSpans.length; ++i) {
3588
+ sp = line.markedSpans[i];
3589
+ if (sp.marker.collapsed && sp.from == span.to &&
3590
+ (sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
3591
+ lineIsHiddenInner(line, sp)) return true;
3592
+ }
3593
+ }
3594
+
3595
+ // hl stands for history-line, a data structure that can be either a
3596
+ // string (line without markers) or a {text, markedSpans} object.
3597
+ function hlText(val) { return typeof val == "string" ? val : val.text; }
3598
+ function hlSpans(val) {
3599
+ if (typeof val == "string") return null;
3600
+ var spans = val.markedSpans, out = null;
3601
+ for (var i = 0; i < spans.length; ++i) {
3602
+ if (spans[i].marker.explicitlyCleared) { if (!out) out = spans.slice(0, i); }
3603
+ else if (out) out.push(spans[i]);
3604
+ }
3605
+ return !out ? spans : out.length ? out : null;
3606
+ }
3607
+ function newHL(text, spans) { return spans ? {text: text, markedSpans: spans} : text; }
3608
+
3609
+ function detachMarkedSpans(line) {
3610
+ var spans = line.markedSpans;
3611
+ if (!spans) return;
3612
+ for (var i = 0; i < spans.length; ++i) {
3613
+ var lines = spans[i].marker.lines;
3614
+ var ix = indexOf(lines, line);
3615
+ lines.splice(ix, 1);
3616
+ }
3617
+ line.markedSpans = null;
3618
+ }
3619
+
3620
+ function attachMarkedSpans(line, spans) {
3621
+ if (!spans) return;
3622
+ for (var i = 0; i < spans.length; ++i)
3623
+ spans[i].marker.lines.push(line);
3624
+ line.markedSpans = spans;
3625
+ }
3626
+
3627
+ // LINE WIDGETS
3628
+
3629
+ var LineWidget = CodeMirror.LineWidget = function(cm, node, options) {
3630
+ for (var opt in options) if (options.hasOwnProperty(opt))
3631
+ this[opt] = options[opt];
3632
+ this.cm = cm;
3633
+ this.node = node;
3634
  };
3635
+ function widgetOperation(f) {
3636
+ return function() {
3637
+ startOperation(this.cm);
3638
+ try {var result = f.apply(this, arguments);}
3639
+ finally {endOperation(this.cm);}
3640
+ return result;
3641
+ };
3642
+ }
3643
+ LineWidget.prototype.clear = widgetOperation(function() {
3644
+ var ws = this.line.widgets, no = lineNo(this.line);
3645
+ if (no == null || !ws) return;
3646
+ for (var i = 0; i < ws.length; ++i) if (ws[i] == this) ws.splice(i--, 1);
3647
+ updateLineHeight(this.line, Math.max(0, this.line.height - widgetHeight(this)));
3648
+ regChange(this.cm, no, no + 1);
3649
+ });
3650
+ LineWidget.prototype.changed = widgetOperation(function() {
3651
+ var oldH = this.height;
3652
+ this.height = null;
3653
+ var diff = widgetHeight(this) - oldH;
3654
+ if (!diff) return;
3655
+ updateLineHeight(this.line, this.line.height + diff);
3656
+ var no = lineNo(this.line);
3657
+ regChange(this.cm, no, no + 1);
3658
+ });
3659
 
3660
+ function widgetHeight(widget) {
3661
+ if (widget.height != null) return widget.height;
3662
+ if (!widget.node.parentNode || widget.node.parentNode.nodeType != 1)
3663
+ removeChildrenAndAdd(widget.cm.display.measure, elt("div", [widget.node], null, "position: relative"));
3664
+ return widget.height = widget.node.offsetHeight;
3665
  }
3666
+
3667
+ function addLineWidget(cm, handle, node, options) {
3668
+ var widget = new LineWidget(cm, node, options);
3669
+ if (widget.noHScroll) cm.display.alignWidgets = true;
3670
+ changeLine(cm, handle, function(line) {
3671
+ (line.widgets || (line.widgets = [])).push(widget);
3672
+ widget.line = line;
3673
+ if (!lineIsHidden(line) || widget.showIfHidden) {
3674
+ var aboveVisible = heightAtLine(cm, line) < cm.display.scroller.scrollTop;
3675
+ updateLineHeight(line, line.height + widgetHeight(widget));
3676
+ if (aboveVisible)
3677
+ setTimeout(function() {cm.display.scroller.scrollTop += widget.height;});
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3678
  }
3679
+ return true;
3680
+ });
3681
+ return widget;
3682
+ }
3683
+
3684
+ // LINE DATA STRUCTURE
3685
 
3686
  // Line objects. These hold state related to a line, including
3687
  // highlighting info (the styles array).
3688
+ function makeLine(text, markedSpans, height) {
3689
+ var line = {text: text, height: height};
3690
+ attachMarkedSpans(line, markedSpans);
3691
+ if (lineIsHidden(line)) line.height = 0;
3692
+ return line;
3693
+ }
3694
+
3695
+ function updateLine(cm, line, text, markedSpans) {
3696
+ line.text = text;
3697
+ if (line.stateAfter) line.stateAfter = null;
3698
+ if (line.styles) line.styles = null;
3699
+ if (line.order != null) line.order = null;
3700
+ detachMarkedSpans(line);
3701
+ attachMarkedSpans(line, markedSpans);
3702
+ if (lineIsHidden(line)) line.height = 0;
3703
+ else if (!line.height) line.height = textHeight(cm.display);
3704
+ signalLater(cm, line, "change");
3705
+ }
3706
+
3707
+ function cleanUpLine(line) {
3708
+ line.parent = null;
3709
+ detachMarkedSpans(line);
3710
+ }
3711
+
3712
+ // Run the given mode's parser over a line, update the styles
3713
+ // array, which contains alternating fragments of text and CSS
3714
+ // classes.
3715
+ function runMode(cm, text, mode, state, f) {
3716
+ var flattenSpans = cm.options.flattenSpans;
3717
+ var curText = "", curStyle = null;
3718
+ var stream = new StringStream(text, cm.options.tabSize);
3719
+ if (text == "" && mode.blankLine) mode.blankLine(state);
3720
+ while (!stream.eol()) {
3721
+ var style = mode.token(stream, state);
3722
+ if (stream.pos > 5000) {
3723
+ flattenSpans = false;
3724
+ // Webkit seems to refuse to render text nodes longer than 57444 characters
3725
+ stream.pos = Math.min(text.length, stream.start + 50000);
3726
+ style = null;
3727
  }
3728
+ var substr = stream.current();
3729
+ stream.start = stream.pos;
3730
+ if (!flattenSpans || curStyle != style) {
3731
+ if (curText) f(curText, curStyle);
3732
+ curText = substr; curStyle = style;
3733
+ } else curText = curText + substr;
3734
  }
3735
+ if (curText) f(curText, curStyle);
3736
+ }
3737
+
3738
+ function highlightLine(cm, line, state) {
3739
+ // A styles array always starts with a number identifying the
3740
+ // mode/overlays that it is based on (for easy invalidation).
3741
+ var st = [cm.view.modeGen];
3742
+ // Compute the base array of styles
3743
+ runMode(cm, line.text, cm.view.mode, state, function(txt, style) {st.push(txt, style);});
3744
+
3745
+ // Run overlays, adjust style array.
3746
+ for (var o = 0; o < cm.view.overlays.length; ++o) {
3747
+ var overlay = cm.view.overlays[o], i = 1;
3748
+ runMode(cm, line.text, overlay.mode, true, function(txt, style) {
3749
+ var start = i, len = txt.length;
3750
+ // Ensure there's a token end at the current position, and that i points at it
3751
+ while (len) {
3752
+ var cur = st[i], len_ = cur.length;
3753
+ if (len_ <= len) {
3754
+ len -= len_;
3755
+ } else {
3756
+ st.splice(i, 1, cur.slice(0, len), st[i+1], cur.slice(len));
3757
+ len = 0;
 
 
 
 
 
 
 
 
 
 
 
3758
  }
3759
+ i += 2;
3760
  }
3761
+ if (!style) return;
3762
+ if (overlay.opaque) {
3763
+ st.splice(start, i - start, txt, style);
3764
+ i = start + 2;
3765
+ } else {
3766
+ for (; start < i; start += 2) {
3767
+ var cur = st[start+1];
3768
+ st[start+1] = cur ? cur + " " + style : style;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3769
  }
 
 
 
 
3770
  }
3771
+ });
3772
+ }
3773
+
3774
+ return st;
3775
+ }
3776
+
3777
+ function getLineStyles(cm, line) {
3778
+ if (!line.styles || line.styles[0] != cm.view.modeGen)
3779
+ line.styles = highlightLine(cm, line, line.stateAfter = getStateBefore(cm, lineNo(line)));
3780
+ return line.styles;
3781
+ }
3782
+
3783
+ // Lightweight form of highlight -- proceed over this line and
3784
+ // update state, but don't save a style array.
3785
+ function processLine(cm, line, state) {
3786
+ var mode = cm.view.mode;
3787
+ var stream = new StringStream(line.text, cm.options.tabSize);
3788
+ if (line.text == "" && mode.blankLine) mode.blankLine(state);
3789
+ while (!stream.eol() && stream.pos <= 5000) {
3790
+ mode.token(stream, state);
3791
+ stream.start = stream.pos;
3792
+ }
3793
+ }
3794
+
3795
+ var styleToClassCache = {};
3796
+ function styleToClass(style) {
3797
+ if (!style) return null;
3798
+ return styleToClassCache[style] ||
3799
+ (styleToClassCache[style] = "cm-" + style.replace(/ +/g, " cm-"));
3800
+ }
3801
+
3802
+ function lineContent(cm, realLine, measure) {
3803
+ var merged, line = realLine, lineBefore, sawBefore, simple = true;
3804
+ while (merged = collapsedSpanAtStart(line)) {
3805
+ simple = false;
3806
+ line = getLine(cm.view.doc, merged.find().from.line);
3807
+ if (!lineBefore) lineBefore = line;
3808
+ }
3809
+
3810
+ var builder = {pre: elt("pre"), col: 0, pos: 0, display: !measure,
3811
+ measure: null, addedOne: false, cm: cm};
3812
+ if (line.textClass) builder.pre.className = line.textClass;
3813
+
3814
+ do {
3815
+ builder.measure = line == realLine && measure;
3816
+ builder.pos = 0;
3817
+ builder.addToken = builder.measure ? buildTokenMeasure : buildToken;
3818
+ if (measure && sawBefore && line != realLine && !builder.addedOne) {
3819
+ measure[0] = builder.pre.appendChild(zeroWidthElement(cm.display.measure));
3820
+ builder.addedOne = true;
3821
  }
3822
+ var next = insertLineContent(line, builder, getLineStyles(cm, line));
3823
+ sawBefore = line == lineBefore;
3824
+ if (next) {
3825
+ line = getLine(cm.view.doc, next.to.line);
3826
+ simple = false;
 
 
 
 
 
 
3827
  }
3828
+ } while (next);
3829
+
3830
+ if (measure && !builder.addedOne)
3831
+ measure[0] = builder.pre.appendChild(simple ? elt("span", "\u00a0") : zeroWidthElement(cm.display.measure));
3832
+ if (!builder.pre.firstChild && !lineIsHidden(realLine))
3833
+ builder.pre.appendChild(document.createTextNode("\u00a0"));
3834
+
3835
+ return builder.pre;
3836
+ }
3837
+
3838
+ var tokenSpecialChars = /[\t\u0000-\u0019\u200b\u2028\u2029\uFEFF]/g;
3839
+ function buildToken(builder, text, style, startStyle, endStyle) {
3840
+ if (!text) return;
3841
+ if (!tokenSpecialChars.test(text)) {
3842
+ builder.col += text.length;
3843
+ var content = document.createTextNode(text);
3844
+ } else {
3845
+ var content = document.createDocumentFragment(), pos = 0;
3846
+ while (true) {
3847
+ tokenSpecialChars.lastIndex = pos;
3848
+ var m = tokenSpecialChars.exec(text);
3849
+ var skipped = m ? m.index - pos : text.length - pos;
3850
+ if (skipped) {
3851
+ content.appendChild(document.createTextNode(text.slice(pos, pos + skipped)));
3852
+ builder.col += skipped;
 
 
 
 
 
3853
  }
3854
+ if (!m) break;
3855
+ pos += skipped + 1;
3856
+ if (m[0] == "\t") {
3857
+ var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize;
3858
+ content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab"));
3859
+ builder.col += tabWidth;
3860
+ } else {
3861
+ var token = elt("span", "\u2022", "cm-invalidchar");
3862
+ token.title = "\\u" + m[0].charCodeAt(0).toString(16);
3863
+ content.appendChild(token);
3864
+ builder.col += 1;
3865
  }
3866
  }
3867
+ }
3868
+ if (style || startStyle || endStyle || builder.measure) {
3869
+ var fullStyle = style || "";
3870
+ if (startStyle) fullStyle += startStyle;
3871
+ if (endStyle) fullStyle += endStyle;
3872
+ return builder.pre.appendChild(elt("span", [content], fullStyle));
3873
+ }
3874
+ builder.pre.appendChild(content);
3875
+ }
3876
+
3877
+ function buildTokenMeasure(builder, text, style, startStyle, endStyle) {
3878
+ for (var i = 0; i < text.length; ++i) {
3879
+ if (i && i < text.length &&
3880
+ builder.cm.options.lineWrapping &&
3881
+ spanAffectsWrapping.test(text.slice(i - 1, i + 1)))
3882
+ builder.pre.appendChild(elt("wbr"));
3883
+ builder.measure[builder.pos++] =
3884
+ buildToken(builder, text.charAt(i), style,
3885
+ i == 0 && startStyle, i == text.length - 1 && endStyle);
3886
+ }
3887
+ if (text.length) builder.addedOne = true;
3888
+ }
3889
+
3890
+ function buildCollapsedSpan(builder, size, widget) {
3891
+ if (widget) {
3892
+ if (!builder.display) widget = widget.cloneNode(true);
3893
+ builder.pre.appendChild(widget);
3894
+ if (builder.measure && size) {
3895
+ builder.measure[builder.pos] = widget;
3896
+ builder.addedOne = true;
3897
  }
3898
+ }
3899
+ builder.pos += size;
3900
+ }
3901
+
3902
+ // Outputs a number of spans to make up a line, taking highlighting
3903
+ // and marked text into account.
3904
+ function insertLineContent(line, builder, styles) {
3905
+ var spans = line.markedSpans;
3906
+ if (!spans) {
3907
+ for (var i = 1; i < styles.length; i+=2)
3908
+ builder.addToken(builder, styles[i], styleToClass(styles[i+1]));
3909
+ return;
3910
+ }
3911
+
3912
+ var allText = line.text, len = allText.length;
3913
+ var pos = 0, i = 1, text = "", style;
3914
+ var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, collapsed;
3915
+ for (;;) {
3916
+ if (nextChange == pos) { // Update current marker set
3917
+ spanStyle = spanEndStyle = spanStartStyle = "";
3918
+ collapsed = null; nextChange = Infinity;
3919
+ var foundBookmark = null;
3920
+ for (var j = 0; j < spans.length; ++j) {
3921
+ var sp = spans[j], m = sp.marker;
3922
+ if (sp.from <= pos && (sp.to == null || sp.to > pos)) {
3923
+ if (sp.to != null && nextChange > sp.to) { nextChange = sp.to; spanEndStyle = ""; }
3924
+ if (m.className) spanStyle += " " + m.className;
3925
+ if (m.startStyle && sp.from == pos) spanStartStyle += " " + m.startStyle;
3926
+ if (m.endStyle && sp.to == nextChange) spanEndStyle += " " + m.endStyle;
3927
+ if (m.collapsed && (!collapsed || collapsed.marker.width < m.width))
3928
+ collapsed = sp;
3929
+ } else if (sp.from > pos && nextChange > sp.from) {
3930
+ nextChange = sp.from;
 
3931
  }
3932
+ if (m.type == "bookmark" && sp.from == pos && m.replacedWith)
3933
+ foundBookmark = m.replacedWith;
3934
  }
3935
+ if (collapsed && (collapsed.from || 0) == pos) {
3936
+ buildCollapsedSpan(builder, (collapsed.to == null ? len : collapsed.to) - pos,
3937
+ collapsed.from != null && collapsed.marker.replacedWith);
3938
+ if (collapsed.to == null) return collapsed.marker.find();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3939
  }
3940
+ if (foundBookmark && !collapsed) buildCollapsedSpan(builder, 0, foundBookmark);
3941
+ }
3942
+ if (pos >= len) break;
3943
+
3944
+ var upto = Math.min(len, nextChange);
3945
+ while (true) {
3946
+ if (text) {
3947
+ var end = pos + text.length;
3948
+ if (!collapsed) {
3949
+ var tokenText = end > upto ? text.slice(0, upto - pos) : text;
3950
+ builder.addToken(builder, tokenText, style + spanStyle,
3951
+ spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "");
 
 
 
3952
  }
3953
+ if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;}
3954
+ pos = end;
3955
+ spanStartStyle = "";
3956
  }
3957
+ text = styles[i++]; style = styleToClass(styles[i++]);
3958
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3959
  }
3960
  }
3961
 
3962
+ // DOCUMENT DATA STRUCTURE
3963
+
3964
  function LeafChunk(lines) {
3965
  this.lines = lines;
3966
  this.parent = null;
3970
  }
3971
  this.height = height;
3972
  }
3973
+
3974
  LeafChunk.prototype = {
3975
  chunkSize: function() { return this.lines.length; },
3976
+ remove: function(at, n, cm) {
3977
  for (var i = at, e = at + n; i < e; ++i) {
3978
  var line = this.lines[i];
3979
  this.height -= line.height;
3980
+ cleanUpLine(line);
3981
+ signalLater(cm, line, "delete");
 
3982
  }
3983
  this.lines.splice(at, n);
3984
  },
3987
  },
3988
  insertHeight: function(at, lines, height) {
3989
  this.height += height;
3990
+ this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at));
3991
  for (var i = 0, e = lines.length; i < e; ++i) lines[i].parent = this;
3992
  },
3993
  iterN: function(at, n, op) {
3995
  if (op(this.lines[at])) return true;
3996
  }
3997
  };
3998
+
3999
  function BranchChunk(children) {
4000
  this.children = children;
4001
  var size = 0, height = 0;
4008
  this.height = height;
4009
  this.parent = null;
4010
  }
4011
+
4012
  BranchChunk.prototype = {
4013
  chunkSize: function() { return this.size; },
4014
  remove: function(at, n, callbacks) {
4096
  }
4097
  };
4098
 
4099
+ // LINE UTILITIES
4100
+
4101
+ function getLine(chunk, n) {
4102
  while (!chunk.lines) {
4103
  for (var i = 0;; ++i) {
4104
  var child = chunk.children[i], sz = child.chunkSize();
4108
  }
4109
  return chunk.lines[n];
4110
  }
4111
+
4112
+ function updateLineHeight(line, height) {
4113
+ var diff = height - line.height;
4114
+ for (var n = line; n; n = n.parent) n.height += diff;
4115
+ }
4116
+
4117
  function lineNo(line) {
4118
  if (line.parent == null) return null;
4119
  var cur = line.parent, no = indexOf(cur.lines, line);
4120
  for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {
4121
+ for (var i = 0;; ++i) {
4122
  if (chunk.children[i] == cur) break;
4123
  no += chunk.children[i].chunkSize();
4124
  }
4125
  }
4126
  return no;
4127
  }
4128
+
4129
+ function lineDoc(line) {
4130
+ for (var d = line.parent; d.parent; d = d.parent) {}
4131
+ return d;
4132
+ }
4133
+
4134
  function lineAtHeight(chunk, h) {
4135
  var n = 0;
4136
  outer: do {
4149
  }
4150
  return n + i;
4151
  }
4152
+
4153
+ function heightAtLine(cm, lineObj) {
4154
+ lineObj = visualLine(cm.view.doc, lineObj);
4155
+
4156
+ var h = 0, chunk = lineObj.parent;
4157
+ for (var i = 0; i < chunk.lines.length; ++i) {
4158
+ var line = chunk.lines[i];
4159
+ if (line == lineObj) break;
4160
+ else h += line.height;
4161
+ }
4162
+ for (var p = chunk.parent; p; chunk = p, p = chunk.parent) {
4163
+ for (var i = 0; i < p.children.length; ++i) {
4164
+ var cur = p.children[i];
4165
+ if (cur == chunk) break;
4166
+ else h += cur.height;
4167
  }
4168
+ }
 
 
4169
  return h;
4170
  }
4171
 
4172
+ function getOrder(line) {
4173
+ var order = line.order;
4174
+ if (order == null) order = line.order = bidiOrdering(line.text);
4175
+ return order;
4176
+ }
4177
+
4178
+ // HISTORY
4179
+
4180
+ function makeHistory() {
4181
+ return {
4182
+ // Arrays of history events. Doing something adds an event to
4183
+ // done and clears undo. Undoing moves events from done to
4184
+ // undone, redoing moves them in the other direction.
4185
+ done: [], undone: [],
4186
+ // Used to track when changes can be merged into a single undo
4187
+ // event
4188
+ lastTime: 0, lastOp: null, lastOrigin: null,
4189
+ // Used by the isClean() method
4190
+ dirtyCounter: 0
4191
+ };
4192
+ }
4193
+
4194
+ function addChange(cm, start, added, old, origin, fromBefore, toBefore, fromAfter, toAfter) {
4195
+ var history = cm.view.history;
4196
+ history.undone.length = 0;
4197
+ var time = +new Date, cur = lst(history.done);
4198
+
4199
+ if (cur &&
4200
+ (history.lastOp == cm.curOp.id ||
4201
+ history.lastOrigin == origin && (origin == "input" || origin == "delete") &&
4202
+ history.lastTime > time - 600)) {
4203
+ // Merge this change into the last event
4204
+ var last = lst(cur.events);
4205
+ if (last.start > start + old.length || last.start + last.added < start) {
4206
+ // Doesn't intersect with last sub-event, add new sub-event
4207
+ cur.events.push({start: start, added: added, old: old});
4208
  } else {
4209
+ // Patch up the last sub-event
4210
+ var startBefore = Math.max(0, last.start - start),
4211
+ endAfter = Math.max(0, (start + old.length) - (last.start + last.added));
4212
+ for (var i = startBefore; i > 0; --i) last.old.unshift(old[i - 1]);
4213
+ for (var i = endAfter; i > 0; --i) last.old.push(old[old.length - i]);
4214
+ if (startBefore) last.start = start;
4215
+ last.added += added - (old.length - startBefore - endAfter);
 
 
 
 
 
 
 
4216
  }
4217
+ cur.fromAfter = fromAfter; cur.toAfter = toAfter;
4218
+ } else {
4219
+ // Can not be merged, start a new event.
4220
+ cur = {events: [{start: start, added: added, old: old}],
4221
+ fromBefore: fromBefore, toBefore: toBefore, fromAfter: fromAfter, toAfter: toAfter};
4222
+ history.done.push(cur);
4223
+ while (history.done.length > cm.options.undoDepth)
4224
+ history.done.shift();
4225
+ if (history.dirtyCounter < 0)
4226
+ // The user has made a change after undoing past the last clean state.
4227
+ // We can never get back to a clean state now until markClean() is called.
4228
+ history.dirtyCounter = NaN;
4229
+ else
4230
+ history.dirtyCounter++;
4231
  }
4232
+ history.lastTime = time;
4233
+ history.lastOp = cm.curOp.id;
4234
+ history.lastOrigin = origin;
4235
+ }
4236
+
4237
+ // EVENT OPERATORS
4238
 
4239
  function stopMethod() {e_stop(this);}
4240
  // Ensure an event has a stop method.
4258
 
4259
  function e_target(e) {return e.target || e.srcElement;}
4260
  function e_button(e) {
4261
+ var b = e.which;
4262
+ if (b == null) {
4263
+ if (e.button & 1) b = 1;
4264
+ else if (e.button & 2) b = 3;
4265
+ else if (e.button & 4) b = 2;
4266
+ }
4267
+ if (mac && e.ctrlKey && b == 1) b = 3;
4268
+ return b;
4269
  }
4270
 
4271
  // Allow 3rd-party code to override event properties by adding an override
4275
  return overridden ? e.override[prop] : e[prop];
4276
  }
4277
 
4278
+ // EVENT HANDLING
4279
+
4280
+ function on(emitter, type, f) {
4281
+ if (emitter.addEventListener)
4282
+ emitter.addEventListener(type, f, false);
4283
+ else if (emitter.attachEvent)
4284
+ emitter.attachEvent("on" + type, f);
4285
+ else {
4286
+ var map = emitter._handlers || (emitter._handlers = {});
4287
+ var arr = map[type] || (map[type] = []);
4288
+ arr.push(f);
4289
  }
4290
+ }
4291
+
4292
+ function off(emitter, type, f) {
4293
+ if (emitter.removeEventListener)
4294
+ emitter.removeEventListener(type, f, false);
4295
+ else if (emitter.detachEvent)
4296
+ emitter.detachEvent("on" + type, f);
4297
  else {
4298
+ var arr = emitter._handlers && emitter._handlers[type];
4299
+ if (!arr) return;
4300
+ for (var i = 0; i < arr.length; ++i)
4301
+ if (arr[i] == f) { arr.splice(i, 1); break; }
4302
  }
4303
  }
 
4304
 
4305
+ function signal(emitter, type /*, values...*/) {
4306
+ var arr = emitter._handlers && emitter._handlers[type];
4307
+ if (!arr) return;
4308
+ var args = Array.prototype.slice.call(arguments, 2);
4309
+ for (var i = 0; i < arr.length; ++i) arr[i].apply(null, args);
4310
+ }
4311
 
4312
+ function signalLater(cm, emitter, type /*, values...*/) {
4313
+ var arr = emitter._handlers && emitter._handlers[type];
4314
+ if (!arr) return;
4315
+ var args = Array.prototype.slice.call(arguments, 3), flist = cm.curOp && cm.curOp.delayedCallbacks;
4316
+ function bnd(f) {return function(){f.apply(null, args);};};
4317
+ for (var i = 0; i < arr.length; ++i)
4318
+ if (flist) flist.push(bnd(arr[i]));
4319
+ else arr[i].apply(null, args);
4320
+ }
4321
 
4322
+ function hasHandler(emitter, type) {
4323
+ var arr = emitter._handlers && emitter._handlers[type];
4324
+ return arr && arr.length > 0;
4325
+ }
 
 
4326
 
4327
+ CodeMirror.on = on; CodeMirror.off = off; CodeMirror.signal = signal;
4328
+
4329
+ // MISC UTILITIES
4330
+
4331
+ // Number of pixels added to scroller and sizer to hide scrollbar
4332
+ var scrollerCutOff = 30;
4333
+
4334
+ // Returned or thrown by various protocols to signal 'I'm not
4335
+ // handling this'.
4336
+ var Pass = CodeMirror.Pass = {toString: function(){return "CodeMirror.Pass";}};
4337
 
4338
+ function Delayed() {this.id = null;}
4339
+ Delayed.prototype = {set: function(ms, f) {clearTimeout(this.id); this.id = setTimeout(f, ms);}};
 
 
 
 
 
4340
 
4341
  // Counts the column offset in a string, taking tabs into account.
4342
  // Used mostly to find indentation.
4351
  }
4352
  return n;
4353
  }
4354
+ CodeMirror.countColumn = countColumn;
4355
 
4356
+ var spaceStrs = [""];
4357
+ function spaceStr(n) {
4358
+ while (spaceStrs.length <= n)
4359
+ spaceStrs.push(lst(spaceStrs) + " ");
4360
+ return spaceStrs[n];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4361
  }
4362
+
4363
+ function lst(arr) { return arr[arr.length-1]; }
4364
+
4365
  function selectInput(node) {
4366
  if (ios) { // Mobile Safari apparently has a bug where select() is broken.
4367
  node.selectionStart = 0;
4369
  } else node.select();
4370
  }
4371
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4372
  function indexOf(collection, elt) {
4373
  if (collection.indexOf) return collection.indexOf(elt);
4374
  for (var i = 0, e = collection.length; i < e; ++i)
4375
  if (collection[i] == elt) return i;
4376
  return -1;
4377
  }
4378
+
4379
+ function emptyArray(size) {
4380
+ for (var a = [], i = 0; i < size; ++i) a.push(undefined);
4381
+ return a;
4382
+ }
4383
+
4384
+ function bind(f) {
4385
+ var args = Array.prototype.slice.call(arguments, 1);
4386
+ return function(){return f.apply(null, args);};
4387
+ }
4388
+
4389
+ var nonASCIISingleCaseWordChar = /[\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc]/;
4390
  function isWordChar(ch) {
4391
+ return /\w/.test(ch) || ch > "\x80" &&
4392
+ (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch));
4393
+ }
4394
+
4395
+ function isEmpty(obj) {
4396
+ var c = 0;
4397
+ for (var n in obj) if (obj.hasOwnProperty(n) && obj[n]) ++c;
4398
+ return !c;
4399
+ }
4400
+
4401
+ var isExtendingChar = /[\u0300-\u036F\u0483-\u0487\u0488-\u0489\u0591-\u05BD\u05BF\u05C1-\u05C2\u05C4-\u05C5\u05C7\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7-\u06E8\u06EA-\u06ED\uA66F\uA670-\uA672\uA674-\uA67D\uA69F]/;
4402
+
4403
+ // DOM UTILITIES
4404
+
4405
+ function elt(tag, content, className, style) {
4406
+ var e = document.createElement(tag);
4407
+ if (className) e.className = className;
4408
+ if (style) e.style.cssText = style;
4409
+ if (typeof content == "string") setTextContent(e, content);
4410
+ else if (content) for (var i = 0; i < content.length; ++i) e.appendChild(content[i]);
4411
+ return e;
4412
+ }
4413
+
4414
+ function removeChildren(e) {
4415
+ // IE will break all parent-child relations in subnodes when setting innerHTML
4416
+ if (!ie) e.innerHTML = "";
4417
+ else while (e.firstChild) e.removeChild(e.firstChild);
4418
+ return e;
4419
+ }
4420
+
4421
+ function removeChildrenAndAdd(parent, e) {
4422
+ return removeChildren(parent).appendChild(e);
4423
+ }
4424
+
4425
+ function setTextContent(e, str) {
4426
+ if (ie_lt9) {
4427
+ e.innerHTML = "";
4428
+ e.appendChild(document.createTextNode(str));
4429
+ } else e.textContent = str;
4430
+ }
4431
+
4432
+ // FEATURE DETECTION
4433
+
4434
+ // Detect drag-and-drop
4435
+ var dragAndDrop = function() {
4436
+ // There is *some* kind of drag-and-drop support in IE6-8, but I
4437
+ // couldn't get it to work yet.
4438
+ if (ie_lt9) return false;
4439
+ var div = elt('div');
4440
+ return "draggable" in div || "dragDrop" in div;
4441
+ }();
4442
+
4443
+ // For a reason I have yet to figure out, some browsers disallow
4444
+ // word wrapping between certain characters *only* if a new inline
4445
+ // element is started between them. This makes it hard to reliably
4446
+ // measure the position of things, since that requires inserting an
4447
+ // extra span. This terribly fragile set of regexps matches the
4448
+ // character combinations that suffer from this phenomenon on the
4449
+ // various browsers.
4450
+ var spanAffectsWrapping = /^$/; // Won't match any two-character string
4451
+ if (gecko) spanAffectsWrapping = /$'/;
4452
+ else if (safari) spanAffectsWrapping = /\-[^ \-?]|\?[^ !'\"\),.\-\/:;\?\]\}]/;
4453
+ else if (chrome) spanAffectsWrapping = /\-[^ \-\.?]|\?[^ \-\.?\]\}:;!'\"\),\/]|[\.!\"#&%\)*+,:;=>\]|\}~][\(\{\[<]|\$'/;
4454
+
4455
+ var knownScrollbarWidth;
4456
+ function scrollbarWidth(measure) {
4457
+ if (knownScrollbarWidth != null) return knownScrollbarWidth;
4458
+ var test = elt("div", null, null, "width: 50px; height: 50px; overflow-x: scroll");
4459
+ removeChildrenAndAdd(measure, test);
4460
+ if (test.offsetWidth)
4461
+ knownScrollbarWidth = test.offsetHeight - test.clientHeight;
4462
+ return knownScrollbarWidth || 0;
4463
+ }
4464
+
4465
+ var zwspSupported;
4466
+ function zeroWidthElement(measure) {
4467
+ if (zwspSupported == null) {
4468
+ var test = elt("span", "\u200b");
4469
+ removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")]));
4470
+ if (measure.firstChild.offsetHeight != 0)
4471
+ zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !ie_lt8;
4472
+ }
4473
+ if (zwspSupported) return elt("span", "\u200b");
4474
+ else return elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px");
4475
  }
4476
 
4477
  // See if "".split is the broken IE version, if so, provide an
4478
  // alternative way to split lines.
4479
  var splitLines = "\n\nb".split(/\n/).length != 3 ? function(string) {
4480
+ var pos = 0, result = [], l = string.length;
4481
+ while (pos <= l) {
4482
+ var nl = string.indexOf("\n", pos);
4483
+ if (nl == -1) nl = string.length;
4484
+ var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl);
4485
+ var rt = line.indexOf("\r");
4486
+ if (rt != -1) {
4487
+ result.push(line.slice(0, rt));
4488
+ pos += rt + 1;
4489
+ } else {
4490
+ result.push(line);
4491
+ pos = nl + 1;
4492
+ }
4493
  }
 
4494
  return result;
4495
+ } : function(string){return string.split(/\r\n?|\n/);};
4496
  CodeMirror.splitLines = splitLines;
4497
 
4498
  var hasSelection = window.getSelection ? function(te) {
4505
  return range.compareEndPoints("StartToEnd", range) != 0;
4506
  };
4507
 
4508
+ var hasCopyEvent = (function() {
4509
+ var e = elt("div");
4510
+ if ("oncopy" in e) return true;
4511
+ e.setAttribute("oncopy", "return;");
4512
+ return typeof e.oncopy == 'function';
4513
+ })();
4514
+
4515
+ // KEY NAMING
4516
 
4517
  var keyNames = {3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
4518
  19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
4519
  36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
4520
+ 46: "Delete", 59: ";", 91: "Mod", 92: "Mod", 93: "Mod", 109: "-", 107: "=", 127: "Delete",
4521
+ 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\",
4522
+ 221: "]", 222: "'", 63276: "PageUp", 63277: "PageDown", 63275: "End", 63273: "Home",
4523
+ 63234: "Left", 63232: "Up", 63235: "Right", 63233: "Down", 63302: "Insert", 63272: "Delete"};
4524
  CodeMirror.keyNames = keyNames;
4525
  (function() {
4526
  // Number keys
4531
  for (var i = 1; i <= 12; i++) keyNames[i + 111] = keyNames[i + 63235] = "F" + i;
4532
  })();
4533
 
4534
+ // BIDI HELPERS
4535
+
4536
+ function iterateBidiSections(order, from, to, f) {
4537
+ if (!order) return f(from, to, "ltr");
4538
+ for (var i = 0; i < order.length; ++i) {
4539
+ var part = order[i];
4540
+ if (part.from < to && part.to > from || from == to && part.to == from)
4541
+ f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr");
4542
+ }
4543
+ }
4544
+
4545
+ function bidiLeft(part) { return part.level % 2 ? part.to : part.from; }
4546
+ function bidiRight(part) { return part.level % 2 ? part.from : part.to; }
4547
+
4548
+ function lineLeft(line) { var order = getOrder(line); return order ? bidiLeft(order[0]) : 0; }
4549
+ function lineRight(line) {
4550
+ var order = getOrder(line);
4551
+ if (!order) return line.text.length;
4552
+ return bidiRight(lst(order));
4553
+ }
4554
+
4555
+ function lineStart(cm, lineN) {
4556
+ var line = getLine(cm.view.doc, lineN);
4557
+ var visual = visualLine(cm.view.doc, line);
4558
+ if (visual != line) lineN = lineNo(visual);
4559
+ var order = getOrder(visual);
4560
+ var ch = !order ? 0 : order[0].level % 2 ? lineRight(visual) : lineLeft(visual);
4561
+ return {line: lineN, ch: ch};
4562
+ }
4563
+ function lineEnd(cm, lineNo) {
4564
+ var merged, line;
4565
+ while (merged = collapsedSpanAtEnd(line = getLine(cm.view.doc, lineNo)))
4566
+ lineNo = merged.find().to.line;
4567
+ var order = getOrder(line);
4568
+ var ch = !order ? line.text.length : order[0].level % 2 ? lineLeft(line) : lineRight(line);
4569
+ return {line: lineNo, ch: ch};
4570
+ }
4571
+
4572
+ // This is somewhat involved. It is needed in order to move
4573
+ // 'visually' through bi-directional text -- i.e., pressing left
4574
+ // should make the cursor go left, even when in RTL text. The
4575
+ // tricky part is the 'jumps', where RTL and LTR text touch each
4576
+ // other. This often requires the cursor offset to move more than
4577
+ // one unit, in order to visually move one unit.
4578
+ function moveVisually(line, start, dir, byUnit) {
4579
+ var bidi = getOrder(line);
4580
+ if (!bidi) return moveLogically(line, start, dir, byUnit);
4581
+ var moveOneUnit = byUnit ? function(pos, dir) {
4582
+ do pos += dir;
4583
+ while (pos > 0 && isExtendingChar.test(line.text.charAt(pos)));
4584
+ return pos;
4585
+ } : function(pos, dir) { return pos + dir; };
4586
+ var linedir = bidi[0].level;
4587
+ for (var i = 0; i < bidi.length; ++i) {
4588
+ var part = bidi[i], sticky = part.level % 2 == linedir;
4589
+ if ((part.from < start && part.to > start) ||
4590
+ (sticky && (part.from == start || part.to == start))) break;
4591
+ }
4592
+ var target = moveOneUnit(start, part.level % 2 ? -dir : dir);
4593
+
4594
+ while (target != null) {
4595
+ if (part.level % 2 == linedir) {
4596
+ if (target < part.from || target > part.to) {
4597
+ part = bidi[i += dir];
4598
+ target = part && (dir > 0 == part.level % 2 ? moveOneUnit(part.to, -1) : moveOneUnit(part.from, 1));
4599
+ } else break;
4600
+ } else {
4601
+ if (target == bidiLeft(part)) {
4602
+ part = bidi[--i];
4603
+ target = part && bidiRight(part);
4604
+ } else if (target == bidiRight(part)) {
4605
+ part = bidi[++i];
4606
+ target = part && bidiLeft(part);
4607
+ } else break;
4608
+ }
4609
+ }
4610
+
4611
+ return target < 0 || target > line.text.length ? null : target;
4612
+ }
4613
+
4614
+ function moveLogically(line, start, dir, byUnit) {
4615
+ var target = start + dir;
4616
+ if (byUnit) while (target > 0 && isExtendingChar.test(line.text.charAt(target))) target += dir;
4617
+ return target < 0 || target > line.text.length ? null : target;
4618
+ }
4619
+
4620
+ // Bidirectional ordering algorithm
4621
+ // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm
4622
+ // that this (partially) implements.
4623
+
4624
+ // One-char codes used for character types:
4625
+ // L (L): Left-to-Right
4626
+ // R (R): Right-to-Left
4627
+ // r (AL): Right-to-Left Arabic
4628
+ // 1 (EN): European Number
4629
+ // + (ES): European Number Separator
4630
+ // % (ET): European Number Terminator
4631
+ // n (AN): Arabic Number
4632
+ // , (CS): Common Number Separator
4633
+ // m (NSM): Non-Spacing Mark
4634
+ // b (BN): Boundary Neutral
4635
+ // s (B): Paragraph Separator
4636
+ // t (S): Segment Separator
4637
+ // w (WS): Whitespace
4638
+ // N (ON): Other Neutrals
4639
+
4640
+ // Returns null if characters are ordered as they appear
4641
+ // (left-to-right), or an array of sections ({from, to, level}
4642
+ // objects) in the order in which they occur visually.
4643
+ var bidiOrdering = (function() {
4644
+ // Character types for codepoints 0 to 0xff
4645
+ var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLL";
4646
+ // Character types for codepoints 0x600 to 0x6ff
4647
+ var arabicTypes = "rrrrrrrrrrrr,rNNmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmrrrrrrrnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmNmmmmrrrrrrrrrrrrrrrrrr";
4648
+ function charType(code) {
4649
+ if (code <= 0xff) return lowTypes.charAt(code);
4650
+ else if (0x590 <= code && code <= 0x5f4) return "R";
4651
+ else if (0x600 <= code && code <= 0x6ff) return arabicTypes.charAt(code - 0x600);
4652
+ else if (0x700 <= code && code <= 0x8ac) return "r";
4653
+ else return "L";
4654
+ }
4655
+
4656
+ var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;
4657
+ var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/;
4658
+ // Browsers seem to always treat the boundaries of block elements as being L.
4659
+ var outerType = "L";
4660
+
4661
+ return function charOrdering(str) {
4662
+ if (!bidiRE.test(str)) return false;
4663
+ var len = str.length, types = [];
4664
+ for (var i = 0, type; i < len; ++i)
4665
+ types.push(type = charType(str.charCodeAt(i)));
4666
+
4667
+ // W1. Examine each non-spacing mark (NSM) in the level run, and
4668
+ // change the type of the NSM to the type of the previous
4669
+ // character. If the NSM is at the start of the level run, it will
4670
+ // get the type of sor.
4671
+ for (var i = 0, prev = outerType; i < len; ++i) {
4672
+ var type = types[i];
4673
+ if (type == "m") types[i] = prev;
4674
+ else prev = type;
4675
+ }
4676
+
4677
+ // W2. Search backwards from each instance of a European number
4678
+ // until the first strong type (R, L, AL, or sor) is found. If an
4679
+ // AL is found, change the type of the European number to Arabic
4680
+ // number.
4681
+ // W3. Change all ALs to R.
4682
+ for (var i = 0, cur = outerType; i < len; ++i) {
4683
+ var type = types[i];
4684
+ if (type == "1" && cur == "r") types[i] = "n";
4685
+ else if (isStrong.test(type)) { cur = type; if (type == "r") types[i] = "R"; }
4686
+ }
4687
+
4688
+ // W4. A single European separator between two European numbers
4689
+ // changes to a European number. A single common separator between
4690
+ // two numbers of the same type changes to that type.
4691
+ for (var i = 1, prev = types[0]; i < len - 1; ++i) {
4692
+ var type = types[i];
4693
+ if (type == "+" && prev == "1" && types[i+1] == "1") types[i] = "1";
4694
+ else if (type == "," && prev == types[i+1] &&
4695
+ (prev == "1" || prev == "n")) types[i] = prev;
4696
+ prev = type;
4697
+ }
4698
+
4699
+ // W5. A sequence of European terminators adjacent to European
4700
+ // numbers changes to all European numbers.
4701
+ // W6. Otherwise, separators and terminators change to Other
4702
+ // Neutral.
4703
+ for (var i = 0; i < len; ++i) {
4704
+ var type = types[i];
4705
+ if (type == ",") types[i] = "N";
4706
+ else if (type == "%") {
4707
+ for (var end = i + 1; end < len && types[end] == "%"; ++end) {}
4708
+ var replace = (i && types[i-1] == "!") || (end < len - 1 && types[end] == "1") ? "1" : "N";
4709
+ for (var j = i; j < end; ++j) types[j] = replace;
4710
+ i = end - 1;
4711
+ }
4712
+ }
4713
+
4714
+ // W7. Search backwards from each instance of a European number
4715
+ // until the first strong type (R, L, or sor) is found. If an L is
4716
+ // found, then change the type of the European number to L.
4717
+ for (var i = 0, cur = outerType; i < len; ++i) {
4718
+ var type = types[i];
4719
+ if (cur == "L" && type == "1") types[i] = "L";
4720
+ else if (isStrong.test(type)) cur = type;
4721
+ }
4722
+
4723
+ // N1. A sequence of neutrals takes the direction of the
4724
+ // surrounding strong text if the text on both sides has the same
4725
+ // direction. European and Arabic numbers act as if they were R in
4726
+ // terms of their influence on neutrals. Start-of-level-run (sor)
4727
+ // and end-of-level-run (eor) are used at level run boundaries.
4728
+ // N2. Any remaining neutrals take the embedding direction.
4729
+ for (var i = 0; i < len; ++i) {
4730
+ if (isNeutral.test(types[i])) {
4731
+ for (var end = i + 1; end < len && isNeutral.test(types[end]); ++end) {}
4732
+ var before = (i ? types[i-1] : outerType) == "L";
4733
+ var after = (end < len - 1 ? types[end] : outerType) == "L";
4734
+ var replace = before || after ? "L" : "R";
4735
+ for (var j = i; j < end; ++j) types[j] = replace;
4736
+ i = end - 1;
4737
+ }
4738
+ }
4739
+
4740
+ // Here we depart from the documented algorithm, in order to avoid
4741
+ // building up an actual levels array. Since there are only three
4742
+ // levels (0, 1, 2) in an implementation that doesn't take
4743
+ // explicit embedding into account, we can build up the order on
4744
+ // the fly, without following the level-based algorithm.
4745
+ var order = [], m;
4746
+ for (var i = 0; i < len;) {
4747
+ if (countsAsLeft.test(types[i])) {
4748
+ var start = i;
4749
+ for (++i; i < len && countsAsLeft.test(types[i]); ++i) {}
4750
+ order.push({from: start, to: i, level: 0});
4751
+ } else {
4752
+ var pos = i, at = order.length;
4753
+ for (++i; i < len && types[i] != "L"; ++i) {}
4754
+ for (var j = pos; j < i;) {
4755
+ if (countsAsNum.test(types[j])) {
4756
+ if (pos < j) order.splice(at, 0, {from: pos, to: j, level: 1});
4757
+ var nstart = j;
4758
+ for (++j; j < i && countsAsNum.test(types[j]); ++j) {}
4759
+ order.splice(at, 0, {from: nstart, to: j, level: 2});
4760
+ pos = j;
4761
+ } else ++j;
4762
+ }
4763
+ if (pos < i) order.splice(at, 0, {from: pos, to: i, level: 1});
4764
+ }
4765
+ }
4766
+ if (order[0].level == 1 && (m = str.match(/^\s+/))) {
4767
+ order[0].from = m[0].length;
4768
+ order.unshift({from: 0, to: m[0].length, level: 0});
4769
+ }
4770
+ if (lst(order).level == 1 && (m = str.match(/\s+$/))) {
4771
+ lst(order).to -= m[0].length;
4772
+ order.push({from: len - m[0].length, to: len, level: 0});
4773
+ }
4774
+ if (order[0].level != lst(order).level)
4775
+ order.push({from: len, to: len, level: order[0].level});
4776
+
4777
+ return order;
4778
+ };
4779
+ })();
4780
+
4781
+ // THE END
4782
+
4783
+ CodeMirror.version = "3.02";
4784
+
4785
  return CodeMirror;
4786
  })();
lib/css.js CHANGED
@@ -1,10 +1,196 @@
1
  CodeMirror.defineMode("css", function(config) {
2
  var indentUnit = config.indentUnit, type;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  function ret(style, tp) {type = tp; return style;}
4
 
5
  function tokenBase(stream, state) {
6
  var ch = stream.next();
7
- if (ch == "@") {stream.eatWhile(/[\w\\\-]/); return ret("meta", stream.current());}
8
  else if (ch == "/" && stream.eat("*")) {
9
  state.tokenize = tokenCComment;
10
  return tokenCComment(stream, state);
@@ -31,15 +217,34 @@ CodeMirror.defineMode("css", function(config) {
31
  stream.eatWhile(/[\w.%]/);
32
  return ret("number", "unit");
33
  }
34
- else if (/[,.+>*\/]/.test(ch)) {
 
 
 
 
 
 
 
 
35
  return ret(null, "select-op");
36
  }
37
- else if (/[;{}:\[\]]/.test(ch)) {
 
 
 
 
 
 
38
  return ret(null, ch);
39
  }
 
 
 
 
 
40
  else {
41
  stream.eatWhile(/[\w\\\-]/);
42
- return ret("variable", "variable");
43
  }
44
  }
45
 
@@ -67,7 +272,7 @@ CodeMirror.defineMode("css", function(config) {
67
  return ret("comment", "comment");
68
  }
69
 
70
- function tokenString(quote) {
71
  return function(stream, state) {
72
  var escaped = false, ch;
73
  while ((ch = stream.next()) != null) {
@@ -75,11 +280,23 @@ CodeMirror.defineMode("css", function(config) {
75
  break;
76
  escaped = !escaped && ch == "\\";
77
  }
78
- if (!escaped) state.tokenize = tokenBase;
 
 
 
79
  return ret("string", "string");
80
  };
81
  }
82
 
 
 
 
 
 
 
 
 
 
83
  return {
84
  startState: function(base) {
85
  return {tokenize: tokenBase,
@@ -88,32 +305,156 @@ CodeMirror.defineMode("css", function(config) {
88
  },
89
 
90
  token: function(stream, state) {
91
- if (stream.eatSpace()) return null;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
92
  var style = state.tokenize(stream, state);
93
 
 
94
  var context = state.stack[state.stack.length-1];
95
- if (type == "hash" && context != "rule") style = "string-2";
96
- else if (style == "variable") {
97
- if (context == "rule") style = "number";
98
- else if (!context || context == "@media{") style = "tag";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99
  }
100
 
101
- if (context == "rule" && /^[\{\};]$/.test(type))
102
- state.stack.pop();
103
  if (type == "{") {
104
- if (context == "@media") state.stack[state.stack.length-1] = "@media{";
105
- else state.stack.push("{");
 
 
 
 
 
 
 
106
  }
107
- else if (type == "}") state.stack.pop();
108
  else if (type == "@media") state.stack.push("@media");
109
- else if (context == "{" && type != "comment") state.stack.push("rule");
 
 
 
 
 
 
110
  return style;
111
  },
112
 
113
  indent: function(state, textAfter) {
114
  var n = state.stack.length;
115
  if (/^\}/.test(textAfter))
116
- n -= state.stack[state.stack.length-1] == "rule" ? 2 : 1;
117
  return state.baseIndent + n * indentUnit;
118
  },
119
 
1
  CodeMirror.defineMode("css", function(config) {
2
  var indentUnit = config.indentUnit, type;
3
+
4
+ var atMediaTypes = keySet([
5
+ "all", "aural", "braille", "handheld", "print", "projection", "screen",
6
+ "tty", "tv", "embossed"
7
+ ]);
8
+
9
+ var atMediaFeatures = keySet([
10
+ "width", "min-width", "max-width", "height", "min-height", "max-height",
11
+ "device-width", "min-device-width", "max-device-width", "device-height",
12
+ "min-device-height", "max-device-height", "aspect-ratio",
13
+ "min-aspect-ratio", "max-aspect-ratio", "device-aspect-ratio",
14
+ "min-device-aspect-ratio", "max-device-aspect-ratio", "color", "min-color",
15
+ "max-color", "color-index", "min-color-index", "max-color-index",
16
+ "monochrome", "min-monochrome", "max-monochrome", "resolution",
17
+ "min-resolution", "max-resolution", "scan", "grid"
18
+ ]);
19
+
20
+ var propertyKeywords = keySet([
21
+ "align-content", "align-items", "align-self", "alignment-adjust",
22
+ "alignment-baseline", "anchor-point", "animation", "animation-delay",
23
+ "animation-direction", "animation-duration", "animation-iteration-count",
24
+ "animation-name", "animation-play-state", "animation-timing-function",
25
+ "appearance", "azimuth", "backface-visibility", "background",
26
+ "background-attachment", "background-clip", "background-color",
27
+ "background-image", "background-origin", "background-position",
28
+ "background-repeat", "background-size", "baseline-shift", "binding",
29
+ "bleed", "bookmark-label", "bookmark-level", "bookmark-state",
30
+ "bookmark-target", "border", "border-bottom", "border-bottom-color",
31
+ "border-bottom-left-radius", "border-bottom-right-radius",
32
+ "border-bottom-style", "border-bottom-width", "border-collapse",
33
+ "border-color", "border-image", "border-image-outset",
34
+ "border-image-repeat", "border-image-slice", "border-image-source",
35
+ "border-image-width", "border-left", "border-left-color",
36
+ "border-left-style", "border-left-width", "border-radius", "border-right",
37
+ "border-right-color", "border-right-style", "border-right-width",
38
+ "border-spacing", "border-style", "border-top", "border-top-color",
39
+ "border-top-left-radius", "border-top-right-radius", "border-top-style",
40
+ "border-top-width", "border-width", "bottom", "box-decoration-break",
41
+ "box-shadow", "box-sizing", "break-after", "break-before", "break-inside",
42
+ "caption-side", "clear", "clip", "color", "color-profile", "column-count",
43
+ "column-fill", "column-gap", "column-rule", "column-rule-color",
44
+ "column-rule-style", "column-rule-width", "column-span", "column-width",
45
+ "columns", "content", "counter-increment", "counter-reset", "crop", "cue",
46
+ "cue-after", "cue-before", "cursor", "direction", "display",
47
+ "dominant-baseline", "drop-initial-after-adjust",
48
+ "drop-initial-after-align", "drop-initial-before-adjust",
49
+ "drop-initial-before-align", "drop-initial-size", "drop-initial-value",
50
+ "elevation", "empty-cells", "fit", "fit-position", "flex", "flex-basis",
51
+ "flex-direction", "flex-flow", "flex-grow", "flex-shrink", "flex-wrap",
52
+ "float", "float-offset", "font", "font-feature-settings", "font-family",
53
+ "font-kerning", "font-language-override", "font-size", "font-size-adjust",
54
+ "font-stretch", "font-style", "font-synthesis", "font-variant",
55
+ "font-variant-alternates", "font-variant-caps", "font-variant-east-asian",
56
+ "font-variant-ligatures", "font-variant-numeric", "font-variant-position",
57
+ "font-weight", "grid-cell", "grid-column", "grid-column-align",
58
+ "grid-column-sizing", "grid-column-span", "grid-columns", "grid-flow",
59
+ "grid-row", "grid-row-align", "grid-row-sizing", "grid-row-span",
60
+ "grid-rows", "grid-template", "hanging-punctuation", "height", "hyphens",
61
+ "icon", "image-orientation", "image-rendering", "image-resolution",
62
+ "inline-box-align", "justify-content", "left", "letter-spacing",
63
+ "line-break", "line-height", "line-stacking", "line-stacking-ruby",
64
+ "line-stacking-shift", "line-stacking-strategy", "list-style",
65
+ "list-style-image", "list-style-position", "list-style-type", "margin",
66
+ "margin-bottom", "margin-left", "margin-right", "margin-top",
67
+ "marker-offset", "marks", "marquee-direction", "marquee-loop",
68
+ "marquee-play-count", "marquee-speed", "marquee-style", "max-height",
69
+ "max-width", "min-height", "min-width", "move-to", "nav-down", "nav-index",
70
+ "nav-left", "nav-right", "nav-up", "opacity", "order", "orphans", "outline",
71
+ "outline-color", "outline-offset", "outline-style", "outline-width",
72
+ "overflow", "overflow-style", "overflow-wrap", "overflow-x", "overflow-y",
73
+ "padding", "padding-bottom", "padding-left", "padding-right", "padding-top",
74
+ "page", "page-break-after", "page-break-before", "page-break-inside",
75
+ "page-policy", "pause", "pause-after", "pause-before", "perspective",
76
+ "perspective-origin", "pitch", "pitch-range", "play-during", "position",
77
+ "presentation-level", "punctuation-trim", "quotes", "rendering-intent",
78
+ "resize", "rest", "rest-after", "rest-before", "richness", "right",
79
+ "rotation", "rotation-point", "ruby-align", "ruby-overhang",
80
+ "ruby-position", "ruby-span", "size", "speak", "speak-as", "speak-header",
81
+ "speak-numeral", "speak-punctuation", "speech-rate", "stress", "string-set",
82
+ "tab-size", "table-layout", "target", "target-name", "target-new",
83
+ "target-position", "text-align", "text-align-last", "text-decoration",
84
+ "text-decoration-color", "text-decoration-line", "text-decoration-skip",
85
+ "text-decoration-style", "text-emphasis", "text-emphasis-color",
86
+ "text-emphasis-position", "text-emphasis-style", "text-height",
87
+ "text-indent", "text-justify", "text-outline", "text-shadow",
88
+ "text-space-collapse", "text-transform", "text-underline-position",
89
+ "text-wrap", "top", "transform", "transform-origin", "transform-style",
90
+ "transition", "transition-delay", "transition-duration",
91
+ "transition-property", "transition-timing-function", "unicode-bidi",
92
+ "vertical-align", "visibility", "voice-balance", "voice-duration",
93
+ "voice-family", "voice-pitch", "voice-range", "voice-rate", "voice-stress",
94
+ "voice-volume", "volume", "white-space", "widows", "width", "word-break",
95
+ "word-spacing", "word-wrap", "z-index"
96
+ ]);
97
+
98
+ var colorKeywords = keySet([
99
+ "black", "silver", "gray", "white", "maroon", "red", "purple", "fuchsia",
100
+ "green", "lime", "olive", "yellow", "navy", "blue", "teal", "aqua"
101
+ ]);
102
+
103
+ var valueKeywords = keySet([
104
+ "above", "absolute", "activeborder", "activecaption", "afar",
105
+ "after-white-space", "ahead", "alias", "all", "all-scroll", "alternate",
106
+ "always", "amharic", "amharic-abegede", "antialiased", "appworkspace",
107
+ "arabic-indic", "armenian", "asterisks", "auto", "avoid", "background",
108
+ "backwards", "baseline", "below", "bidi-override", "binary", "bengali",
109
+ "blink", "block", "block-axis", "bold", "bolder", "border", "border-box",
110
+ "both", "bottom", "break-all", "break-word", "button", "button-bevel",
111
+ "buttonface", "buttonhighlight", "buttonshadow", "buttontext", "cambodian",
112
+ "capitalize", "caps-lock-indicator", "caption", "captiontext", "caret",
113
+ "cell", "center", "checkbox", "circle", "cjk-earthly-branch",
114
+ "cjk-heavenly-stem", "cjk-ideographic", "clear", "clip", "close-quote",
115
+ "col-resize", "collapse", "compact", "condensed", "contain", "content",
116
+ "content-box", "context-menu", "continuous", "copy", "cover", "crop",
117
+ "cross", "crosshair", "currentcolor", "cursive", "dashed", "decimal",
118
+ "decimal-leading-zero", "default", "default-button", "destination-atop",
119
+ "destination-in", "destination-out", "destination-over", "devanagari",
120
+ "disc", "discard", "document", "dot-dash", "dot-dot-dash", "dotted",
121
+ "double", "down", "e-resize", "ease", "ease-in", "ease-in-out", "ease-out",
122
+ "element", "ellipsis", "embed", "end", "ethiopic", "ethiopic-abegede",
123
+ "ethiopic-abegede-am-et", "ethiopic-abegede-gez", "ethiopic-abegede-ti-er",
124
+ "ethiopic-abegede-ti-et", "ethiopic-halehame-aa-er",
125
+ "ethiopic-halehame-aa-et", "ethiopic-halehame-am-et",
126
+ "ethiopic-halehame-gez", "ethiopic-halehame-om-et",
127
+ "ethiopic-halehame-sid-et", "ethiopic-halehame-so-et",
128
+ "ethiopic-halehame-ti-er", "ethiopic-halehame-ti-et",
129
+ "ethiopic-halehame-tig", "ew-resize", "expanded", "extra-condensed",
130
+ "extra-expanded", "fantasy", "fast", "fill", "fixed", "flat", "footnotes",
131
+ "forwards", "from", "geometricPrecision", "georgian", "graytext", "groove",
132
+ "gujarati", "gurmukhi", "hand", "hangul", "hangul-consonant", "hebrew",
133
+ "help", "hidden", "hide", "higher", "highlight", "highlighttext",
134
+ "hiragana", "hiragana-iroha", "horizontal", "hsl", "hsla", "icon", "ignore",
135
+ "inactiveborder", "inactivecaption", "inactivecaptiontext", "infinite",
136
+ "infobackground", "infotext", "inherit", "initial", "inline", "inline-axis",
137
+ "inline-block", "inline-table", "inset", "inside", "intrinsic", "invert",
138
+ "italic", "justify", "kannada", "katakana", "katakana-iroha", "khmer",
139
+ "landscape", "lao", "large", "larger", "left", "level", "lighter",
140
+ "line-through", "linear", "lines", "list-item", "listbox", "listitem",
141
+ "local", "logical", "loud", "lower", "lower-alpha", "lower-armenian",
142
+ "lower-greek", "lower-hexadecimal", "lower-latin", "lower-norwegian",
143
+ "lower-roman", "lowercase", "ltr", "malayalam", "match",
144
+ "media-controls-background", "media-current-time-display",
145
+ "media-fullscreen-button", "media-mute-button", "media-play-button",
146
+ "media-return-to-realtime-button", "media-rewind-button",
147
+ "media-seek-back-button", "media-seek-forward-button", "media-slider",
148
+ "media-sliderthumb", "media-time-remaining-display", "media-volume-slider",
149
+ "media-volume-slider-container", "media-volume-sliderthumb", "medium",
150
+ "menu", "menulist", "menulist-button", "menulist-text",
151
+ "menulist-textfield", "menutext", "message-box", "middle", "min-intrinsic",
152
+ "mix", "mongolian", "monospace", "move", "multiple", "myanmar", "n-resize",
153
+ "narrower", "navy", "ne-resize", "nesw-resize", "no-close-quote", "no-drop",
154
+ "no-open-quote", "no-repeat", "none", "normal", "not-allowed", "nowrap",
155
+ "ns-resize", "nw-resize", "nwse-resize", "oblique", "octal", "open-quote",
156
+ "optimizeLegibility", "optimizeSpeed", "oriya", "oromo", "outset",
157
+ "outside", "overlay", "overline", "padding", "padding-box", "painted",
158
+ "paused", "persian", "plus-darker", "plus-lighter", "pointer", "portrait",
159
+ "pre", "pre-line", "pre-wrap", "preserve-3d", "progress", "push-button",
160
+ "radio", "read-only", "read-write", "read-write-plaintext-only", "relative",
161
+ "repeat", "repeat-x", "repeat-y", "reset", "reverse", "rgb", "rgba",
162
+ "ridge", "right", "round", "row-resize", "rtl", "run-in", "running",
163
+ "s-resize", "sans-serif", "scroll", "scrollbar", "se-resize", "searchfield",
164
+ "searchfield-cancel-button", "searchfield-decoration",
165
+ "searchfield-results-button", "searchfield-results-decoration",
166
+ "semi-condensed", "semi-expanded", "separate", "serif", "show", "sidama",
167
+ "single", "skip-white-space", "slide", "slider-horizontal",
168
+ "slider-vertical", "sliderthumb-horizontal", "sliderthumb-vertical", "slow",
169
+ "small", "small-caps", "small-caption", "smaller", "solid", "somali",
170
+ "source-atop", "source-in", "source-out", "source-over", "space", "square",
171
+ "square-button", "start", "static", "status-bar", "stretch", "stroke",
172
+ "sub", "subpixel-antialiased", "super", "sw-resize", "table",
173
+ "table-caption", "table-cell", "table-column", "table-column-group",
174
+ "table-footer-group", "table-header-group", "table-row", "table-row-group",
175
+ "telugu", "text", "text-bottom", "text-top", "textarea", "textfield", "thai",
176
+ "thick", "thin", "threeddarkshadow", "threedface", "threedhighlight",
177
+ "threedlightshadow", "threedshadow", "tibetan", "tigre", "tigrinya-er",
178
+ "tigrinya-er-abegede", "tigrinya-et", "tigrinya-et-abegede", "to", "top",
179
+ "transparent", "ultra-condensed", "ultra-expanded", "underline", "up",
180
+ "upper-alpha", "upper-armenian", "upper-greek", "upper-hexadecimal",
181
+ "upper-latin", "upper-norwegian", "upper-roman", "uppercase", "urdu", "url",
182
+ "vertical", "vertical-text", "visible", "visibleFill", "visiblePainted",
183
+ "visibleStroke", "visual", "w-resize", "wait", "wave", "white", "wider",
184
+ "window", "windowframe", "windowtext", "x-large", "x-small", "xor",
185
+ "xx-large", "xx-small", "yellow"
186
+ ]);
187
+
188
+ function keySet(array) { var keys = {}; for (var i = 0; i < array.length; ++i) keys[array[i]] = true; return keys; }
189
  function ret(style, tp) {type = tp; return style;}
190
 
191
  function tokenBase(stream, state) {
192
  var ch = stream.next();
193
+ if (ch == "@") {stream.eatWhile(/[\w\\\-]/); return ret("def", stream.current());}
194
  else if (ch == "/" && stream.eat("*")) {
195
  state.tokenize = tokenCComment;
196
  return tokenCComment(stream, state);
217
  stream.eatWhile(/[\w.%]/);
218
  return ret("number", "unit");
219
  }
220
+ else if (ch === "-") {
221
+ if (/\d/.test(stream.peek())) {
222
+ stream.eatWhile(/[\w.%]/);
223
+ return ret("number", "unit");
224
+ } else if (stream.match(/^[^-]+-/)) {
225
+ return ret("meta", "meta");
226
+ }
227
+ }
228
+ else if (/[,+>*\/]/.test(ch)) {
229
  return ret(null, "select-op");
230
  }
231
+ else if (ch == "." && stream.match(/^-?[_a-z][_a-z0-9-]*/i)) {
232
+ return ret("qualifier", "qualifier");
233
+ }
234
+ else if (ch == ":") {
235
+ return ret("operator", ch);
236
+ }
237
+ else if (/[;{}\[\]\(\)]/.test(ch)) {
238
  return ret(null, ch);
239
  }
240
+ else if (ch == "u" && stream.match("rl(")) {
241
+ stream.backUp(1);
242
+ state.tokenize = tokenParenthesized;
243
+ return ret("property", "variable");
244
+ }
245
  else {
246
  stream.eatWhile(/[\w\\\-]/);
247
+ return ret("property", "variable");
248
  }
249
  }
250
 
272
  return ret("comment", "comment");
273
  }
274
 
275
+ function tokenString(quote, nonInclusive) {
276
  return function(stream, state) {
277
  var escaped = false, ch;
278
  while ((ch = stream.next()) != null) {
280
  break;
281
  escaped = !escaped && ch == "\\";
282
  }
283
+ if (!escaped) {
284
+ if (nonInclusive) stream.backUp(1);
285
+ state.tokenize = tokenBase;
286
+ }
287
  return ret("string", "string");
288
  };
289
  }
290
 
291
+ function tokenParenthesized(stream, state) {
292
+ stream.next(); // Must be '('
293
+ if (!stream.match(/\s*[\"\']/, false))
294
+ state.tokenize = tokenString(")", true);
295
+ else
296
+ state.tokenize = tokenBase;
297
+ return ret(null, "(");
298
+ }
299
+
300
  return {
301
  startState: function(base) {
302
  return {tokenize: tokenBase,
305
  },
306
 
307
  token: function(stream, state) {
308
+
309
+ // Use these terms when applicable (see http://www.xanthir.com/blog/b4E50)
310
+ //
311
+ // rule** or **ruleset:
312
+ // A selector + braces combo, or an at-rule.
313
+ //
314
+ // declaration block:
315
+ // A sequence of declarations.
316
+ //
317
+ // declaration:
318
+ // A property + colon + value combo.
319
+ //
320
+ // property value:
321
+ // The entire value of a property.
322
+ //
323
+ // component value:
324
+ // A single piece of a property value. Like the 5px in
325
+ // text-shadow: 0 0 5px blue;. Can also refer to things that are
326
+ // multiple terms, like the 1-4 terms that make up the background-size
327
+ // portion of the background shorthand.
328
+ //
329
+ // term:
330
+ // The basic unit of author-facing CSS, like a single number (5),
331
+ // dimension (5px), string ("foo"), or function. Officially defined
332
+ // by the CSS 2.1 grammar (look for the 'term' production)
333
+ //
334
+ //
335
+ // simple selector:
336
+ // A single atomic selector, like a type selector, an attr selector, a
337
+ // class selector, etc.
338
+ //
339
+ // compound selector:
340
+ // One or more simple selectors without a combinator. div.example is
341
+ // compound, div > .example is not.
342
+ //
343
+ // complex selector:
344
+ // One or more compound selectors chained with combinators.
345
+ //
346
+ // combinator:
347
+ // The parts of selectors that express relationships. There are four
348
+ // currently - the space (descendant combinator), the greater-than
349
+ // bracket (child combinator), the plus sign (next sibling combinator),
350
+ // and the tilda (following sibling combinator).
351
+ //
352
+ // sequence of selectors:
353
+ // One or more of the named type of selector chained with commas.
354
+
355
+ if (state.tokenize == tokenBase && stream.eatSpace()) return null;
356
  var style = state.tokenize(stream, state);
357
 
358
+ // Changing style returned based on context
359
  var context = state.stack[state.stack.length-1];
360
+ if (style == "property") {
361
+ if (context == "propertyValue"){
362
+ if (valueKeywords[stream.current()]) {
363
+ style = "string-2";
364
+ } else if (colorKeywords[stream.current()]) {
365
+ style = "keyword";
366
+ } else {
367
+ style = "variable-2";
368
+ }
369
+ } else if (context == "rule") {
370
+ if (!propertyKeywords[stream.current()]) {
371
+ style += " error";
372
+ }
373
+ } else if (!context || context == "@media{") {
374
+ style = "tag";
375
+ } else if (context == "@media") {
376
+ if (atMediaTypes[stream.current()]) {
377
+ style = "attribute"; // Known attribute
378
+ } else if (/^(only|not)$/i.test(stream.current())) {
379
+ style = "keyword";
380
+ } else if (stream.current().toLowerCase() == "and") {
381
+ style = "error"; // "and" is only allowed in @mediaType
382
+ } else if (atMediaFeatures[stream.current()]) {
383
+ style = "error"; // Known property, should be in @mediaType(
384
+ } else {
385
+ // Unknown, expecting keyword or attribute, assuming attribute
386
+ style = "attribute error";
387
+ }
388
+ } else if (context == "@mediaType") {
389
+ if (atMediaTypes[stream.current()]) {
390
+ style = "attribute";
391
+ } else if (stream.current().toLowerCase() == "and") {
392
+ style = "operator";
393
+ } else if (/^(only|not)$/i.test(stream.current())) {
394
+ style = "error"; // Only allowed in @media
395
+ } else if (atMediaFeatures[stream.current()]) {
396
+ style = "error"; // Known property, should be in parentheses
397
+ } else {
398
+ // Unknown attribute or property, but expecting property (preceded
399
+ // by "and"). Should be in parentheses
400
+ style = "error";
401
+ }
402
+ } else if (context == "@mediaType(") {
403
+ if (propertyKeywords[stream.current()]) {
404
+ // do nothing, remains "property"
405
+ } else if (atMediaTypes[stream.current()]) {
406
+ style = "error"; // Known property, should be in parentheses
407
+ } else if (stream.current().toLowerCase() == "and") {
408
+ style = "operator";
409
+ } else if (/^(only|not)$/i.test(stream.current())) {
410
+ style = "error"; // Only allowed in @media
411
+ } else {
412
+ style += " error";
413
+ }
414
+ } else {
415
+ style = "error";
416
+ }
417
+ } else if (style == "atom") {
418
+ if(!context || context == "@media{") {
419
+ style = "builtin";
420
+ } else if (context == "propertyValue") {
421
+ if (!/^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/.test(stream.current())) {
422
+ style += " error";
423
+ }
424
+ } else {
425
+ style = "error";
426
+ }
427
+ } else if (context == "@media" && type == "{") {
428
+ style = "error";
429
  }
430
 
431
+ // Push/pop context stack
 
432
  if (type == "{") {
433
+ if (context == "@media" || context == "@mediaType") {
434
+ state.stack.pop();
435
+ state.stack[state.stack.length-1] = "@media{";
436
+ }
437
+ else state.stack.push("rule");
438
+ }
439
+ else if (type == "}") {
440
+ state.stack.pop();
441
+ if (context == "propertyValue") state.stack.pop();
442
  }
 
443
  else if (type == "@media") state.stack.push("@media");
444
+ else if (context == "@media" && /\b(keyword|attribute)\b/.test(style))
445
+ state.stack.push("@mediaType");
446
+ else if (context == "@mediaType" && stream.current() == ",") state.stack.pop();
447
+ else if (context == "@mediaType" && type == "(") state.stack.push("@mediaType(");
448
+ else if (context == "@mediaType(" && type == ")") state.stack.pop();
449
+ else if (context == "rule" && type == ":") state.stack.push("propertyValue");
450
+ else if (context == "propertyValue" && type == ";") state.stack.pop();
451
  return style;
452
  },
453
 
454
  indent: function(state, textAfter) {
455
  var n = state.stack.length;
456
  if (/^\}/.test(textAfter))
457
+ n -= state.stack[state.stack.length-1] == "propertyValue" ? 2 : 1;
458
  return state.baseIndent + n * indentUnit;
459
  },
460
 
lib/htmlmixed.js CHANGED
@@ -1,35 +1,36 @@
1
- CodeMirror.defineMode("htmlmixed", function(config, parserConfig) {
2
  var htmlMode = CodeMirror.getMode(config, {name: "xml", htmlMode: true});
3
  var jsMode = CodeMirror.getMode(config, "javascript");
4
  var cssMode = CodeMirror.getMode(config, "css");
5
 
6
  function html(stream, state) {
7
  var style = htmlMode.token(stream, state.htmlState);
8
- if (style == "tag" && stream.current() == ">" && state.htmlState.context) {
9
  if (/^script$/i.test(state.htmlState.context.tagName)) {
10
  state.token = javascript;
11
  state.localState = jsMode.startState(htmlMode.indent(state.htmlState, ""));
12
- state.mode = "javascript";
13
  }
14
  else if (/^style$/i.test(state.htmlState.context.tagName)) {
15
  state.token = css;
16
  state.localState = cssMode.startState(htmlMode.indent(state.htmlState, ""));
17
- state.mode = "css";
18
  }
19
  }
20
  return style;
21
  }
22
  function maybeBackup(stream, pat, style) {
23
  var cur = stream.current();
24
- var close = cur.search(pat);
25
  if (close > -1) stream.backUp(cur.length - close);
 
 
 
 
26
  return style;
27
  }
28
  function javascript(stream, state) {
29
  if (stream.match(/^<\/\s*script\s*>/i, false)) {
30
  state.token = html;
31
  state.localState = null;
32
- state.mode = "html";
33
  return html(stream, state);
34
  }
35
  return maybeBackup(stream, /<\/\s*script\s*>/,
@@ -39,7 +40,6 @@ CodeMirror.defineMode("htmlmixed", function(config, parserConfig) {
39
  if (stream.match(/^<\/\s*style\s*>/i, false)) {
40
  state.token = html;
41
  state.localState = null;
42
- state.mode = "html";
43
  return html(stream, state);
44
  }
45
  return maybeBackup(stream, /<\/\s*style\s*>/,
@@ -72,12 +72,13 @@ CodeMirror.defineMode("htmlmixed", function(config, parserConfig) {
72
  return cssMode.indent(state.localState, textAfter);
73
  },
74
 
75
- compareStates: function(a, b) {
76
- return htmlMode.compareStates(a.htmlState, b.htmlState);
77
- },
78
 
79
- electricChars: "/{}:"
80
- }
81
- });
 
 
 
82
 
83
  CodeMirror.defineMIME("text/html", "htmlmixed");
1
+ CodeMirror.defineMode("htmlmixed", function(config) {
2
  var htmlMode = CodeMirror.getMode(config, {name: "xml", htmlMode: true});
3
  var jsMode = CodeMirror.getMode(config, "javascript");
4
  var cssMode = CodeMirror.getMode(config, "css");
5
 
6
  function html(stream, state) {
7
  var style = htmlMode.token(stream, state.htmlState);
8
+ if (/(?:^|\s)tag(?:\s|$)/.test(style) && stream.current() == ">" && state.htmlState.context) {
9
  if (/^script$/i.test(state.htmlState.context.tagName)) {
10
  state.token = javascript;
11
  state.localState = jsMode.startState(htmlMode.indent(state.htmlState, ""));
 
12
  }
13
  else if (/^style$/i.test(state.htmlState.context.tagName)) {
14
  state.token = css;
15
  state.localState = cssMode.startState(htmlMode.indent(state.htmlState, ""));
 
16
  }
17
  }
18
  return style;
19
  }
20
  function maybeBackup(stream, pat, style) {
21
  var cur = stream.current();
22
+ var close = cur.search(pat), m;
23
  if (close > -1) stream.backUp(cur.length - close);
24
+ else if (m = cur.match(/<\/?$/)) {
25
+ stream.backUp(cur.length);
26
+ if (!stream.match(pat, false)) stream.match(cur[0]);
27
+ }
28
  return style;
29
  }
30
  function javascript(stream, state) {
31
  if (stream.match(/^<\/\s*script\s*>/i, false)) {
32
  state.token = html;
33
  state.localState = null;
 
34
  return html(stream, state);
35
  }
36
  return maybeBackup(stream, /<\/\s*script\s*>/,
40
  if (stream.match(/^<\/\s*style\s*>/i, false)) {
41
  state.token = html;
42
  state.localState = null;
 
43
  return html(stream, state);
44
  }
45
  return maybeBackup(stream, /<\/\s*style\s*>/,
72
  return cssMode.indent(state.localState, textAfter);
73
  },
74
 
75
+ electricChars: "/{}:",
 
 
76
 
77
+ innerMode: function(state) {
78
+ var mode = state.token == html ? htmlMode : state.token == javascript ? jsMode : cssMode;
79
+ return {state: state.localState || state.htmlState, mode: mode};
80
+ }
81
+ };
82
+ }, "xml", "javascript", "css");
83
 
84
  CodeMirror.defineMIME("text/html", "htmlmixed");
lib/javascript.js CHANGED
@@ -1,6 +1,9 @@
 
 
1
  CodeMirror.defineMode("javascript", function(config, parserConfig) {
2
  var indentUnit = config.indentUnit;
3
  var jsonMode = parserConfig.json;
 
4
 
5
  // Tokenizer
6
 
@@ -8,7 +11,8 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
8
  function kw(type) {return {type: type, style: "keyword"};}
9
  var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c");
10
  var operator = kw("operator"), atom = {type: "atom", style: "atom"};
11
- return {
 
12
  "if": A, "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B,
13
  "return": C, "break": C, "continue": C, "new": C, "delete": C, "throw": C,
14
  "var": kw("var"), "const": kw("var"), "let": kw("var"),
@@ -17,6 +21,35 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
17
  "in": operator, "typeof": operator, "instanceof": operator,
18
  "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom
19
  };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  }();
21
 
22
  var isOperatorChar = /[+\-*&%=<>!?|]/;
@@ -54,7 +87,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
54
  stream.eatWhile(/[\da-f]/i);
55
  return ret("number", "number");
56
  }
57
- else if (/\d/.test(ch)) {
58
  stream.match(/^\d*(?:\.\d*)?(?:[eE][+\-]?\d+)?/);
59
  return ret("number", "number");
60
  }
@@ -66,7 +99,8 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
66
  stream.skipToEnd();
67
  return ret("comment", "comment");
68
  }
69
- else if (state.reAllowed) {
 
70
  nextUntilUnescaped(stream, "/");
71
  stream.eatWhile(/[gimy]/); // 'y' is "sticky" option in Mozilla
72
  return ret("regexp", "string-2");
@@ -87,7 +121,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
87
  else {
88
  stream.eatWhile(/[\w\$_]/);
89
  var word = stream.current(), known = keywords.propertyIsEnumerable(word) && keywords[word];
90
- return (known && state.kwAllowed) ? ret(known.type, known.style, word) :
91
  ret("variable", "variable", word);
92
  }
93
  }
@@ -162,12 +196,19 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
162
  return true;
163
  }
164
  function register(varname) {
 
 
 
 
 
165
  var state = cx.state;
166
  if (state.context) {
167
  cx.marked = "def";
168
- for (var v = state.localVars; v; v = v.next)
169
- if (v.name == varname) return;
170
  state.localVars = {name: varname, next: state.localVars};
 
 
 
171
  }
172
  }
173
 
@@ -175,8 +216,8 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
175
 
176
  var defaultVars = {name: "this", next: {name: "arguments"}};
177
  function pushcontext() {
178
- if (!cx.state.context) cx.state.localVars = defaultVars;
179
  cx.state.context = {prev: cx.state.context, vars: cx.state.localVars};
 
180
  }
181
  function popcontext() {
182
  cx.state.localVars = cx.state.context.vars;
@@ -185,7 +226,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
185
  function pushlex(type, info) {
186
  var result = function() {
187
  var state = cx.state;
188
- state.lexical = new JSLexical(state.indented, cx.stream.column(), type, null, state.lexical, info)
189
  };
190
  result.lex = true;
191
  return result;
@@ -243,7 +284,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
243
 
244
  function maybeoperator(type, value) {
245
  if (type == "operator" && /\+\+|--/.test(value)) return cont(maybeoperator);
246
- if (type == "operator") return cont(expression);
247
  if (type == ";") return;
248
  if (type == "(") return cont(pushlex(")"), commasep(expression, ")"), poplex, maybeoperator);
249
  if (type == ".") return cont(property, maybeoperator);
@@ -275,21 +316,32 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
275
  if (type == "}") return cont();
276
  return pass(statement, block);
277
  }
 
 
 
 
 
 
 
 
278
  function vardef1(type, value) {
279
- if (type == "variable"){register(value); return cont(vardef2);}
280
- return cont();
 
 
 
281
  }
282
  function vardef2(type, value) {
283
  if (value == "=") return cont(expression, vardef2);
284
  if (type == ",") return cont(vardef1);
285
  }
286
  function forspec1(type) {
287
- if (type == "var") return cont(vardef1, forspec2);
288
- if (type == ";") return pass(forspec2);
289
  if (type == "variable") return cont(formaybein);
290
- return pass(forspec2);
291
  }
292
- function formaybein(type, value) {
293
  if (value == "in") return cont(expression);
294
  return cont(maybeoperator, forspec2);
295
  }
@@ -306,7 +358,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
306
  if (type == "(") return cont(pushlex(")"), pushcontext, commasep(funarg, ")"), poplex, statement, popcontext);
307
  }
308
  function funarg(type, value) {
309
- if (type == "variable") {register(value); return cont();}
310
  }
311
 
312
  // Interface
@@ -315,11 +367,11 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
315
  startState: function(basecolumn) {
316
  return {
317
  tokenize: jsTokenBase,
318
- reAllowed: true,
319
- kwAllowed: true,
320
  cc: [],
321
  lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false),
322
  localVars: parserConfig.localVars,
 
323
  context: parserConfig.localVars && {vars: parserConfig.localVars},
324
  indented: 0
325
  };
@@ -334,27 +386,37 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
334
  if (stream.eatSpace()) return null;
335
  var style = state.tokenize(stream, state);
336
  if (type == "comment") return style;
337
- state.reAllowed = !!(type == "operator" || type == "keyword c" || type.match(/^[\[{}\(,;:]$/));
338
- state.kwAllowed = type != '.';
339
  return parseJS(state, style, type, content, stream);
340
  },
341
 
342
  indent: function(state, textAfter) {
 
343
  if (state.tokenize != jsTokenBase) return 0;
344
- var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical,
345
- type = lexical.type, closing = firstChar == type;
346
- if (type == "vardef") return lexical.indented + 4;
 
347
  else if (type == "form" && firstChar == "{") return lexical.indented;
348
- else if (type == "stat" || type == "form") return lexical.indented + indentUnit;
 
 
349
  else if (lexical.info == "switch" && !closing)
350
  return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit);
351
  else if (lexical.align) return lexical.column + (closing ? 0 : 1);
352
  else return lexical.indented + (closing ? 0 : indentUnit);
353
  },
354
 
355
- electricChars: ":{}"
 
 
356
  };
357
  });
358
 
359
  CodeMirror.defineMIME("text/javascript", "javascript");
 
 
 
360
  CodeMirror.defineMIME("application/json", {name: "javascript", json: true});
 
 
1
+ // TODO actually recognize syntax of TypeScript constructs
2
+
3
  CodeMirror.defineMode("javascript", function(config, parserConfig) {
4
  var indentUnit = config.indentUnit;
5
  var jsonMode = parserConfig.json;
6
+ var isTS = parserConfig.typescript;
7
 
8
  // Tokenizer
9
 
11
  function kw(type) {return {type: type, style: "keyword"};}
12
  var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c");
13
  var operator = kw("operator"), atom = {type: "atom", style: "atom"};
14
+
15
+ var jsKeywords = {
16
  "if": A, "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B,
17
  "return": C, "break": C, "continue": C, "new": C, "delete": C, "throw": C,
18
  "var": kw("var"), "const": kw("var"), "let": kw("var"),
21
  "in": operator, "typeof": operator, "instanceof": operator,
22
  "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom
23
  };
24
+
25
+ // Extend the 'normal' keywords with the TypeScript language extensions
26
+ if (isTS) {
27
+ var type = {type: "variable", style: "variable-3"};
28
+ var tsKeywords = {
29
+ // object-like things
30
+ "interface": kw("interface"),
31
+ "class": kw("class"),
32
+ "extends": kw("extends"),
33
+ "constructor": kw("constructor"),
34
+
35
+ // scope modifiers
36
+ "public": kw("public"),
37
+ "private": kw("private"),
38
+ "protected": kw("protected"),
39
+ "static": kw("static"),
40
+
41
+ "super": kw("super"),
42
+
43
+ // types
44
+ "string": type, "number": type, "bool": type, "any": type
45
+ };
46
+
47
+ for (var attr in tsKeywords) {
48
+ jsKeywords[attr] = tsKeywords[attr];
49
+ }
50
+ }
51
+
52
+ return jsKeywords;
53
  }();
54
 
55
  var isOperatorChar = /[+\-*&%=<>!?|]/;
87
  stream.eatWhile(/[\da-f]/i);
88
  return ret("number", "number");
89
  }
90
+ else if (/\d/.test(ch) || ch == "-" && stream.eat(/\d/)) {
91
  stream.match(/^\d*(?:\.\d*)?(?:[eE][+\-]?\d+)?/);
92
  return ret("number", "number");
93
  }
99
  stream.skipToEnd();
100
  return ret("comment", "comment");
101
  }
102
+ else if (state.lastType == "operator" || state.lastType == "keyword c" ||
103
+ /^[\[{}\(,;:]$/.test(state.lastType)) {
104
  nextUntilUnescaped(stream, "/");
105
  stream.eatWhile(/[gimy]/); // 'y' is "sticky" option in Mozilla
106
  return ret("regexp", "string-2");
121
  else {
122
  stream.eatWhile(/[\w\$_]/);
123
  var word = stream.current(), known = keywords.propertyIsEnumerable(word) && keywords[word];
124
+ return (known && state.lastType != ".") ? ret(known.type, known.style, word) :
125
  ret("variable", "variable", word);
126
  }
127
  }
196
  return true;
197
  }
198
  function register(varname) {
199
+ function inList(list) {
200
+ for (var v = list; v; v = v.next)
201
+ if (v.name == varname) return true;
202
+ return false;
203
+ }
204
  var state = cx.state;
205
  if (state.context) {
206
  cx.marked = "def";
207
+ if (inList(state.localVars)) return;
 
208
  state.localVars = {name: varname, next: state.localVars};
209
+ } else {
210
+ if (inList(state.globalVars)) return;
211
+ state.globalVars = {name: varname, next: state.globalVars};
212
  }
213
  }
214
 
216
 
217
  var defaultVars = {name: "this", next: {name: "arguments"}};
218
  function pushcontext() {
 
219
  cx.state.context = {prev: cx.state.context, vars: cx.state.localVars};
220
+ cx.state.localVars = defaultVars;
221
  }
222
  function popcontext() {
223
  cx.state.localVars = cx.state.context.vars;
226
  function pushlex(type, info) {
227
  var result = function() {
228
  var state = cx.state;
229
+ state.lexical = new JSLexical(state.indented, cx.stream.column(), type, null, state.lexical, info);
230
  };
231
  result.lex = true;
232
  return result;
284
 
285
  function maybeoperator(type, value) {
286
  if (type == "operator" && /\+\+|--/.test(value)) return cont(maybeoperator);
287
+ if (type == "operator" && value == "?") return cont(expression, expect(":"), expression);
288
  if (type == ";") return;
289
  if (type == "(") return cont(pushlex(")"), commasep(expression, ")"), poplex, maybeoperator);
290
  if (type == ".") return cont(property, maybeoperator);
316
  if (type == "}") return cont();
317
  return pass(statement, block);
318
  }
319
+ function maybetype(type) {
320
+ if (type == ":") return cont(typedef);
321
+ return pass();
322
+ }
323
+ function typedef(type) {
324
+ if (type == "variable"){cx.marked = "variable-3"; return cont();}
325
+ return pass();
326
+ }
327
  function vardef1(type, value) {
328
+ if (type == "variable") {
329
+ register(value);
330
+ return isTS ? cont(maybetype, vardef2) : cont(vardef2);
331
+ }
332
+ return pass();
333
  }
334
  function vardef2(type, value) {
335
  if (value == "=") return cont(expression, vardef2);
336
  if (type == ",") return cont(vardef1);
337
  }
338
  function forspec1(type) {
339
+ if (type == "var") return cont(vardef1, expect(";"), forspec2);
340
+ if (type == ";") return cont(forspec2);
341
  if (type == "variable") return cont(formaybein);
342
+ return cont(forspec2);
343
  }
344
+ function formaybein(_type, value) {
345
  if (value == "in") return cont(expression);
346
  return cont(maybeoperator, forspec2);
347
  }
358
  if (type == "(") return cont(pushlex(")"), pushcontext, commasep(funarg, ")"), poplex, statement, popcontext);
359
  }
360
  function funarg(type, value) {
361
+ if (type == "variable") {register(value); return isTS ? cont(maybetype) : cont();}
362
  }
363
 
364
  // Interface
367
  startState: function(basecolumn) {
368
  return {
369
  tokenize: jsTokenBase,
370
+ lastType: null,
 
371
  cc: [],
372
  lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false),
373
  localVars: parserConfig.localVars,
374
+ globalVars: parserConfig.globalVars,
375
  context: parserConfig.localVars && {vars: parserConfig.localVars},
376
  indented: 0
377
  };
386
  if (stream.eatSpace()) return null;
387
  var style = state.tokenize(stream, state);
388
  if (type == "comment") return style;
389
+ state.lastType = type;
 
390
  return parseJS(state, style, type, content, stream);
391
  },
392
 
393
  indent: function(state, textAfter) {
394
+ if (state.tokenize == jsTokenComment) return CodeMirror.Pass;
395
  if (state.tokenize != jsTokenBase) return 0;
396
+ var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical;
397
+ if (lexical.type == "stat" && firstChar == "}") lexical = lexical.prev;
398
+ var type = lexical.type, closing = firstChar == type;
399
+ if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? 4 : 0);
400
  else if (type == "form" && firstChar == "{") return lexical.indented;
401
+ else if (type == "form") return lexical.indented + indentUnit;
402
+ else if (type == "stat")
403
+ return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? indentUnit : 0);
404
  else if (lexical.info == "switch" && !closing)
405
  return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit);
406
  else if (lexical.align) return lexical.column + (closing ? 0 : 1);
407
  else return lexical.indented + (closing ? 0 : indentUnit);
408
  },
409
 
410
+ electricChars: ":{}",
411
+
412
+ jsonMode: jsonMode
413
  };
414
  });
415
 
416
  CodeMirror.defineMIME("text/javascript", "javascript");
417
+ CodeMirror.defineMIME("text/ecmascript", "javascript");
418
+ CodeMirror.defineMIME("application/javascript", "javascript");
419
+ CodeMirror.defineMIME("application/ecmascript", "javascript");
420
  CodeMirror.defineMIME("application/json", {name: "javascript", json: true});
421
+ CodeMirror.defineMIME("text/typescript", { name: "javascript", typescript: true });
422
+ CodeMirror.defineMIME("application/typescript", { name: "javascript", typescript: true });
lib/util/active-line.js ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Because sometimes you need to style the cursor's line.
2
+ //
3
+ // Adds an option 'styleActiveLine' which, when enabled, gives the
4
+ // active line's wrapping <div> the CSS class "CodeMirror-activeline",
5
+ // and gives its background <div> the class "CodeMirror-activeline-background".
6
+
7
+ (function() {
8
+ "use strict";
9
+ var WRAP_CLASS = "CodeMirror-activeline";
10
+ var BACK_CLASS = "CodeMirror-activeline-background";
11
+
12
+ CodeMirror.defineOption("styleActiveLine", false, function(cm, val, old) {
13
+ var prev = old && old != CodeMirror.Init;
14
+ if (val && !prev) {
15
+ updateActiveLine(cm);
16
+ cm.on("cursorActivity", updateActiveLine);
17
+ } else if (!val && prev) {
18
+ cm.off("cursorActivity", updateActiveLine);
19
+ clearActiveLine(cm);
20
+ delete cm._activeLine;
21
+ }
22
+ });
23
+
24
+ function clearActiveLine(cm) {
25
+ if ("_activeLine" in cm) {
26
+ cm.removeLineClass(cm._activeLine, "wrap", WRAP_CLASS);
27
+ cm.removeLineClass(cm._activeLine, "background", BACK_CLASS);
28
+ }
29
+ }
30
+
31
+ function updateActiveLine(cm) {
32
+ var line = cm.getLineHandle(cm.getCursor().line);
33
+ if (cm._activeLine == line) return;
34
+ clearActiveLine(cm);
35
+ cm.addLineClass(line, "wrap", WRAP_CLASS);
36
+ cm.addLineClass(line, "background", BACK_CLASS);
37
+ cm._activeLine = line;
38
+ }
39
+ })();
lib/util/closetag.js DELETED
@@ -1,174 +0,0 @@
1
- /**
2
- * Tag-closer extension for CodeMirror.
3
- *
4
- * This extension adds a "closeTag" utility function that can be used with key bindings to
5
- * insert a matching end tag after the ">" character of a start tag has been typed. It can
6
- * also complete "</" if a matching start tag is found. It will correctly ignore signal
7
- * characters for empty tags, comments, CDATA, etc.
8
- *
9
- * The function depends on internal parser state to identify tags. It is compatible with the
10
- * following CodeMirror modes and will ignore all others:
11
- * - htmlmixed
12
- * - xml
13
- * - xmlpure
14
- *
15
- * See demos/closetag.html for a usage example.
16
- *
17
- * @author Nathan Williams <nathan@nlwillia.net>
18
- * Contributed under the same license terms as CodeMirror.
19
- */
20
- (function() {
21
- /** Option that allows tag closing behavior to be toggled. Default is true. */
22
- CodeMirror.defaults['closeTagEnabled'] = true;
23
-
24
- /** Array of tag names to add indentation after the start tag for. Default is the list of block-level html tags. */
25
- CodeMirror.defaults['closeTagIndent'] = ['applet', 'blockquote', 'body', 'button', 'div', 'dl', 'fieldset', 'form', 'frameset', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'html', 'iframe', 'layer', 'legend', 'object', 'ol', 'p', 'select', 'table', 'ul'];
26
-
27
- /**
28
- * Call during key processing to close tags. Handles the key event if the tag is closed, otherwise throws CodeMirror.Pass.
29
- * - cm: The editor instance.
30
- * - ch: The character being processed.
31
- * - indent: Optional. Omit or pass true to use the default indentation tag list defined in the 'closeTagIndent' option.
32
- * Pass false to disable indentation. Pass an array to override the default list of tag names.
33
- */
34
- CodeMirror.defineExtension("closeTag", function(cm, ch, indent) {
35
- if (!cm.getOption('closeTagEnabled')) {
36
- throw CodeMirror.Pass;
37
- }
38
-
39
- var mode = cm.getOption('mode');
40
-
41
- if (mode == 'text/html') {
42
-
43
- /*
44
- * Relevant structure of token:
45
- *
46
- * htmlmixed
47
- * className
48
- * state
49
- * htmlState
50
- * type
51
- * context
52
- * tagName
53
- * mode
54
- *
55
- * xml
56
- * className
57
- * state
58
- * tagName
59
- * type
60
- */
61
-
62
- var pos = cm.getCursor();
63
- var tok = cm.getTokenAt(pos);
64
- var state = tok.state;
65
-
66
- if (state.mode && state.mode != 'html') {
67
- throw CodeMirror.Pass; // With htmlmixed, we only care about the html sub-mode.
68
- }
69
-
70
- if (ch == '>') {
71
- var type = state.htmlState ? state.htmlState.type : state.type; // htmlmixed : xml
72
-
73
- if (tok.className == 'tag' && type == 'closeTag') {
74
- throw CodeMirror.Pass; // Don't process the '>' at the end of an end-tag.
75
- }
76
-
77
- cm.replaceSelection('>'); // Mode state won't update until we finish the tag.
78
- pos = {line: pos.line, ch: pos.ch + 1};
79
- cm.setCursor(pos);
80
-
81
- tok = cm.getTokenAt(cm.getCursor());
82
- state = tok.state;
83
- type = state.htmlState ? state.htmlState.type : state.type; // htmlmixed : xml
84
-
85
- if (tok.className == 'tag' && type != 'selfcloseTag') {
86
- var tagName = state.htmlState ? state.htmlState.context.tagName : state.tagName; // htmlmixed : xml
87
- if (tagName.length > 0) {
88
- insertEndTag(cm, indent, pos, tagName);
89
- }
90
- return;
91
- }
92
-
93
- // Undo the '>' insert and allow cm to handle the key instead.
94
- cm.setSelection({line: pos.line, ch: pos.ch - 1}, pos);
95
- cm.replaceSelection("");
96
-
97
- } else if (ch == '/') {
98
- if (tok.className == 'tag' && tok.string == '<') {
99
- var tagName = state.htmlState ? (state.htmlState.context ? state.htmlState.context.tagName : '') : state.context.tagName; // htmlmixed : xml # extra htmlmized check is for '</' edge case
100
- if (tagName.length > 0) {
101
- completeEndTag(cm, pos, tagName);
102
- return;
103
- }
104
- }
105
- }
106
-
107
- } else if (mode == 'xmlpure') {
108
-
109
- var pos = cm.getCursor();
110
- var tok = cm.getTokenAt(pos);
111
- var tagName = tok.state.context.tagName;
112
-
113
- if (ch == '>') {
114
- // <foo> tagName=foo, string=foo
115
- // <foo /> tagName=foo, string=/ # ignore
116
- // <foo></foo> tagName=foo, string=/foo # ignore
117
- if (tok.string == tagName) {
118
- cm.replaceSelection('>'); // parity w/html modes
119
- pos = {line: pos.line, ch: pos.ch + 1};
120
- cm.setCursor(pos);
121
-
122
- insertEndTag(cm, indent, pos, tagName);
123
- return;
124
- }
125
-
126
- } else if (ch == '/') {
127
- // <foo / tagName=foo, string= # ignore
128
- // <foo></ tagName=foo, string=<
129
- if (tok.string == '<') {
130
- completeEndTag(cm, pos, tagName);
131
- return;
132
- }
133
- }
134
- }
135
-
136
- throw CodeMirror.Pass; // Bubble if not handled
137
- });
138
-
139
- function insertEndTag(cm, indent, pos, tagName) {
140
- if (shouldIndent(cm, indent, tagName)) {
141
- cm.replaceSelection('\n\n</' + tagName + '>', 'end');
142
- cm.indentLine(pos.line + 1);
143
- cm.indentLine(pos.line + 2);
144
- cm.setCursor({line: pos.line + 1, ch: cm.getLine(pos.line + 1).length});
145
- } else {
146
- cm.replaceSelection('</' + tagName + '>');
147
- cm.setCursor(pos);
148
- }
149
- }
150
-
151
- function shouldIndent(cm, indent, tagName) {
152
- if (typeof indent == 'undefined' || indent == null || indent == true) {
153
- indent = cm.getOption('closeTagIndent');
154
- }
155
- if (!indent) {
156
- indent = [];
157
- }
158
- return indexOf(indent, tagName.toLowerCase()) != -1;
159
- }
160
-
161
- // C&P from codemirror.js...would be nice if this were visible to utilities.
162
- function indexOf(collection, elt) {
163
- if (collection.indexOf) return collection.indexOf(elt);
164
- for (var i = 0, e = collection.length; i < e; ++i)
165
- if (collection[i] == elt) return i;
166
- return -1;
167
- }
168
-
169
- function completeEndTag(cm, pos, tagName) {
170
- cm.replaceSelection('/' + tagName + '>');
171
- cm.setCursor({line: pos.line, ch: pos.ch + tagName.length + 2 });
172
- }
173
-
174
- })();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/util/dialog.css DELETED
@@ -1,23 +0,0 @@
1
- .CodeMirror-dialog {
2
- position: relative;
3
- }
4
-
5
- .CodeMirror-dialog > div {
6
- position: absolute;
7
- top: 0; left: 0; right: 0;
8
- background: white;
9
- border-bottom: 1px solid #eee;
10
- z-index: 15;
11
- padding: .1em .8em;
12
- overflow: hidden;
13
- color: #333;
14
- }
15
-
16
- .CodeMirror-dialog input {
17
- border: none;
18
- outline: none;
19
- background: transparent;
20
- width: 20em;
21
- color: inherit;
22
- font-family: monospace;
23
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/util/dialog.js DELETED
@@ -1,63 +0,0 @@
1
- // Open simple dialogs on top of an editor. Relies on dialog.css.
2
-
3
- (function() {
4
- function dialogDiv(cm, template) {
5
- var wrap = cm.getWrapperElement();
6
- var dialog = wrap.insertBefore(document.createElement("div"), wrap.firstChild);
7
- dialog.className = "CodeMirror-dialog";
8
- dialog.innerHTML = '<div>' + template + '</div>';
9
- return dialog;
10
- }
11
-
12
- CodeMirror.defineExtension("openDialog", function(template, callback) {
13
- var dialog = dialogDiv(this, template);
14
- var closed = false, me = this;
15
- function close() {
16
- if (closed) return;
17
- closed = true;
18
- dialog.parentNode.removeChild(dialog);
19
- }
20
- var inp = dialog.getElementsByTagName("input")[0];
21
- if (inp) {
22
- CodeMirror.connect(inp, "keydown", function(e) {
23
- if (e.keyCode == 13 || e.keyCode == 27) {
24
- CodeMirror.e_stop(e);
25
- close();
26
- me.focus();
27
- if (e.keyCode == 13) callback(inp.value);
28
- }
29
- });
30
- inp.focus();
31
- CodeMirror.connect(inp, "blur", close);
32
- }
33
- return close;
34
- });
35
-
36
- CodeMirror.defineExtension("openConfirm", function(template, callbacks) {
37
- var dialog = dialogDiv(this, template);
38
- var buttons = dialog.getElementsByTagName("button");
39
- var closed = false, me = this, blurring = 1;
40
- function close() {
41
- if (closed) return;
42
- closed = true;
43
- dialog.parentNode.removeChild(dialog);
44
- me.focus();
45
- }
46
- buttons[0].focus();
47
- for (var i = 0; i < buttons.length; ++i) {
48
- var b = buttons[i];
49
- (function(callback) {
50
- CodeMirror.connect(b, "click", function(e) {
51
- CodeMirror.e_preventDefault(e);
52
- close();
53
- if (callback) callback(me);
54
- });
55
- })(callbacks[i]);
56
- CodeMirror.connect(b, "blur", function() {
57
- --blurring;
58
- setTimeout(function() { if (blurring <= 0) close(); }, 200);
59
- });
60
- CodeMirror.connect(b, "focus", function() { ++blurring; });
61
- }
62
- });
63
- })();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/util/foldcode.js DELETED
@@ -1,186 +0,0 @@
1
- // the tagRangeFinder function is
2
- // Copyright (C) 2011 by Daniel Glazman <daniel@glazman.org>
3
- // released under the MIT license (../../LICENSE) like the rest of CodeMirror
4
- CodeMirror.tagRangeFinder = function(cm, line) {
5
- var nameStartChar = "A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD";
6
- var nameChar = nameStartChar + "\-\.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040";
7
- var xmlNAMERegExp = new RegExp("^[" + nameStartChar + "][" + nameChar + "]*");
8
-
9
- var lineText = cm.getLine(line);
10
- var found = false;
11
- var tag = null;
12
- var pos = 0;
13
- while (!found) {
14
- pos = lineText.indexOf("<", pos);
15
- if (-1 == pos) // no tag on line
16
- return;
17
- if (pos + 1 < lineText.length && lineText[pos + 1] == "/") { // closing tag
18
- pos++;
19
- continue;
20
- }
21
- // ok we weem to have a start tag
22
- if (!lineText.substr(pos + 1).match(xmlNAMERegExp)) { // not a tag name...
23
- pos++;
24
- continue;
25
- }
26
- var gtPos = lineText.indexOf(">", pos + 1);
27
- if (-1 == gtPos) { // end of start tag not in line
28
- var l = line + 1;
29
- var foundGt = false;
30
- var lastLine = cm.lineCount();
31
- while (l < lastLine && !foundGt) {
32
- var lt = cm.getLine(l);
33
- var gt = lt.indexOf(">");
34
- if (-1 != gt) { // found a >
35
- foundGt = true;
36
- var slash = lt.lastIndexOf("/", gt);
37
- if (-1 != slash && slash < gt) {
38
- var str = lineText.substr(slash, gt - slash + 1);
39
- if (!str.match( /\/\s*\>/ )) // yep, that's the end of empty tag
40
- return l+1;
41
- }
42
- }
43
- l++;
44
- }
45
- found = true;
46
- }
47
- else {
48
- var slashPos = lineText.lastIndexOf("/", gtPos);
49
- if (-1 == slashPos) { // cannot be empty tag
50
- found = true;
51
- // don't continue
52
- }
53
- else { // empty tag?
54
- // check if really empty tag
55
- var str = lineText.substr(slashPos, gtPos - slashPos + 1);
56
- if (!str.match( /\/\s*\>/ )) { // finally not empty
57
- found = true;
58
- // don't continue
59
- }
60
- }
61
- }
62
- if (found) {
63
- var subLine = lineText.substr(pos + 1);
64
- tag = subLine.match(xmlNAMERegExp);
65
- if (tag) {
66
- // we have an element name, wooohooo !
67
- tag = tag[0];
68
- // do we have the close tag on same line ???
69
- if (-1 != lineText.indexOf("</" + tag + ">", pos)) // yep
70
- {
71
- found = false;
72
- }
73
- // we don't, so we have a candidate...
74
- }
75
- else
76
- found = false;
77
- }
78
- if (!found)
79
- pos++;
80
- }
81
-
82
- if (found) {
83
- var startTag = "(\\<\\/" + tag + "\\>)|(\\<" + tag + "\\>)|(\\<" + tag + "\\s)|(\\<" + tag + "$)";
84
- var startTagRegExp = new RegExp(startTag, "g");
85
- var endTag = "</" + tag + ">";
86
- var depth = 1;
87
- var l = line + 1;
88
- var lastLine = cm.lineCount();
89
- while (l < lastLine) {
90
- lineText = cm.getLine(l);
91
- var match = lineText.match(startTagRegExp);
92
- if (match) {
93
- for (var i = 0; i < match.length; i++) {
94
- if (match[i] == endTag)
95
- depth--;
96
- else
97
- depth++;
98
- if (!depth)
99
- return l+1;
100
- }
101
- }
102
- l++;
103
- }
104
- return;
105
- }
106
- };
107
-
108
- CodeMirror.braceRangeFinder = function(cm, line) {
109
- var lineText = cm.getLine(line);
110
- var startChar = lineText.lastIndexOf("{");
111
- if (startChar < 0 || lineText.lastIndexOf("}") > startChar) return;
112
- var tokenType = cm.getTokenAt({line: line, ch: startChar}).className;
113
- var count = 1, lastLine = cm.lineCount(), end;
114
- outer: for (var i = line + 1; i < lastLine; ++i) {
115
- var text = cm.getLine(i), pos = 0;
116
- for (;;) {
117
- var nextOpen = text.indexOf("{", pos), nextClose = text.indexOf("}", pos);
118
- if (nextOpen < 0) nextOpen = text.length;
119
- if (nextClose < 0) nextClose = text.length;
120
- pos = Math.min(nextOpen, nextClose);
121
- if (pos == text.length) break;
122
- if (cm.getTokenAt({line: i, ch: pos + 1}).className == tokenType) {
123
- if (pos == nextOpen) ++count;
124
- else if (!--count) { end = i; break outer; }
125
- }
126
- ++pos;
127
- }
128
- }
129
- if (end == null || end == line + 1) return;
130
- return end;
131
- };
132
-
133
- CodeMirror.indentRangeFinder = function(cm, line) {
134
- var tabSize = cm.getOption("tabSize");
135
- var myIndent = cm.getLineHandle(line).indentation(tabSize), last;
136
- for (var i = line + 1, end = cm.lineCount(); i < end; ++i) {
137
- var handle = cm.getLineHandle(i);
138
- if (!/^\s*$/.test(handle.text)) {
139
- if (handle.indentation(tabSize) <= myIndent) break;
140
- last = i;
141
- }
142
- }
143
- if (!last) return null;
144
- return last + 1;
145
- };
146
-
147
- CodeMirror.newFoldFunction = function(rangeFinder, markText) {
148
- var folded = [];
149
- if (markText == null) markText = '<div style="position: absolute; left: 2px; color:#600">&#x25bc;</div>%N%';
150
-
151
- function isFolded(cm, n) {
152
- for (var i = 0; i < folded.length; ++i) {
153
- var start = cm.lineInfo(folded[i].start);
154
- if (!start) folded.splice(i--, 1);
155
- else if (start.line == n) return {pos: i, region: folded[i]};
156
- }
157
- }
158
-
159
- function expand(cm, region) {
160
- cm.clearMarker(region.start);
161
- for (var i = 0; i < region.hidden.length; ++i)
162
- cm.showLine(region.hidden[i]);
163
- }
164
-
165
- return function(cm, line) {
166
- cm.operation(function() {
167
- var known = isFolded(cm, line);
168
- if (known) {
169
- folded.splice(known.pos, 1);
170
- expand(cm, known.region);
171
- } else {
172
- var end = rangeFinder(cm, line);
173
- if (end == null) return;
174
- var hidden = [];
175
- for (var i = line + 1; i < end; ++i) {
176
- var handle = cm.hideLine(i);
177
- if (handle) hidden.push(handle);
178
- }
179
- var first = cm.setMarker(line, markText);
180
- var region = {start: first, hidden: hidden};
181
- cm.onDeleteLine(first, function() { expand(cm, region); });
182
- folded.push(region);
183
- }
184
- });
185
- };
186
- };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/util/formatting.js CHANGED
@@ -1,294 +1,114 @@
1
- // ============== Formatting extensions ============================
2
- // A common storage for all mode-specific formatting features
3
- if (!CodeMirror.modeExtensions) CodeMirror.modeExtensions = {};
4
 
5
- // Returns the extension of the editor's current mode
6
- CodeMirror.defineExtension("getModeExt", function () {
7
- var mname = CodeMirror.resolveMode(this.getOption("mode")).name;
8
- var ext = CodeMirror.modeExtensions[mname];
9
- if (!ext) throw new Error("No extensions found for mode " + mname);
10
- return ext;
11
- });
12
-
13
- // If the current mode is 'htmlmixed', returns the extension of a mode located at
14
- // the specified position (can be htmlmixed, css or javascript). Otherwise, simply
15
- // returns the extension of the editor's current mode.
16
- CodeMirror.defineExtension("getModeExtAtPos", function (pos) {
17
- var token = this.getTokenAt(pos);
18
- if (token && token.state && token.state.mode)
19
- return CodeMirror.modeExtensions[token.state.mode == "html" ? "htmlmixed" : token.state.mode];
20
- else
21
- return this.getModeExt();
22
- });
23
-
24
- // Comment/uncomment the specified range
25
- CodeMirror.defineExtension("commentRange", function (isComment, from, to) {
26
- var curMode = this.getModeExtAtPos(this.getCursor());
27
- if (isComment) { // Comment range
28
- var commentedText = this.getRange(from, to);
29
- this.replaceRange(curMode.commentStart + this.getRange(from, to) + curMode.commentEnd
30
- , from, to);
31
- if (from.line == to.line && from.ch == to.ch) { // An empty comment inserted - put cursor inside
32
- this.setCursor(from.line, from.ch + curMode.commentStart.length);
33
- }
34
- }
35
- else { // Uncomment range
36
- var selText = this.getRange(from, to);
37
- var startIndex = selText.indexOf(curMode.commentStart);
38
- var endIndex = selText.lastIndexOf(curMode.commentEnd);
39
- if (startIndex > -1 && endIndex > -1 && endIndex > startIndex) {
40
- // Take string till comment start
41
- selText = selText.substr(0, startIndex)
42
- // From comment start till comment end
43
- + selText.substring(startIndex + curMode.commentStart.length, endIndex)
44
- // From comment end till string end
45
- + selText.substr(endIndex + curMode.commentEnd.length);
46
  }
47
- this.replaceRange(selText, from, to);
48
- }
49
- });
50
 
51
- // Applies automatic mode-aware indentation to the specified range
52
- CodeMirror.defineExtension("autoIndentRange", function (from, to) {
53
- var cmInstance = this;
54
- this.operation(function () {
55
- for (var i = from.line; i <= to.line; i++) {
56
- cmInstance.indentLine(i, "smart");
 
 
 
 
 
57
  }
58
  });
59
- });
60
 
61
- // Applies automatic formatting to the specified range
62
- CodeMirror.defineExtension("autoFormatRange", function (from, to) {
63
- var absStart = this.indexFromPos(from);
64
- var absEnd = this.indexFromPos(to);
65
- // Insert additional line breaks where necessary according to the
66
- // mode's syntax
67
- var res = this.getModeExt().autoFormatLineBreaks(this.getValue(), absStart, absEnd);
68
- var cmInstance = this;
69
-
70
- // Replace and auto-indent the range
71
- this.operation(function () {
72
- cmInstance.replaceRange(res, from, to);
73
- var startLine = cmInstance.posFromIndex(absStart).line;
74
- var endLine = cmInstance.posFromIndex(absStart + res.length).line;
75
- for (var i = startLine; i <= endLine; i++) {
76
- cmInstance.indentLine(i, "smart");
77
  }
78
  });
79
- });
80
-
81
- // Define extensions for a few modes
82
-
83
- CodeMirror.modeExtensions["css"] = {
84
- commentStart: "/*",
85
- commentEnd: "*/",
86
- wordWrapChars: [";", "\\{", "\\}"],
87
- autoFormatLineBreaks: function (text) {
88
- return text.replace(new RegExp("(;|\\{|\\})([^\r\n])", "g"), "$1\n$2");
89
- }
90
- };
91
 
92
- CodeMirror.modeExtensions["javascript"] = {
93
- commentStart: "/*",
94
- commentEnd: "*/",
95
- wordWrapChars: [";", "\\{", "\\}"],
96
-
97
- getNonBreakableBlocks: function (text) {
98
- var nonBreakableRegexes = [
99
- new RegExp("for\\s*?\\(([\\s\\S]*?)\\)"),
100
- new RegExp("'([\\s\\S]*?)('|$)"),
101
- new RegExp("\"([\\s\\S]*?)(\"|$)"),
102
- new RegExp("//.*([\r\n]|$)")
103
- ];
104
- var nonBreakableBlocks = new Array();
105
- for (var i = 0; i < nonBreakableRegexes.length; i++) {
106
- var curPos = 0;
107
- while (curPos < text.length) {
108
- var m = text.substr(curPos).match(nonBreakableRegexes[i]);
109
- if (m != null) {
110
- nonBreakableBlocks.push({
111
- start: curPos + m.index,
112
- end: curPos + m.index + m[0].length
113
- });
114
- curPos += m.index + Math.max(1, m[0].length);
115
- }
116
- else { // No more matches
117
- break;
118
  }
 
119
  }
120
- }
121
- nonBreakableBlocks.sort(function (a, b) {
122
- return a.start - b.start;
123
  });
 
124
 
125
- return nonBreakableBlocks;
126
- },
127
-
128
- autoFormatLineBreaks: function (text) {
129
- var curPos = 0;
130
- var reLinesSplitter = new RegExp("(;|\\{|\\})([^\r\n])", "g");
131
- var nonBreakableBlocks = this.getNonBreakableBlocks(text);
132
- if (nonBreakableBlocks != null) {
133
- var res = "";
134
- for (var i = 0; i < nonBreakableBlocks.length; i++) {
135
- if (nonBreakableBlocks[i].start > curPos) { // Break lines till the block
136
- res += text.substring(curPos, nonBreakableBlocks[i].start).replace(reLinesSplitter, "$1\n$2");
137
- curPos = nonBreakableBlocks[i].start;
138
- }
139
- if (nonBreakableBlocks[i].start <= curPos
140
- && nonBreakableBlocks[i].end >= curPos) { // Skip non-breakable block
141
- res += text.substring(curPos, nonBreakableBlocks[i].end);
142
- curPos = nonBreakableBlocks[i].end;
143
- }
144
- }
145
- if (curPos < text.length - 1) {
146
- res += text.substr(curPos).replace(reLinesSplitter, "$1\n$2");
147
- }
148
- return res;
149
- }
150
- else {
151
- return text.replace(reLinesSplitter, "$1\n$2");
152
- }
153
- }
154
- };
155
-
156
- CodeMirror.modeExtensions["xml"] = {
157
- commentStart: "<!--",
158
- commentEnd: "-->",
159
- wordWrapChars: [">"],
160
-
161
- autoFormatLineBreaks: function (text) {
162
- var lines = text.split("\n");
163
- var reProcessedPortion = new RegExp("(^\\s*?<|^[^<]*?)(.+)(>\\s*?$|[^>]*?$)");
164
- var reOpenBrackets = new RegExp("<", "g");
165
- var reCloseBrackets = new RegExp("(>)([^\r\n])", "g");
166
- for (var i = 0; i < lines.length; i++) {
167
- var mToProcess = lines[i].match(reProcessedPortion);
168
- if (mToProcess != null && mToProcess.length > 3) { // The line starts with whitespaces and ends with whitespaces
169
- lines[i] = mToProcess[1]
170
- + mToProcess[2].replace(reOpenBrackets, "\n$&").replace(reCloseBrackets, "$1\n$2")
171
- + mToProcess[3];
172
- continue;
173
- }
174
- }
175
-
176
- return lines.join("\n");
177
- }
178
- };
179
-
180
- CodeMirror.modeExtensions["htmlmixed"] = {
181
- commentStart: "<!--",
182
- commentEnd: "-->",
183
- wordWrapChars: [">", ";", "\\{", "\\}"],
184
-
185
- getModeInfos: function (text, absPos) {
186
- var modeInfos = new Array();
187
- modeInfos[0] =
188
- {
189
- pos: 0,
190
- modeExt: CodeMirror.modeExtensions["xml"],
191
- modeName: "xml"
192
- };
193
-
194
- var modeMatchers = new Array();
195
- modeMatchers[0] =
196
- {
197
- regex: new RegExp("<style[^>]*>([\\s\\S]*?)(</style[^>]*>|$)", "i"),
198
- modeExt: CodeMirror.modeExtensions["css"],
199
- modeName: "css"
200
- };
201
- modeMatchers[1] =
202
- {
203
- regex: new RegExp("<script[^>]*>([\\s\\S]*?)(</script[^>]*>|$)", "i"),
204
- modeExt: CodeMirror.modeExtensions["javascript"],
205
- modeName: "javascript"
206
- };
207
-
208
- var lastCharPos = (typeof (absPos) !== "undefined" ? absPos : text.length - 1);
209
- // Detect modes for the entire text
210
- for (var i = 0; i < modeMatchers.length; i++) {
211
- var curPos = 0;
212
- while (curPos <= lastCharPos) {
213
- var m = text.substr(curPos).match(modeMatchers[i].regex);
214
- if (m != null) {
215
- if (m.length > 1 && m[1].length > 0) {
216
- // Push block begin pos
217
- var blockBegin = curPos + m.index + m[0].indexOf(m[1]);
218
- modeInfos.push(
219
- {
220
- pos: blockBegin,
221
- modeExt: modeMatchers[i].modeExt,
222
- modeName: modeMatchers[i].modeName
223
- });
224
- // Push block end pos
225
- modeInfos.push(
226
- {
227
- pos: blockBegin + m[1].length,
228
- modeExt: modeInfos[0].modeExt,
229
- modeName: modeInfos[0].modeName
230
- });
231
- curPos += m.index + m[0].length;
232
- continue;
233
- }
234
- else {
235
- curPos += m.index + Math.max(m[0].length, 1);
236
- }
237
- }
238
- else { // No more matches
239
- break;
240
- }
241
  }
242
- }
243
- // Sort mode infos
244
- modeInfos.sort(function sortModeInfo(a, b) {
245
- return a.pos - b.pos;
246
  });
 
247
 
248
- return modeInfos;
249
- },
250
-
251
- autoFormatLineBreaks: function (text, startPos, endPos) {
252
- var modeInfos = this.getModeInfos(text);
253
- var reBlockStartsWithNewline = new RegExp("^\\s*?\n");
254
- var reBlockEndsWithNewline = new RegExp("\n\\s*?$");
255
- var res = "";
256
- // Use modes info to break lines correspondingly
257
- if (modeInfos.length > 1) { // Deal with multi-mode text
258
- for (var i = 1; i <= modeInfos.length; i++) {
259
- var selStart = modeInfos[i - 1].pos;
260
- var selEnd = (i < modeInfos.length ? modeInfos[i].pos : endPos);
261
 
262
- if (selStart >= endPos) { // The block starts later than the needed fragment
263
- break;
264
- }
265
- if (selStart < startPos) {
266
- if (selEnd <= startPos) { // The block starts earlier than the needed fragment
267
- continue;
268
- }
269
- selStart = startPos;
 
270
  }
271
- if (selEnd > endPos) {
272
- selEnd = endPos;
273
- }
274
- var textPortion = text.substring(selStart, selEnd);
275
- if (modeInfos[i - 1].modeName != "xml") { // Starting a CSS or JavaScript block
276
- if (!reBlockStartsWithNewline.test(textPortion)
277
- && selStart > 0) { // The block does not start with a line break
278
- textPortion = "\n" + textPortion;
279
- }
280
- if (!reBlockEndsWithNewline.test(textPortion)
281
- && selEnd < text.length - 1) { // The block does not end with a line break
282
- textPortion += "\n";
283
- }
284
- }
285
- res += modeInfos[i - 1].modeExt.autoFormatLineBreaks(textPortion);
286
  }
287
- }
288
- else { // Single-mode text
289
- res = modeInfos[0].modeExt.autoFormatLineBreaks(text.substring(startPos, endPos));
290
  }
291
 
292
- return res;
293
- }
294
- };
 
 
 
 
 
1
+ (function() {
 
 
2
 
3
+ CodeMirror.extendMode("css", {
4
+ commentStart: "/*",
5
+ commentEnd: "*/",
6
+ newlineAfterToken: function(_type, content) {
7
+ return /^[;{}]$/.test(content);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  }
9
+ });
 
 
10
 
11
+ CodeMirror.extendMode("javascript", {
12
+ commentStart: "/*",
13
+ commentEnd: "*/",
14
+ // FIXME semicolons inside of for
15
+ newlineAfterToken: function(_type, content, textAfter, state) {
16
+ if (this.jsonMode) {
17
+ return /^[\[,{]$/.test(content) || /^}/.test(textAfter);
18
+ } else {
19
+ if (content == ";" && state.lexical && state.lexical.type == ")") return false;
20
+ return /^[;{}]$/.test(content) && !/^;/.test(textAfter);
21
+ }
22
  }
23
  });
 
24
 
25
+ var inlineElements = /^(a|abbr|acronym|area|base|bdo|big|br|button|caption|cite|code|col|colgroup|dd|del|dfn|em|frame|hr|iframe|img|input|ins|kbd|label|legend|link|map|object|optgroup|option|param|q|samp|script|select|small|span|strong|sub|sup|textarea|tt|var)$/;
26
+
27
+ CodeMirror.extendMode("xml", {
28
+ commentStart: "<!--",
29
+ commentEnd: "-->",
30
+ newlineAfterToken: function(type, content, textAfter, state) {
31
+ var inline = false;
32
+ if (this.configuration == "html")
33
+ inline = state.context ? inlineElements.test(state.context.tagName) : false;
34
+ return !inline && ((type == "tag" && />$/.test(content) && state.context) ||
35
+ /^</.test(textAfter));
 
 
 
 
 
36
  }
37
  });
 
 
 
 
 
 
 
 
 
 
 
 
38
 
39
+ // Comment/uncomment the specified range
40
+ CodeMirror.defineExtension("commentRange", function (isComment, from, to) {
41
+ var cm = this, curMode = CodeMirror.innerMode(cm.getMode(), cm.getTokenAt(from).state).mode;
42
+ cm.operation(function() {
43
+ if (isComment) { // Comment range
44
+ cm.replaceRange(curMode.commentEnd, to);
45
+ cm.replaceRange(curMode.commentStart, from);
46
+ if (from.line == to.line && from.ch == to.ch) // An empty comment inserted - put cursor inside
47
+ cm.setCursor(from.line, from.ch + curMode.commentStart.length);
48
+ } else { // Uncomment range
49
+ var selText = cm.getRange(from, to);
50
+ var startIndex = selText.indexOf(curMode.commentStart);
51
+ var endIndex = selText.lastIndexOf(curMode.commentEnd);
52
+ if (startIndex > -1 && endIndex > -1 && endIndex > startIndex) {
53
+ // Take string till comment start
54
+ selText = selText.substr(0, startIndex)
55
+ // From comment start till comment end
56
+ + selText.substring(startIndex + curMode.commentStart.length, endIndex)
57
+ // From comment end till string end
58
+ + selText.substr(endIndex + curMode.commentEnd.length);
 
 
 
 
 
 
59
  }
60
+ cm.replaceRange(selText, from, to);
61
  }
 
 
 
62
  });
63
+ });
64
 
65
+ // Applies automatic mode-aware indentation to the specified range
66
+ CodeMirror.defineExtension("autoIndentRange", function (from, to) {
67
+ var cmInstance = this;
68
+ this.operation(function () {
69
+ for (var i = from.line; i <= to.line; i++) {
70
+ cmInstance.indentLine(i, "smart");
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71
  }
 
 
 
 
72
  });
73
+ });
74
 
75
+ // Applies automatic formatting to the specified range
76
+ CodeMirror.defineExtension("autoFormatRange", function (from, to) {
77
+ var cm = this;
78
+ var outer = cm.getMode(), text = cm.getRange(from, to).split("\n");
79
+ var state = CodeMirror.copyState(outer, cm.getTokenAt(from).state);
80
+ var tabSize = cm.getOption("tabSize");
81
+
82
+ var out = "", lines = 0, atSol = from.ch == 0;
83
+ function newline() {
84
+ out += "\n";
85
+ atSol = true;
86
+ ++lines;
87
+ }
88
 
89
+ for (var i = 0; i < text.length; ++i) {
90
+ var stream = new CodeMirror.StringStream(text[i], tabSize);
91
+ while (!stream.eol()) {
92
+ var inner = CodeMirror.innerMode(outer, state);
93
+ var style = outer.token(stream, state), cur = stream.current();
94
+ stream.start = stream.pos;
95
+ if (!atSol || /\S/.test(cur)) {
96
+ out += cur;
97
+ atSol = false;
98
  }
99
+ if (!atSol && inner.mode.newlineAfterToken &&
100
+ inner.mode.newlineAfterToken(style, cur, stream.string.slice(stream.pos) || text[i+1] || "", inner.state))
101
+ newline();
 
 
 
 
 
 
 
 
 
 
 
 
102
  }
103
+ if (!stream.pos && outer.blankLine) outer.blankLine(state);
104
+ if (!atSol && i < text.length - 1) newline();
 
105
  }
106
 
107
+ cm.operation(function () {
108
+ cm.replaceRange(out, from, to);
109
+ for (var cur = from.line + 1, end = from.line + lines; cur <= end; ++cur)
110
+ cm.indentLine(cur, "smart");
111
+ cm.setSelection(from, cm.getCursor(false));
112
+ });
113
+ });
114
+ })();
lib/util/javascript-hint.js DELETED
@@ -1,134 +0,0 @@
1
- (function () {
2
- function forEach(arr, f) {
3
- for (var i = 0, e = arr.length; i < e; ++i) f(arr[i]);
4
- }
5
-
6
- function arrayContains(arr, item) {
7
- if (!Array.prototype.indexOf) {
8
- var i = arr.length;
9
- while (i--) {
10
- if (arr[i] === item) {
11
- return true;
12
- }
13
- }
14
- return false;
15
- }
16
- return arr.indexOf(item) != -1;
17
- }
18
-
19
- function scriptHint(editor, keywords, getToken) {
20
- // Find the token at the cursor
21
- var cur = editor.getCursor(), token = getToken(editor, cur), tprop = token;
22
- // If it's not a 'word-style' token, ignore the token.
23
- if (!/^[\w$_]*$/.test(token.string)) {
24
- token = tprop = {start: cur.ch, end: cur.ch, string: "", state: token.state,
25
- className: token.string == "." ? "property" : null};
26
- }
27
- // If it is a property, find out what it is a property of.
28
- while (tprop.className == "property") {
29
- tprop = getToken(editor, {line: cur.line, ch: tprop.start});
30
- if (tprop.string != ".") return;
31
- tprop = getToken(editor, {line: cur.line, ch: tprop.start});
32
- if (tprop.string == ')') {
33
- var level = 1;
34
- do {
35
- tprop = getToken(editor, {line: cur.line, ch: tprop.start});
36
- switch (tprop.string) {
37
- case ')': level++; break;
38
- case '(': level--; break;
39
- default: break;
40
- }
41
- } while (level > 0)
42
- tprop = getToken(editor, {line: cur.line, ch: tprop.start});
43
- if (tprop.className == 'variable')
44
- tprop.className = 'function';
45
- else return; // no clue
46
- }
47
- if (!context) var context = [];
48
- context.push(tprop);
49
- }
50
- return {list: getCompletions(token, context, keywords),
51
- from: {line: cur.line, ch: token.start},
52
- to: {line: cur.line, ch: token.end}};
53
- }
54
-
55
- CodeMirror.javascriptHint = function(editor) {
56
- return scriptHint(editor, javascriptKeywords,
57
- function (e, cur) {return e.getTokenAt(cur);});
58
- }
59
-
60
- function getCoffeeScriptToken(editor, cur) {
61
- // This getToken, it is for coffeescript, imitates the behavior of
62
- // getTokenAt method in javascript.js, that is, returning "property"
63
- // type and treat "." as indepenent token.
64
- var token = editor.getTokenAt(cur);
65
- if (cur.ch == token.start + 1 && token.string.charAt(0) == '.') {
66
- token.end = token.start;
67
- token.string = '.';
68
- token.className = "property";
69
- }
70
- else if (/^\.[\w$_]*$/.test(token.string)) {
71
- token.className = "property";
72
- token.start++;
73
- token.string = token.string.replace(/\./, '');
74
- }
75
- return token;
76
- }
77
-
78
- CodeMirror.coffeescriptHint = function(editor) {
79
- return scriptHint(editor, coffeescriptKeywords, getCoffeeScriptToken);
80
- }
81
-
82
- var stringProps = ("charAt charCodeAt indexOf lastIndexOf substring substr slice trim trimLeft trimRight " +
83
- "toUpperCase toLowerCase split concat match replace search").split(" ");
84
- var arrayProps = ("length concat join splice push pop shift unshift slice reverse sort indexOf " +
85
- "lastIndexOf every some filter forEach map reduce reduceRight ").split(" ");
86
- var funcProps = "prototype apply call bind".split(" ");
87
- var javascriptKeywords = ("break case catch continue debugger default delete do else false finally for function " +
88
- "if in instanceof new null return switch throw true try typeof var void while with").split(" ");
89
- var coffeescriptKeywords = ("and break catch class continue delete do else extends false finally for " +
90
- "if in instanceof isnt new no not null of off on or return switch then throw true try typeof until void while with yes").split(" ");
91
-
92
- function getCompletions(token, context, keywords) {
93
- var found = [], start = token.string;
94
- function maybeAdd(str) {
95
- if (str.indexOf(start) == 0 && !arrayContains(found, str)) found.push(str);
96
- }
97
- function gatherCompletions(obj) {
98
- if (typeof obj == "string") forEach(stringProps, maybeAdd);
99
- else if (obj instanceof Array) forEach(arrayProps, maybeAdd);
100
- else if (obj instanceof Function) forEach(funcProps, maybeAdd);
101
- for (var name in obj) maybeAdd(name);
102
- }
103
-
104
- if (context) {
105
- // If this is a property, see if it belongs to some object we can
106
- // find in the current environment.
107
- var obj = context.pop(), base;
108
- if (obj.className == "variable")
109
- base = window[obj.string];
110
- else if (obj.className == "string")
111
- base = "";
112
- else if (obj.className == "atom")
113
- base = 1;
114
- else if (obj.className == "function") {
115
- if (window.jQuery != null && (obj.string == '$' || obj.string == 'jQuery') &&
116
- (typeof window.jQuery == 'function'))
117
- base = window.jQuery();
118
- else if (window._ != null && (obj.string == '_') && (typeof window._ == 'function'))
119
- base = window._();
120
- }
121
- while (base != null && context.length)
122
- base = base[context.pop().string];
123
- if (base != null) gatherCompletions(base);
124
- }
125
- else {
126
- // If not, just look in the window object and any local scope
127
- // (reading into JS mode internals to get at the local variables)
128
- for (var v = token.state.localVars; v; v = v.next) maybeAdd(v.name);
129
- gatherCompletions(window);
130
- forEach(keywords, maybeAdd);
131
- }
132
- return found;
133
- }
134
- })();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/util/match-highlighter.js DELETED
@@ -1,44 +0,0 @@
1
- // Define match-highlighter commands. Depends on searchcursor.js
2
- // Use by attaching the following function call to the onCursorActivity event:
3
- //myCodeMirror.matchHighlight(minChars);
4
- // And including a special span.CodeMirror-matchhighlight css class (also optionally a separate one for .CodeMirror-focused -- see demo matchhighlighter.html)
5
-
6
- (function() {
7
- var DEFAULT_MIN_CHARS = 2;
8
-
9
- function MatchHighlightState() {
10
- this.marked = [];
11
- }
12
- function getMatchHighlightState(cm) {
13
- return cm._matchHighlightState || (cm._matchHighlightState = new MatchHighlightState());
14
- }
15
-
16
- function clearMarks(cm) {
17
- var state = getMatchHighlightState(cm);
18
- for (var i = 0; i < state.marked.length; ++i)
19
- state.marked[i].clear();
20
- state.marked = [];
21
- }
22
-
23
- function markDocument(cm, className, minChars) {
24
- clearMarks(cm);
25
- minChars = (typeof minChars !== 'undefined' ? minChars : DEFAULT_MIN_CHARS);
26
- if (cm.somethingSelected() && cm.getSelection().length >= minChars) {
27
- var state = getMatchHighlightState(cm);
28
- var query = cm.getSelection();
29
- cm.operation(function() {
30
- if (cm.lineCount() < 2000) { // This is too expensive on big documents.
31
- for (var cursor = cm.getSearchCursor(query); cursor.findNext();) {
32
- //Only apply matchhighlight to the matches other than the one actually selected
33
- if (!(cursor.from().line === cm.getCursor(true).line && cursor.from().ch === cm.getCursor(true).ch))
34
- state.marked.push(cm.markText(cursor.from(), cursor.to(), className));
35
- }
36
- }
37
- });
38
- }
39
- }
40
-
41
- CodeMirror.defineExtension("matchHighlight", function(className, minChars) {
42
- markDocument(this, className, minChars);
43
- });
44
- })();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/util/overlay.js DELETED
@@ -1,51 +0,0 @@
1
- // Utility function that allows modes to be combined. The mode given
2
- // as the base argument takes care of most of the normal mode
3
- // functionality, but a second (typically simple) mode is used, which
4
- // can override the style of text. Both modes get to parse all of the
5
- // text, but when both assign a non-null style to a piece of code, the
6
- // overlay wins, unless the combine argument was true, in which case
7
- // the styles are combined.
8
-
9
- CodeMirror.overlayParser = function(base, overlay, combine) {
10
- return {
11
- startState: function() {
12
- return {
13
- base: CodeMirror.startState(base),
14
- overlay: CodeMirror.startState(overlay),
15
- basePos: 0, baseCur: null,
16
- overlayPos: 0, overlayCur: null
17
- };
18
- },
19
- copyState: function(state) {
20
- return {
21
- base: CodeMirror.copyState(base, state.base),
22
- overlay: CodeMirror.copyState(overlay, state.overlay),
23
- basePos: state.basePos, baseCur: null,
24
- overlayPos: state.overlayPos, overlayCur: null
25
- };
26
- },
27
-
28
- token: function(stream, state) {
29
- if (stream.start == state.basePos) {
30
- state.baseCur = base.token(stream, state.base);
31
- state.basePos = stream.pos;
32
- }
33
- if (stream.start == state.overlayPos) {
34
- stream.pos = stream.start;
35
- state.overlayCur = overlay.token(stream, state.overlay);
36
- state.overlayPos = stream.pos;
37
- }
38
- stream.pos = Math.min(state.basePos, state.overlayPos);
39
- if (stream.eol()) state.basePos = state.overlayPos = 0;
40
-
41
- if (state.overlayCur == null) return state.baseCur;
42
- if (state.baseCur != null && combine) return state.baseCur + " " + state.overlayCur;
43
- else return state.overlayCur;
44
- },
45
-
46
- indent: function(state, textAfter) {
47
- return base.indent(state.base, textAfter);
48
- },
49
- electricChars: base.electricChars
50
- };
51
- };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/util/runmode.js DELETED
@@ -1,49 +0,0 @@
1
- CodeMirror.runMode = function(string, modespec, callback, options) {
2
- var mode = CodeMirror.getMode(CodeMirror.defaults, modespec);
3
- var isNode = callback.nodeType == 1;
4
- var tabSize = (options && options.tabSize) || CodeMirror.defaults.tabSize;
5
- if (isNode) {
6
- var node = callback, accum = [], col = 0;
7
- callback = function(text, style) {
8
- if (text == "\n") {
9
- accum.push("<br>");
10
- col = 0;
11
- return;
12
- }
13
- var escaped = "";
14
- // HTML-escape and replace tabs
15
- for (var pos = 0;;) {
16
- var idx = text.indexOf("\t", pos);
17
- if (idx == -1) {
18
- escaped += CodeMirror.htmlEscape(text.slice(pos));
19
- col += text.length - pos;
20
- break;
21
- } else {
22
- col += idx - pos;
23
- escaped += CodeMirror.htmlEscape(text.slice(pos, idx));
24
- var size = tabSize - col % tabSize;
25
- col += size;
26
- for (var i = 0; i < size; ++i) escaped += " ";
27
- pos = idx + 1;
28
- }
29
- }
30
-
31
- if (style)
32
- accum.push("<span class=\"cm-" + CodeMirror.htmlEscape(style) + "\">" + escaped + "</span>");
33
- else
34
- accum.push(escaped);
35
- }
36
- }
37
- var lines = CodeMirror.splitLines(string), state = CodeMirror.startState(mode);
38
- for (var i = 0, e = lines.length; i < e; ++i) {
39
- if (i) callback("\n");
40
- var stream = new CodeMirror.StringStream(lines[i]);
41
- while (!stream.eol()) {
42
- var style = mode.token(stream, state);
43
- callback(stream.current(), style, i, stream.start);
44
- stream.start = stream.pos;
45
- }
46
- }
47
- if (isNode)
48
- node.innerHTML = accum.join("");
49
- };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/util/search.js DELETED
@@ -1,114 +0,0 @@
1
- // Define search commands. Depends on dialog.js or another
2
- // implementation of the openDialog method.
3
-
4
- // Replace works a little oddly -- it will do the replace on the next
5
- // Ctrl-G (or whatever is bound to findNext) press. You prevent a
6
- // replace by making sure the match is no longer selected when hitting
7
- // Ctrl-G.
8
-
9
- (function() {
10
- function SearchState() {
11
- this.posFrom = this.posTo = this.query = null;
12
- this.marked = [];
13
- }
14
- function getSearchState(cm) {
15
- return cm._searchState || (cm._searchState = new SearchState());
16
- }
17
- function dialog(cm, text, shortText, f) {
18
- if (cm.openDialog) cm.openDialog(text, f);
19
- else f(prompt(shortText, ""));
20
- }
21
- function confirmDialog(cm, text, shortText, fs) {
22
- if (cm.openConfirm) cm.openConfirm(text, fs);
23
- else if (confirm(shortText)) fs[0]();
24
- }
25
- function parseQuery(query) {
26
- var isRE = query.match(/^\/(.*)\/$/);
27
- return isRE ? new RegExp(isRE[1]) : query;
28
- }
29
- var queryDialog =
30
- 'Search: <input type="text" style="width: 10em"> <span style="color: #888">(Use /re/ syntax for regexp search)</span>';
31
- function doSearch(cm, rev) {
32
- var state = getSearchState(cm);
33
- if (state.query) return findNext(cm, rev);
34
- dialog(cm, queryDialog, "Search for:", function(query) {
35
- cm.operation(function() {
36
- if (!query || state.query) return;
37
- state.query = parseQuery(query);
38
- if (cm.lineCount() < 2000) { // This is too expensive on big documents.
39
- for (var cursor = cm.getSearchCursor(query); cursor.findNext();)
40
- state.marked.push(cm.markText(cursor.from(), cursor.to(), "CodeMirror-searching"));
41
- }
42
- state.posFrom = state.posTo = cm.getCursor();
43
- findNext(cm, rev);
44
- });
45
- });
46
- }
47
- function findNext(cm, rev) {cm.operation(function() {
48
- var state = getSearchState(cm);
49
- var cursor = cm.getSearchCursor(state.query, rev ? state.posFrom : state.posTo);
50
- if (!cursor.find(rev)) {
51
- cursor = cm.getSearchCursor(state.query, rev ? {line: cm.lineCount() - 1} : {line: 0, ch: 0});
52
- if (!cursor.find(rev)) return;
53
- }
54
- cm.setSelection(cursor.from(), cursor.to());
55
- state.posFrom = cursor.from(); state.posTo = cursor.to();
56
- })}
57
- function clearSearch(cm) {cm.operation(function() {
58
- var state = getSearchState(cm);
59
- if (!state.query) return;
60
- state.query = null;
61
- for (var i = 0; i < state.marked.length; ++i) state.marked[i].clear();
62
- state.marked.length = 0;
63
- })}
64
-
65
- var replaceQueryDialog =
66
- 'Replace: <input type="text" style="width: 10em"> <span style="color: #888">(Use /re/ syntax for regexp search)</span>';
67
- var replacementQueryDialog = 'With: <input type="text" style="width: 10em">';
68
- var doReplaceConfirm = "Replace? <button>Yes</button> <button>No</button> <button>Stop</button>";
69
- function replace(cm, all) {
70
- dialog(cm, replaceQueryDialog, "Replace:", function(query) {
71
- if (!query) return;
72
- query = parseQuery(query);
73
- dialog(cm, replacementQueryDialog, "Replace with:", function(text) {
74
- if (all) {
75
- cm.operation(function() {
76
- for (var cursor = cm.getSearchCursor(query); cursor.findNext();) {
77
- if (typeof query != "string") {
78
- var match = cm.getRange(cursor.from(), cursor.to()).match(query);
79
- cursor.replace(text.replace(/\$(\d)/, function(w, i) {return match[i];}));
80
- } else cursor.replace(text);
81
- }
82
- });
83
- } else {
84
- clearSearch(cm);
85
- var cursor = cm.getSearchCursor(query, cm.getCursor());
86
- function advance() {
87
- var start = cursor.from(), match;
88
- if (!(match = cursor.findNext())) {
89
- cursor = cm.getSearchCursor(query);
90
- if (!(match = cursor.findNext()) ||
91
- (cursor.from().line == start.line && cursor.from().ch == start.ch)) return;
92
- }
93
- cm.setSelection(cursor.from(), cursor.to());
94
- confirmDialog(cm, doReplaceConfirm, "Replace?",
95
- [function() {doReplace(match);}, advance]);
96
- }
97
- function doReplace(match) {
98
- cursor.replace(typeof query == "string" ? text :
99
- text.replace(/\$(\d)/, function(w, i) {return match[i];}));
100
- advance();
101
- }
102
- advance();
103
- }
104
- });
105
- });
106
- }
107
-
108
- CodeMirror.commands.find = function(cm) {clearSearch(cm); doSearch(cm);};
109
- CodeMirror.commands.findNext = doSearch;
110
- CodeMirror.commands.findPrev = function(cm) {doSearch(cm, true);};
111
- CodeMirror.commands.clearSearch = clearSearch;
112
- CodeMirror.commands.replace = replace;
113
- CodeMirror.commands.replaceAll = function(cm) {replace(cm, true);};
114
- })();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/util/searchcursor.js DELETED
@@ -1,117 +0,0 @@
1
- (function(){
2
- function SearchCursor(cm, query, pos, caseFold) {
3
- this.atOccurrence = false; this.cm = cm;
4
- if (caseFold == null) caseFold = typeof query == "string" && query == query.toLowerCase();
5
-
6
- pos = pos ? cm.clipPos(pos) : {line: 0, ch: 0};
7
- this.pos = {from: pos, to: pos};
8
-
9
- // The matches method is filled in based on the type of query.
10
- // It takes a position and a direction, and returns an object
11
- // describing the next occurrence of the query, or null if no
12
- // more matches were found.
13
- if (typeof query != "string") // Regexp match
14
- this.matches = function(reverse, pos) {
15
- if (reverse) {
16
- var line = cm.getLine(pos.line).slice(0, pos.ch), match = line.match(query), start = 0;
17
- while (match) {
18
- var ind = line.indexOf(match[0]);
19
- start += ind;
20
- line = line.slice(ind + 1);
21
- var newmatch = line.match(query);
22
- if (newmatch) match = newmatch;
23
- else break;
24
- start++;
25
- }
26
- }
27
- else {
28
- var line = cm.getLine(pos.line).slice(pos.ch), match = line.match(query),
29
- start = match && pos.ch + line.indexOf(match[0]);
30
- }
31
- if (match)
32
- return {from: {line: pos.line, ch: start},
33
- to: {line: pos.line, ch: start + match[0].length},
34
- match: match};
35
- };
36
- else { // String query
37
- if (caseFold) query = query.toLowerCase();
38
- var fold = caseFold ? function(str){return str.toLowerCase();} : function(str){return str;};
39
- var target = query.split("\n");
40
- // Different methods for single-line and multi-line queries
41
- if (target.length == 1)
42
- this.matches = function(reverse, pos) {
43
- var line = fold(cm.getLine(pos.line)), len = query.length, match;
44
- if (reverse ? (pos.ch >= len && (match = line.lastIndexOf(query, pos.ch - len)) != -1)
45
- : (match = line.indexOf(query, pos.ch)) != -1)
46
- return {from: {line: pos.line, ch: match},
47
- to: {line: pos.line, ch: match + len}};
48
- };
49
- else
50
- this.matches = function(reverse, pos) {
51
- var ln = pos.line, idx = (reverse ? target.length - 1 : 0), match = target[idx], line = fold(cm.getLine(ln));
52
- var offsetA = (reverse ? line.indexOf(match) + match.length : line.lastIndexOf(match));
53
- if (reverse ? offsetA >= pos.ch || offsetA != match.length
54
- : offsetA <= pos.ch || offsetA != line.length - match.length)
55
- return;
56
- for (;;) {
57
- if (reverse ? !ln : ln == cm.lineCount() - 1) return;
58
- line = fold(cm.getLine(ln += reverse ? -1 : 1));
59
- match = target[reverse ? --idx : ++idx];
60
- if (idx > 0 && idx < target.length - 1) {
61
- if (line != match) return;
62
- else continue;
63
- }
64
- var offsetB = (reverse ? line.lastIndexOf(match) : line.indexOf(match) + match.length);
65
- if (reverse ? offsetB != line.length - match.length : offsetB != match.length)
66
- return;
67
- var start = {line: pos.line, ch: offsetA}, end = {line: ln, ch: offsetB};
68
- return {from: reverse ? end : start, to: reverse ? start : end};
69
- }
70
- };
71
- }
72
- }
73
-
74
- SearchCursor.prototype = {
75
- findNext: function() {return this.find(false);},
76
- findPrevious: function() {return this.find(true);},
77
-
78
- find: function(reverse) {
79
- var self = this, pos = this.cm.clipPos(reverse ? this.pos.from : this.pos.to);
80
- function savePosAndFail(line) {
81
- var pos = {line: line, ch: 0};
82
- self.pos = {from: pos, to: pos};
83
- self.atOccurrence = false;
84
- return false;
85
- }
86
-
87
- for (;;) {
88
- if (this.pos = this.matches(reverse, pos)) {
89
- this.atOccurrence = true;
90
- return this.pos.match || true;
91
- }
92
- if (reverse) {
93
- if (!pos.line) return savePosAndFail(0);
94
- pos = {line: pos.line-1, ch: this.cm.getLine(pos.line-1).length};
95
- }
96
- else {
97
- var maxLine = this.cm.lineCount();
98
- if (pos.line == maxLine - 1) return savePosAndFail(maxLine);
99
- pos = {line: pos.line+1, ch: 0};
100
- }
101
- }
102
- },
103
-
104
- from: function() {if (this.atOccurrence) return this.pos.from;},
105
- to: function() {if (this.atOccurrence) return this.pos.to;},
106
-
107
- replace: function(newText) {
108
- var self = this;
109
- if (this.atOccurrence)
110
- self.pos.to = this.cm.replaceRange(newText, self.pos.from, self.pos.to);
111
- }
112
- };
113
-
114
- CodeMirror.defineExtension("getSearchCursor", function(query, pos, caseFold) {
115
- return new SearchCursor(this, query, pos, caseFold);
116
- });
117
- })();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/util/simple-hint.css DELETED
@@ -1,16 +0,0 @@
1
- .CodeMirror-completions {
2
- position: absolute;
3
- z-index: 10;
4
- overflow: hidden;
5
- -webkit-box-shadow: 2px 3px 5px rgba(0,0,0,.2);
6
- -moz-box-shadow: 2px 3px 5px rgba(0,0,0,.2);
7
- box-shadow: 2px 3px 5px rgba(0,0,0,.2);
8
- }
9
- .CodeMirror-completions select {
10
- background: #fafafa;
11
- outline: none;
12
- border: none;
13
- padding: 0;
14
- margin: 0;
15
- font-family: monospace;
16
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/util/simple-hint.js DELETED
@@ -1,72 +0,0 @@
1
- (function() {
2
- CodeMirror.simpleHint = function(editor, getHints) {
3
- // We want a single cursor position.
4
- if (editor.somethingSelected()) return;
5
- var result = getHints(editor);
6
- if (!result || !result.list.length) return;
7
- var completions = result.list;
8
- function insert(str) {
9
- editor.replaceRange(str, result.from, result.to);
10
- }
11
- // When there is only one completion, use it directly.
12
- if (completions.length == 1) {insert(completions[0]); return true;}
13
-
14
- // Build the select widget
15
- var complete = document.createElement("div");
16
- complete.className = "CodeMirror-completions";
17
- var sel = complete.appendChild(document.createElement("select"));
18
- // Opera doesn't move the selection when pressing up/down in a
19
- // multi-select, but it does properly support the size property on
20
- // single-selects, so no multi-select is necessary.
21
- if (!window.opera) sel.multiple = true;
22
- for (var i = 0; i < completions.length; ++i) {
23
- var opt = sel.appendChild(document.createElement("option"));
24
- opt.appendChild(document.createTextNode(completions[i]));
25
- }
26
- sel.firstChild.selected = true;
27
- sel.size = Math.min(10, completions.length);
28
- var pos = editor.cursorCoords();
29
- complete.style.left = pos.x + "px";
30
- complete.style.top = pos.yBot + "px";
31
- document.body.appendChild(complete);
32
- // If we're at the edge of the screen, then we want the menu to appear on the left of the cursor.
33
- var winW = window.innerWidth || Math.max(document.body.offsetWidth, document.documentElement.offsetWidth);
34
- if(winW - pos.x < sel.clientWidth)
35
- complete.style.left = (pos.x - sel.clientWidth) + "px";
36
- // Hack to hide the scrollbar.
37
- if (completions.length <= 10)
38
- complete.style.width = (sel.clientWidth - 1) + "px";
39
-
40
- var done = false;
41
- function close() {
42
- if (done) return;
43
- done = true;
44
- complete.parentNode.removeChild(complete);
45
- }
46
- function pick() {
47
- insert(completions[sel.selectedIndex]);
48
- close();
49
- setTimeout(function(){editor.focus();}, 50);
50
- }
51
- CodeMirror.connect(sel, "blur", close);
52
- CodeMirror.connect(sel, "keydown", function(event) {
53
- var code = event.keyCode;
54
- // Enter
55
- if (code == 13) {CodeMirror.e_stop(event); pick();}
56
- // Escape
57
- else if (code == 27) {CodeMirror.e_stop(event); close(); editor.focus();}
58
- else if (code != 38 && code != 40) {
59
- close(); editor.focus();
60
- // Pass the event to the CodeMirror instance so that it can handle things like backspace properly.
61
- editor.triggerOnKeyDown(event);
62
- setTimeout(function(){CodeMirror.simpleHint(editor, getHints);}, 50);
63
- }
64
- });
65
- CodeMirror.connect(sel, "dblclick", pick);
66
-
67
- sel.focus();
68
- // Opera sometimes ignores focusing a freshly created node
69
- if (window.opera) setTimeout(function(){if (!done) sel.focus();}, 100);
70
- return true;
71
- };
72
- })();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/xml.js CHANGED
@@ -1,12 +1,44 @@
1
  CodeMirror.defineMode("xml", function(config, parserConfig) {
2
  var indentUnit = config.indentUnit;
3
  var Kludges = parserConfig.htmlMode ? {
4
- autoSelfClosers: {"br": true, "img": true, "hr": true, "link": true, "input": true,
5
- "meta": true, "col": true, "frame": true, "base": true, "area": true},
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
  doNotIndent: {"pre": true},
7
  allowUnquoted: true,
 
 
 
 
 
 
 
8
  allowMissing: false
9
- } : {autoSelfClosers: {}, doNotIndent: {}, allowUnquoted: false, allowMissing: false};
10
  var alignCDATA = parserConfig.alignCDATA;
11
 
12
  // Return variables for tokenizers
@@ -38,11 +70,12 @@ CodeMirror.defineMode("xml", function(config, parserConfig) {
38
  return "meta";
39
  }
40
  else {
41
- type = stream.eat("/") ? "closeTag" : "openTag";
42
- stream.eatSpace();
43
  tagName = "";
44
  var c;
45
  while ((c = stream.eat(/[^\s\u00a0=<>\"\'\/?]/))) tagName += c;
 
 
46
  state.tokenize = inTag;
47
  return "tag";
48
  }
@@ -82,7 +115,7 @@ CodeMirror.defineMode("xml", function(config, parserConfig) {
82
  return state.tokenize(stream, state);
83
  }
84
  else {
85
- stream.eatWhile(/[^\s\u00a0=<>\"\'\/?]/);
86
  return "word";
87
  }
88
  }
@@ -162,7 +195,12 @@ CodeMirror.defineMode("xml", function(config, parserConfig) {
162
  } else if (type == "closeTag") {
163
  var err = false;
164
  if (curState.context) {
165
- err = curState.context.tagName != tagName;
 
 
 
 
 
166
  } else {
167
  err = true;
168
  }
@@ -173,10 +211,18 @@ CodeMirror.defineMode("xml", function(config, parserConfig) {
173
  }
174
  function endtag(startOfLine) {
175
  return function(type) {
 
 
176
  if (type == "selfcloseTag" ||
177
- (type == "endTag" && Kludges.autoSelfClosers.hasOwnProperty(curState.tagName.toLowerCase())))
 
178
  return cont();
179
- if (type == "endTag") {pushContext(curState.tagName, startOfLine); return cont();}
 
 
 
 
 
180
  return cont();
181
  };
182
  }
@@ -186,6 +232,20 @@ CodeMirror.defineMode("xml", function(config, parserConfig) {
186
  if (type == "endTag") { popContext(); return cont(); }
187
  setStyle = "error";
188
  return cont(arguments.callee);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
189
  }
190
  }
191
 
@@ -198,6 +258,7 @@ CodeMirror.defineMode("xml", function(config, parserConfig) {
198
  function attribute(type) {
199
  if (type == "equals") return cont(attvalue, attributes);
200
  if (!Kludges.allowMissing) setStyle = "error";
 
201
  return (type == "endTag" || type == "selfcloseTag") ? pass() : cont();
202
  }
203
  function attvalue(type) {
@@ -251,18 +312,13 @@ CodeMirror.defineMode("xml", function(config, parserConfig) {
251
  else return 0;
252
  },
253
 
254
- compareStates: function(a, b) {
255
- if (a.indented != b.indented || a.tokenize != b.tokenize) return false;
256
- for (var ca = a.context, cb = b.context; ; ca = ca.prev, cb = cb.prev) {
257
- if (!ca || !cb) return ca == cb;
258
- if (ca.tagName != cb.tagName) return false;
259
- }
260
- },
261
 
262
- electricChars: "/"
263
  };
264
  });
265
 
 
266
  CodeMirror.defineMIME("application/xml", "xml");
267
  if (!CodeMirror.mimeModes.hasOwnProperty("text/html"))
268
  CodeMirror.defineMIME("text/html", {name: "xml", htmlMode: true});
1
  CodeMirror.defineMode("xml", function(config, parserConfig) {
2
  var indentUnit = config.indentUnit;
3
  var Kludges = parserConfig.htmlMode ? {
4
+ autoSelfClosers: {'area': true, 'base': true, 'br': true, 'col': true, 'command': true,
5
+ 'embed': true, 'frame': true, 'hr': true, 'img': true, 'input': true,
6
+ 'keygen': true, 'link': true, 'meta': true, 'param': true, 'source': true,
7
+ 'track': true, 'wbr': true},
8
+ implicitlyClosed: {'dd': true, 'li': true, 'optgroup': true, 'option': true, 'p': true,
9
+ 'rp': true, 'rt': true, 'tbody': true, 'td': true, 'tfoot': true,
10
+ 'th': true, 'tr': true},
11
+ contextGrabbers: {
12
+ 'dd': {'dd': true, 'dt': true},
13
+ 'dt': {'dd': true, 'dt': true},
14
+ 'li': {'li': true},
15
+ 'option': {'option': true, 'optgroup': true},
16
+ 'optgroup': {'optgroup': true},
17
+ 'p': {'address': true, 'article': true, 'aside': true, 'blockquote': true, 'dir': true,
18
+ 'div': true, 'dl': true, 'fieldset': true, 'footer': true, 'form': true,
19
+ 'h1': true, 'h2': true, 'h3': true, 'h4': true, 'h5': true, 'h6': true,
20
+ 'header': true, 'hgroup': true, 'hr': true, 'menu': true, 'nav': true, 'ol': true,
21
+ 'p': true, 'pre': true, 'section': true, 'table': true, 'ul': true},
22
+ 'rp': {'rp': true, 'rt': true},
23
+ 'rt': {'rp': true, 'rt': true},
24
+ 'tbody': {'tbody': true, 'tfoot': true},
25
+ 'td': {'td': true, 'th': true},
26
+ 'tfoot': {'tbody': true},
27
+ 'th': {'td': true, 'th': true},
28
+ 'thead': {'tbody': true, 'tfoot': true},
29
+ 'tr': {'tr': true}
30
+ },
31
  doNotIndent: {"pre": true},
32
  allowUnquoted: true,
33
+ allowMissing: true
34
+ } : {
35
+ autoSelfClosers: {},
36
+ implicitlyClosed: {},
37
+ contextGrabbers: {},
38
+ doNotIndent: {},
39
+ allowUnquoted: false,
40
  allowMissing: false
41
+ };
42
  var alignCDATA = parserConfig.alignCDATA;
43
 
44
  // Return variables for tokenizers
70
  return "meta";
71
  }
72
  else {
73
+ var isClose = stream.eat("/");
 
74
  tagName = "";
75
  var c;
76
  while ((c = stream.eat(/[^\s\u00a0=<>\"\'\/?]/))) tagName += c;
77
+ if (!tagName) return "error";
78
+ type = isClose ? "closeTag" : "openTag";
79
  state.tokenize = inTag;
80
  return "tag";
81
  }
115
  return state.tokenize(stream, state);
116
  }
117
  else {
118
+ stream.eatWhile(/[^\s\u00a0=<>\"\']/);
119
  return "word";
120
  }
121
  }
195
  } else if (type == "closeTag") {
196
  var err = false;
197
  if (curState.context) {
198
+ if (curState.context.tagName != tagName) {
199
+ if (Kludges.implicitlyClosed.hasOwnProperty(curState.context.tagName.toLowerCase())) {
200
+ popContext();
201
+ }
202
+ err = !curState.context || curState.context.tagName != tagName;
203
+ }
204
  } else {
205
  err = true;
206
  }
211
  }
212
  function endtag(startOfLine) {
213
  return function(type) {
214
+ var tagName = curState.tagName;
215
+ curState.tagName = null;
216
  if (type == "selfcloseTag" ||
217
+ (type == "endTag" && Kludges.autoSelfClosers.hasOwnProperty(tagName.toLowerCase()))) {
218
+ maybePopContext(tagName.toLowerCase());
219
  return cont();
220
+ }
221
+ if (type == "endTag") {
222
+ maybePopContext(tagName.toLowerCase());
223
+ pushContext(tagName, startOfLine);
224
+ return cont();
225
+ }
226
  return cont();
227
  };
228
  }
232
  if (type == "endTag") { popContext(); return cont(); }
233
  setStyle = "error";
234
  return cont(arguments.callee);
235
+ };
236
+ }
237
+ function maybePopContext(nextTagName) {
238
+ var parentTagName;
239
+ while (true) {
240
+ if (!curState.context) {
241
+ return;
242
+ }
243
+ parentTagName = curState.context.tagName.toLowerCase();
244
+ if (!Kludges.contextGrabbers.hasOwnProperty(parentTagName) ||
245
+ !Kludges.contextGrabbers[parentTagName].hasOwnProperty(nextTagName)) {
246
+ return;
247
+ }
248
+ popContext();
249
  }
250
  }
251
 
258
  function attribute(type) {
259
  if (type == "equals") return cont(attvalue, attributes);
260
  if (!Kludges.allowMissing) setStyle = "error";
261
+ else if (type == "word") setStyle = "attribute";
262
  return (type == "endTag" || type == "selfcloseTag") ? pass() : cont();
263
  }
264
  function attvalue(type) {
312
  else return 0;
313
  },
314
 
315
+ electricChars: "/",
 
 
 
 
 
 
316
 
317
+ configuration: parserConfig.htmlMode ? "html" : "xml"
318
  };
319
  });
320
 
321
+ CodeMirror.defineMIME("text/xml", "xml");
322
  CodeMirror.defineMIME("application/xml", "xml");
323
  if (!CodeMirror.mimeModes.hasOwnProperty("text/html"))
324
  CodeMirror.defineMIME("text/html", {name: "xml", htmlMode: true});
readme.txt CHANGED
@@ -3,8 +3,8 @@ Contributors: nixdns
3
  Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=TM7JTJY6HDTAQ
4
  Tags: html editor, syntax highlighter, plugin editor, syntax, highlighting, syntax hilighting
5
  Requires at least: 3.3
6
- Tested up to: 3.3
7
- Stable tag: 1.2.1
8
 
9
  Add syntax highlighting to the HTML editor.
10
 
@@ -17,20 +17,30 @@ Add syntax highlighting to the HTML editor.
17
  1. Upload the 'html-editor-syntax-highlighter' directory to the '/wp-content/plugins/' directory
18
  2. Activate the plugin on the 'Plugins' page
19
 
20
- = 1.0 =
21
- Initial release.
22
 
23
- = 1.1 =
24
- Bug fix (thanks to collinprice):
25
- when user has the visual editor disabled this plugin does not show up.
 
 
 
 
 
26
 
27
  = 1.2 =
28
  Bug fix:
29
- plugin does not work in new post/page.
 
 
 
 
 
 
 
 
 
30
 
31
- = 1.2.1 =
32
- – vertical resize for the editing box (works on FireFox, Chrome, Safari).
33
- – not working buttons/tags was hidden
34
 
35
  == Screenshots ==
36
  1. Syntax highlighting in the Post/Page HTML editor.
3
  Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=TM7JTJY6HDTAQ
4
  Tags: html editor, syntax highlighter, plugin editor, syntax, highlighting, syntax hilighting
5
  Requires at least: 3.3
6
+ Tested up to: 3.5.1
7
+ Stable tag: 1.3.0
8
 
9
  Add syntax highlighting to the HTML editor.
10
 
17
  1. Upload the 'html-editor-syntax-highlighter' directory to the '/wp-content/plugins/' directory
18
  2. Activate the plugin on the 'Plugins' page
19
 
20
+ == Changelog ==
 
21
 
22
+ = 1.3.0 =
23
+ * CodeMirror libruary updated to the version 3.02
24
+ * Added quicktags toolbar buttons
25
+ * Preserve the scroll position after update or page reload
26
+
27
+ = 1.2.1 =
28
+ * vertical resize for the editing box (works on FireFox, Chrome, Safari).
29
+ * not working buttons/tags was hidden
30
 
31
  = 1.2 =
32
  Bug fix:
33
+ * plugin does not work in new post/page.
34
+
35
+ = 1.1 =
36
+ Bug fix (thanks to collinprice):
37
+ * when user has the visual editor disabled this plugin does not show up.
38
+
39
+ = 1.0 =
40
+ Initial release.
41
+
42
+
43
 
 
 
 
44
 
45
  == Screenshots ==
46
  1. Syntax highlighting in the Post/Page HTML editor.
screenshot-1.png CHANGED
Binary file
screenshot-2.png CHANGED
Binary file