Code Snippets - Version 1.7.1.1

Version Description

  • Fix a minor bug with custom capabilities and admin menus
Download this release

Release Info

Developer bungeshea
Plugin Icon Code Snippets
Version 1.7.1.1
Comparing to
See all releases

Code changes from version 1.7.1 to 1.7.1.1

{images → assets}/menu-icon.png RENAMED
File without changes
assets/screen-icon.css CHANGED
@@ -1,10 +1,10 @@
1
- /**
2
- * Add the snippet screen icon to admin pages
3
- *
4
- * @package Code Snippets
5
- * @subpackage Assets
6
- */
7
-
8
- #icon-snippets.icon32 {
9
- background: url('../images/screen-icon.png') no-repeat scroll transparent;
10
- }
1
+ /**
2
+ * Add the snippet screen icon to admin pages
3
+ *
4
+ * @package Code Snippets
5
+ * @subpackage Assets
6
+ */
7
+
8
+ #icon-snippets.icon32 {
9
+ background: url(screen-icon.png) no-repeat scroll transparent;
10
+ }
{images → assets}/screen-icon.png RENAMED
File without changes
code-snippets.php CHANGED
@@ -16,7 +16,7 @@
16
  * Description: An easy, clean and simple way to add code snippets to your site. No need to edit to your theme's functions.php file again!
17
  * Author: Shea Bunge
18
  * Author URI: http://bungeshea.com
19
- * Version: 1.7.1
20
  * License: MIT
21
  * License URI: license.txt
22
  * Text Domain: code-snippets
@@ -53,7 +53,7 @@ final class Code_Snippets {
53
  * @access public
54
  * @var string A PHP-standardized version number string
55
  */
56
- public $version = '1.7.1';
57
 
58
  /**
59
  * Variables to hold plugin paths
@@ -512,25 +512,41 @@ final class Code_Snippets {
512
  /**
513
  * Check if the current user can perform some action on snippets or not
514
  *
515
- * If multisite, checks if *Enable Administration Menus: Snippets* is active
516
- * under the *Settings > Network Settings* network admin menu
517
- *
518
- * @uses current_user_can() To check if the current user can perform a task
519
  *
520
  * @param string $do_what The task to check against.
521
  * @return bool Whether the current user can perform this task or not
 
 
 
 
522
  */
523
  public function user_can( $do_what ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
524
 
525
  if ( is_multisite() ) {
526
 
527
  if ( in_array( 'snippets', get_site_option( 'menu_items' ) ) )
528
- return current_user_can( "{$do_what}_snippets" );
529
  else
530
- return current_user_can( "{$do_what}_network_snippets" );
531
 
532
  } else {
533
- return current_user_can( "{$do_what}_snippets" );
534
  }
535
  }
536
 
16
  * Description: An easy, clean and simple way to add code snippets to your site. No need to edit to your theme's functions.php file again!
17
  * Author: Shea Bunge
18
  * Author URI: http://bungeshea.com
19
+ * Version: 1.7.1.1
20
  * License: MIT
21
  * License URI: license.txt
22
  * Text Domain: code-snippets
53
  * @access public
54
  * @var string A PHP-standardized version number string
55
  */
56
+ public $version = '1.7.1.1';
57
 
58
  /**
59
  * Variables to hold plugin paths
512
  /**
513
  * Check if the current user can perform some action on snippets or not
514
  *
515
+ * @uses current_user_can() To check if the current user can perform a task
516
+ * @uses $this->get_cap() To get the required capability
 
 
517
  *
518
  * @param string $do_what The task to check against.
519
  * @return bool Whether the current user can perform this task or not
520
+ *
521
+ * @since 1.7.1.1 Moved logic to $this->get_cap() method
522
+ * @since 1.7.1
523
+ * @access public
524
  */
525
  public function user_can( $do_what ) {
526
+ return current_user_can( $this->get_cap( $do_what ) );
527
+ }
528
+
529
+ /**
530
+ * Get the required capability to perform a certain action on snippets.
531
+ * Does not check if the user has this capability or not.
532
+ *
533
+ * If multisite, checks if *Enable Administration Menus: Snippets* is active
534
+ * under the *Settings > Network Settings* network admin menu
535
+ *
536
+ * @since 1.7.1.1
537
+ * @access public
538
+ */
539
+ public function get_cap( $do_what ) {
540
 
541
  if ( is_multisite() ) {
542
 
543
  if ( in_array( 'snippets', get_site_option( 'menu_items' ) ) )
544
+ return "{$do_what}_snippets";
545
  else
546
+ return "{$do_what}_network_snippets";
547
 
548
  } else {
549
+ return "{$do_what}_snippets";
550
  }
551
  }
552
 
includes/class-admin.php CHANGED
@@ -181,7 +181,7 @@ class Code_Snippets_Admin {
181
  $this->manage_page = add_menu_page(
182
  __('Snippets', 'code-snippets'),
183
  __('Snippets', 'code-snippets'),
184
- is_multisite() ? 'manage_network_snippets' : 'manage_snippets',
185
  $this->manage_slug,
186
  array( $this, 'display_manage_menu' ),
187
  $menu_icon,
@@ -192,7 +192,7 @@ class Code_Snippets_Admin {
192
  $this->manage_slug,
193
  __('Snippets', 'code-snippets'),
194
  __('Manage', 'code-snippets'),
195
- $code_snippets->user_can( 'manage' ),
196
  $this->manage_slug,
197
  array( $this, 'display_manage_menu')
198
  );
@@ -204,7 +204,7 @@ class Code_Snippets_Admin {
204
  $this->manage_slug,
205
  $editing ? __('Edit Snippet', 'code-snippets') : __('Add New Snippet', 'code-snippets'),
206
  $editing ? __('Edit', 'code-snippets') : __('Add New', 'code-snippets'),
207
- $code_snippets->user_can( 'install' ),
208
  $this->single_slug,
209
  array( $this, 'display_single_menu' )
210
  );
@@ -236,7 +236,7 @@ class Code_Snippets_Admin {
236
  $this->manage_slug,
237
  __('Import Snippets', 'code-snippets'),
238
  __('Import', 'code-snippets'),
239
- $code_snippets->user_can( 'import' ),
240
  'import-code-snippets',
241
  array( $this, 'display_import_menu' )
242
  );
@@ -380,14 +380,14 @@ class Code_Snippets_Admin {
380
 
381
  wp_register_script(
382
  'codemirror',
383
- plugins_url( 'assets/codemirror/lib/codemirror.js', $code_snippets->file ),
384
  false,
385
  $codemirror_version
386
  );
387
 
388
  wp_register_style(
389
  'codemirror',
390
- plugins_url( 'assets/codemirror/lib/codemirror.css', $code_snippets->file ),
391
  false,
392
  $codemirror_version
393
  );
@@ -400,7 +400,7 @@ class Code_Snippets_Admin {
400
 
401
  wp_register_script(
402
  "codemirror-mode-$mode",
403
- plugins_url( "assets/codemirror/mode/$mode.js", $code_snippets->file ),
404
  array( 'codemirror' ),
405
  $codemirror_version
406
  );
@@ -414,7 +414,7 @@ class Code_Snippets_Admin {
414
 
415
  wp_register_script(
416
  "codemirror-addon-$addon",
417
- plugins_url( "assets/codemirror/addon/$addon.js", $code_snippets->file ),
418
  array( 'codemirror' ),
419
  $codemirror_version
420
  );
@@ -422,7 +422,7 @@ class Code_Snippets_Admin {
422
 
423
  wp_register_style(
424
  'codemirror-addon-dialog',
425
- plugins_url( 'assets/codemirror/addon/dialog.css', $code_snippets->file ),
426
  array( 'codemirror' ),
427
  $codemirror_version
428
  );
181
  $this->manage_page = add_menu_page(
182
  __('Snippets', 'code-snippets'),
183
  __('Snippets', 'code-snippets'),
184
+ $code_snippets->get_cap( 'manage' ),
185
  $this->manage_slug,
186
  array( $this, 'display_manage_menu' ),
187
  $menu_icon,
192
  $this->manage_slug,
193
  __('Snippets', 'code-snippets'),
194
  __('Manage', 'code-snippets'),
195
+ $code_snippets->get_cap( 'manage' ),
196
  $this->manage_slug,
197
  array( $this, 'display_manage_menu')
198
  );
204
  $this->manage_slug,
205
  $editing ? __('Edit Snippet', 'code-snippets') : __('Add New Snippet', 'code-snippets'),
206
  $editing ? __('Edit', 'code-snippets') : __('Add New', 'code-snippets'),
207
+ $code_snippets->get_cap( 'install' ),
208
  $this->single_slug,
209
  array( $this, 'display_single_menu' )
210
  );
236
  $this->manage_slug,
237
  __('Import Snippets', 'code-snippets'),
238
  __('Import', 'code-snippets'),
239
+ $code_snippets->get_cap( 'import' ),
240
  'import-code-snippets',
241
  array( $this, 'display_import_menu' )
242
  );
380
 
381
  wp_register_script(
382
  'codemirror',
383
+ plugins_url( 'vendor/codemirror/lib/codemirror.js', $code_snippets->file ),
384
  false,
385
  $codemirror_version
386
  );
387
 
388
  wp_register_style(
389
  'codemirror',
390
+ plugins_url( 'vendor/codemirror/lib/codemirror.css', $code_snippets->file ),
391
  false,
392
  $codemirror_version
393
  );
400
 
401
  wp_register_script(
402
  "codemirror-mode-$mode",
403
+ plugins_url( "vendor/codemirror/mode/$mode.js", $code_snippets->file ),
404
  array( 'codemirror' ),
405
  $codemirror_version
406
  );
414
 
415
  wp_register_script(
416
  "codemirror-addon-$addon",
417
+ plugins_url( "vendor/codemirror/addon/$addon.js", $code_snippets->file ),
418
  array( 'codemirror' ),
419
  $codemirror_version
420
  );
422
 
423
  wp_register_style(
424
  'codemirror-addon-dialog',
425
+ plugins_url( 'vendor/codemirror/addon/dialog.css', $code_snippets->file ),
426
  array( 'codemirror' ),
427
  $codemirror_version
428
  );
readme.txt CHANGED
@@ -4,7 +4,7 @@ Donate link: http://code-snippets.bungeshea.com/donate/
4
  Tags: code-snippets, snippets, code, php, network, multisite
5
  Requires at least: 3.3
6
  Tested up to: 3.6
7
- Stable tag: 1.7.1
8
  License: MIT
9
  License URI: license.txt
10
 
@@ -111,6 +111,9 @@ That's fantastic! Join me on [GitHub](https://github.com/bungeshea/code-snippets
111
 
112
  == Changelog ==
113
 
 
 
 
114
  = 1.7.1 =
115
  * Fix a bug with snippet being set as deactivated when saved
116
  * Updated PHP Documentation completely. [[View online](http://bungeshea.github.io/code-snippets/api)]
@@ -204,6 +207,9 @@ Plugin updates will be posted on the [plugin's homepage](http://code-snippets.bu
204
 
205
  == Upgrade Notice ==
206
 
 
 
 
207
  = 1.7.1 =
208
  Added German translation thanks to David Decker; bug fixes and improvements
209
 
4
  Tags: code-snippets, snippets, code, php, network, multisite
5
  Requires at least: 3.3
6
  Tested up to: 3.6
7
+ Stable tag: 1.7.1.1
8
  License: MIT
9
  License URI: license.txt
10
 
111
 
112
  == Changelog ==
113
 
114
+ = 1.7.1.1 =
115
+ * Fix a minor bug with custom capabilities and admin menus
116
+
117
  = 1.7.1 =
118
  * Fix a bug with snippet being set as deactivated when saved
119
  * Updated PHP Documentation completely. [[View online](http://bungeshea.github.io/code-snippets/api)]
207
 
208
  == Upgrade Notice ==
209
 
210
+ = 1.7.1.1 =
211
+ Fixes a minor bug with custom capabilities and admin menus
212
+
213
  = 1.7.1 =
214
  Added German translation thanks to David Decker; bug fixes and improvements
215
 
{assets → vendor}/codemirror/addon/matchbrackets.js RENAMED
@@ -1,74 +1,74 @@
1
- (function() {
2
- var ie_lt8 = /MSIE \d/.test(navigator.userAgent) &&
3
- (document.documentMode == null || document.documentMode < 8);
4
-
5
- var Pos = CodeMirror.Pos;
6
- // Disable brace matching in long lines, since it'll cause hugely slow updates
7
- var maxLineLen = 1000;
8
-
9
- var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<"};
10
- function findMatchingBracket(cm) {
11
- var cur = cm.getCursor(), line = cm.getLineHandle(cur.line), pos = cur.ch - 1;
12
- var match = (pos >= 0 && matching[line.text.charAt(pos)]) || matching[line.text.charAt(++pos)];
13
- if (!match) return null;
14
- var forward = match.charAt(1) == ">", d = forward ? 1 : -1;
15
- var style = cm.getTokenAt(Pos(cur.line, pos + 1)).type;
16
-
17
- var stack = [line.text.charAt(pos)], re = /[(){}[\]]/;
18
- function scan(line, lineNo, start) {
19
- if (!line.text) return;
20
- var pos = forward ? 0 : line.text.length - 1, end = forward ? line.text.length : -1;
21
- if (start != null) pos = start + d;
22
- for (; pos != end; pos += d) {
23
- var ch = line.text.charAt(pos);
24
- if (re.test(ch) && cm.getTokenAt(Pos(lineNo, pos + 1)).type == style) {
25
- var match = matching[ch];
26
- if (match.charAt(1) == ">" == forward) stack.push(ch);
27
- else if (stack.pop() != match.charAt(0)) return {pos: pos, match: false};
28
- else if (!stack.length) return {pos: pos, match: true};
29
- }
30
- }
31
- }
32
- for (var i = cur.line, found, e = forward ? Math.min(i + 100, cm.lineCount()) : Math.max(-1, i - 100); i != e; i+=d) {
33
- if (i == cur.line) found = scan(line, i, pos);
34
- else found = scan(cm.getLineHandle(i), i);
35
- if (found) break;
36
- }
37
- return {from: Pos(cur.line, pos), to: found && Pos(i, found.pos), match: found && found.match};
38
- }
39
-
40
- function matchBrackets(cm, autoclear) {
41
- var found = findMatchingBracket(cm);
42
- if (!found || cm.getLine(found.from.line).length > maxLineLen ||
43
- found.to && cm.getLine(found.to.line).length > maxLineLen)
44
- return;
45
-
46
- var style = found.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket";
47
- var one = cm.markText(found.from, Pos(found.from.line, found.from.ch + 1), {className: style});
48
- var two = found.to && cm.markText(found.to, Pos(found.to.line, found.to.ch + 1), {className: style});
49
- // Kludge to work around the IE bug from issue #1193, where text
50
- // input stops going to the textare whever this fires.
51
- if (ie_lt8 && cm.state.focused) cm.display.input.focus();
52
- var clear = function() {
53
- cm.operation(function() { one.clear(); two && two.clear(); });
54
- };
55
- if (autoclear) setTimeout(clear, 800);
56
- else return clear;
57
- }
58
-
59
- var currentlyHighlighted = null;
60
- function doMatchBrackets(cm) {
61
- cm.operation(function() {
62
- if (currentlyHighlighted) {currentlyHighlighted(); currentlyHighlighted = null;}
63
- if (!cm.somethingSelected()) currentlyHighlighted = matchBrackets(cm, false);
64
- });
65
- }
66
-
67
- CodeMirror.defineOption("matchBrackets", false, function(cm, val) {
68
- if (val) cm.on("cursorActivity", doMatchBrackets);
69
- else cm.off("cursorActivity", doMatchBrackets);
70
- });
71
-
72
- CodeMirror.defineExtension("matchBrackets", function() {matchBrackets(this, true);});
73
- CodeMirror.defineExtension("findMatchingBracket", function(){return findMatchingBracket(this);});
74
- })();
1
+ (function() {
2
+ var ie_lt8 = /MSIE \d/.test(navigator.userAgent) &&
3
+ (document.documentMode == null || document.documentMode < 8);
4
+
5
+ var Pos = CodeMirror.Pos;
6
+ // Disable brace matching in long lines, since it'll cause hugely slow updates
7
+ var maxLineLen = 1000;
8
+
9
+ var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<"};
10
+ function findMatchingBracket(cm) {
11
+ var cur = cm.getCursor(), line = cm.getLineHandle(cur.line), pos = cur.ch - 1;
12
+ var match = (pos >= 0 && matching[line.text.charAt(pos)]) || matching[line.text.charAt(++pos)];
13
+ if (!match) return null;
14
+ var forward = match.charAt(1) == ">", d = forward ? 1 : -1;
15
+ var style = cm.getTokenAt(Pos(cur.line, pos + 1)).type;
16
+
17
+ var stack = [line.text.charAt(pos)], re = /[(){}[\]]/;
18
+ function scan(line, lineNo, start) {
19
+ if (!line.text) return;
20
+ var pos = forward ? 0 : line.text.length - 1, end = forward ? line.text.length : -1;
21
+ if (start != null) pos = start + d;
22
+ for (; pos != end; pos += d) {
23
+ var ch = line.text.charAt(pos);
24
+ if (re.test(ch) && cm.getTokenAt(Pos(lineNo, pos + 1)).type == style) {
25
+ var match = matching[ch];
26
+ if (match.charAt(1) == ">" == forward) stack.push(ch);
27
+ else if (stack.pop() != match.charAt(0)) return {pos: pos, match: false};
28
+ else if (!stack.length) return {pos: pos, match: true};
29
+ }
30
+ }
31
+ }
32
+ for (var i = cur.line, found, e = forward ? Math.min(i + 100, cm.lineCount()) : Math.max(-1, i - 100); i != e; i+=d) {
33
+ if (i == cur.line) found = scan(line, i, pos);
34
+ else found = scan(cm.getLineHandle(i), i);
35
+ if (found) break;
36
+ }
37
+ return {from: Pos(cur.line, pos), to: found && Pos(i, found.pos), match: found && found.match};
38
+ }
39
+
40
+ function matchBrackets(cm, autoclear) {
41
+ var found = findMatchingBracket(cm);
42
+ if (!found || cm.getLine(found.from.line).length > maxLineLen ||
43
+ found.to && cm.getLine(found.to.line).length > maxLineLen)
44
+ return;
45
+
46
+ var style = found.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket";
47
+ var one = cm.markText(found.from, Pos(found.from.line, found.from.ch + 1), {className: style});
48
+ var two = found.to && cm.markText(found.to, Pos(found.to.line, found.to.ch + 1), {className: style});
49
+ // Kludge to work around the IE bug from issue #1193, where text
50
+ // input stops going to the textare whever this fires.
51
+ if (ie_lt8 && cm.state.focused) cm.display.input.focus();
52
+ var clear = function() {
53
+ cm.operation(function() { one.clear(); two && two.clear(); });
54
+ };
55
+ if (autoclear) setTimeout(clear, 800);
56
+ else return clear;
57
+ }
58
+
59
+ var currentlyHighlighted = null;
60
+ function doMatchBrackets(cm) {
61
+ cm.operation(function() {
62
+ if (currentlyHighlighted) {currentlyHighlighted(); currentlyHighlighted = null;}
63
+ if (!cm.somethingSelected()) currentlyHighlighted = matchBrackets(cm, false);
64
+ });
65
+ }
66
+
67
+ CodeMirror.defineOption("matchBrackets", false, function(cm, val) {
68
+ if (val) cm.on("cursorActivity", doMatchBrackets);
69
+ else cm.off("cursorActivity", doMatchBrackets);
70
+ });
71
+
72
+ CodeMirror.defineExtension("matchBrackets", function() {matchBrackets(this, true);});
73
+ CodeMirror.defineExtension("findMatchingBracket", function(){return findMatchingBracket(this);});
74
+ })();
{assets → vendor}/codemirror/addon/search.js RENAMED
@@ -1,131 +1,131 @@
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 searchOverlay(query) {
11
- if (typeof query == "string") return {token: function(stream) {
12
- if (stream.match(query)) return "searching";
13
- stream.next();
14
- stream.skipTo(query.charAt(0)) || stream.skipToEnd();
15
- }};
16
- return {token: function(stream) {
17
- if (stream.match(query)) return "searching";
18
- while (!stream.eol()) {
19
- stream.next();
20
- if (stream.match(query, false)) break;
21
- }
22
- }};
23
- }
24
-
25
- function SearchState() {
26
- this.posFrom = this.posTo = this.query = null;
27
- this.overlay = null;
28
- }
29
- function getSearchState(cm) {
30
- return cm._searchState || (cm._searchState = new SearchState());
31
- }
32
- function getSearchCursor(cm, query, pos) {
33
- // Heuristic: if the query string is all lowercase, do a case insensitive search.
34
- return cm.getSearchCursor(query, pos, typeof query == "string" && query == query.toLowerCase());
35
- }
36
- function dialog(cm, text, shortText, f) {
37
- if (cm.openDialog) cm.openDialog(text, f);
38
- else f(prompt(shortText, ""));
39
- }
40
- function confirmDialog(cm, text, shortText, fs) {
41
- if (cm.openConfirm) cm.openConfirm(text, fs);
42
- else if (confirm(shortText)) fs[0]();
43
- }
44
- function parseQuery(query) {
45
- var isRE = query.match(/^\/(.*)\/([a-z]*)$/);
46
- return isRE ? new RegExp(isRE[1], isRE[2].indexOf("i") == -1 ? "" : "i") : query;
47
- }
48
- var queryDialog =
49
- 'Search: <input type="text" style="width: 10em"/> <span style="color: #888">(Use /re/ syntax for regexp search)</span>';
50
- function doSearch(cm, rev) {
51
- var state = getSearchState(cm);
52
- if (state.query) return findNext(cm, rev);
53
- dialog(cm, queryDialog, "Search for:", function(query) {
54
- cm.operation(function() {
55
- if (!query || state.query) return;
56
- state.query = parseQuery(query);
57
- cm.removeOverlay(state.overlay);
58
- state.overlay = searchOverlay(query);
59
- cm.addOverlay(state.overlay);
60
- state.posFrom = state.posTo = cm.getCursor();
61
- findNext(cm, rev);
62
- });
63
- });
64
- }
65
- function findNext(cm, rev) {cm.operation(function() {
66
- var state = getSearchState(cm);
67
- var cursor = getSearchCursor(cm, state.query, rev ? state.posFrom : state.posTo);
68
- if (!cursor.find(rev)) {
69
- cursor = getSearchCursor(cm, state.query, rev ? CodeMirror.Pos(cm.lastLine()) : CodeMirror.Pos(cm.firstLine(), 0));
70
- if (!cursor.find(rev)) return;
71
- }
72
- cm.setSelection(cursor.from(), cursor.to());
73
- state.posFrom = cursor.from(); state.posTo = cursor.to();
74
- });}
75
- function clearSearch(cm) {cm.operation(function() {
76
- var state = getSearchState(cm);
77
- if (!state.query) return;
78
- state.query = null;
79
- cm.removeOverlay(state.overlay);
80
- });}
81
-
82
- var replaceQueryDialog =
83
- 'Replace: <input type="text" style="width: 10em"/> <span style="color: #888">(Use /re/ syntax for regexp search)</span>';
84
- var replacementQueryDialog = 'With: <input type="text" style="width: 10em"/>';
85
- var doReplaceConfirm = "Replace? <button>Yes</button> <button>No</button> <button>Stop</button>";
86
- function replace(cm, all) {
87
- dialog(cm, replaceQueryDialog, "Replace:", function(query) {
88
- if (!query) return;
89
- query = parseQuery(query);
90
- dialog(cm, replacementQueryDialog, "Replace with:", function(text) {
91
- if (all) {
92
- cm.operation(function() {
93
- for (var cursor = getSearchCursor(cm, query); cursor.findNext();) {
94
- if (typeof query != "string") {
95
- var match = cm.getRange(cursor.from(), cursor.to()).match(query);
96
- cursor.replace(text.replace(/\$(\d)/, function(_, i) {return match[i];}));
97
- } else cursor.replace(text);
98
- }
99
- });
100
- } else {
101
- clearSearch(cm);
102
- var cursor = getSearchCursor(cm, query, cm.getCursor());
103
- var advance = function() {
104
- var start = cursor.from(), match;
105
- if (!(match = cursor.findNext())) {
106
- cursor = getSearchCursor(cm, query);
107
- if (!(match = cursor.findNext()) ||
108
- (start && cursor.from().line == start.line && cursor.from().ch == start.ch)) return;
109
- }
110
- cm.setSelection(cursor.from(), cursor.to());
111
- confirmDialog(cm, doReplaceConfirm, "Replace?",
112
- [function() {doReplace(match);}, advance]);
113
- };
114
- var doReplace = function(match) {
115
- cursor.replace(typeof query == "string" ? text :
116
- text.replace(/\$(\d)/, function(_, i) {return match[i];}));
117
- advance();
118
- };
119
- advance();
120
- }
121
- });
122
- });
123
- }
124
-
125
- CodeMirror.commands.find = function(cm) {clearSearch(cm); doSearch(cm);};
126
- CodeMirror.commands.findNext = doSearch;
127
- CodeMirror.commands.findPrev = function(cm) {doSearch(cm, true);};
128
- CodeMirror.commands.clearSearch = clearSearch;
129
- CodeMirror.commands.replace = replace;
130
- CodeMirror.commands.replaceAll = function(cm) {replace(cm, true);};
131
- })();
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 searchOverlay(query) {
11
+ if (typeof query == "string") return {token: function(stream) {
12
+ if (stream.match(query)) return "searching";
13
+ stream.next();
14
+ stream.skipTo(query.charAt(0)) || stream.skipToEnd();
15
+ }};
16
+ return {token: function(stream) {
17
+ if (stream.match(query)) return "searching";
18
+ while (!stream.eol()) {
19
+ stream.next();
20
+ if (stream.match(query, false)) break;
21
+ }
22
+ }};
23
+ }
24
+
25
+ function SearchState() {
26
+ this.posFrom = this.posTo = this.query = null;
27
+ this.overlay = null;
28
+ }
29
+ function getSearchState(cm) {
30
+ return cm._searchState || (cm._searchState = new SearchState());
31
+ }
32
+ function getSearchCursor(cm, query, pos) {
33
+ // Heuristic: if the query string is all lowercase, do a case insensitive search.
34
+ return cm.getSearchCursor(query, pos, typeof query == "string" && query == query.toLowerCase());
35
+ }
36
+ function dialog(cm, text, shortText, f) {
37
+ if (cm.openDialog) cm.openDialog(text, f);
38
+ else f(prompt(shortText, ""));
39
+ }
40
+ function confirmDialog(cm, text, shortText, fs) {
41
+ if (cm.openConfirm) cm.openConfirm(text, fs);
42
+ else if (confirm(shortText)) fs[0]();
43
+ }
44
+ function parseQuery(query) {
45
+ var isRE = query.match(/^\/(.*)\/([a-z]*)$/);
46
+ return isRE ? new RegExp(isRE[1], isRE[2].indexOf("i") == -1 ? "" : "i") : query;
47
+ }
48
+ var queryDialog =
49
+ 'Search: <input type="text" style="width: 10em"/> <span style="color: #888">(Use /re/ syntax for regexp search)</span>';
50
+ function doSearch(cm, rev) {
51
+ var state = getSearchState(cm);
52
+ if (state.query) return findNext(cm, rev);
53
+ dialog(cm, queryDialog, "Search for:", function(query) {
54
+ cm.operation(function() {
55
+ if (!query || state.query) return;
56
+ state.query = parseQuery(query);
57
+ cm.removeOverlay(state.overlay);
58
+ state.overlay = searchOverlay(query);
59
+ cm.addOverlay(state.overlay);
60
+ state.posFrom = state.posTo = cm.getCursor();
61
+ findNext(cm, rev);
62
+ });
63
+ });
64
+ }
65
+ function findNext(cm, rev) {cm.operation(function() {
66
+ var state = getSearchState(cm);
67
+ var cursor = getSearchCursor(cm, state.query, rev ? state.posFrom : state.posTo);
68
+ if (!cursor.find(rev)) {
69
+ cursor = getSearchCursor(cm, state.query, rev ? CodeMirror.Pos(cm.lastLine()) : CodeMirror.Pos(cm.firstLine(), 0));
70
+ if (!cursor.find(rev)) return;
71
+ }
72
+ cm.setSelection(cursor.from(), cursor.to());
73
+ state.posFrom = cursor.from(); state.posTo = cursor.to();
74
+ });}
75
+ function clearSearch(cm) {cm.operation(function() {
76
+ var state = getSearchState(cm);
77
+ if (!state.query) return;
78
+ state.query = null;
79
+ cm.removeOverlay(state.overlay);
80
+ });}
81
+
82
+ var replaceQueryDialog =
83
+ 'Replace: <input type="text" style="width: 10em"/> <span style="color: #888">(Use /re/ syntax for regexp search)</span>';
84
+ var replacementQueryDialog = 'With: <input type="text" style="width: 10em"/>';
85
+ var doReplaceConfirm = "Replace? <button>Yes</button> <button>No</button> <button>Stop</button>";
86
+ function replace(cm, all) {
87
+ dialog(cm, replaceQueryDialog, "Replace:", function(query) {
88
+ if (!query) return;
89
+ query = parseQuery(query);
90
+ dialog(cm, replacementQueryDialog, "Replace with:", function(text) {
91
+ if (all) {
92
+ cm.operation(function() {
93
+ for (var cursor = getSearchCursor(cm, query); cursor.findNext();) {
94
+ if (typeof query != "string") {
95
+ var match = cm.getRange(cursor.from(), cursor.to()).match(query);
96
+ cursor.replace(text.replace(/\$(\d)/, function(_, i) {return match[i];}));
97
+ } else cursor.replace(text);
98
+ }
99
+ });
100
+ } else {
101
+ clearSearch(cm);
102
+ var cursor = getSearchCursor(cm, query, cm.getCursor());
103
+ var advance = function() {
104
+ var start = cursor.from(), match;
105
+ if (!(match = cursor.findNext())) {
106
+ cursor = getSearchCursor(cm, query);
107
+ if (!(match = cursor.findNext()) ||
108
+ (start && cursor.from().line == start.line && cursor.from().ch == start.ch)) return;
109
+ }
110
+ cm.setSelection(cursor.from(), cursor.to());
111
+ confirmDialog(cm, doReplaceConfirm, "Replace?",
112
+ [function() {doReplace(match);}, advance]);
113
+ };
114
+ var doReplace = function(match) {
115
+ cursor.replace(typeof query == "string" ? text :
116
+ text.replace(/\$(\d)/, function(_, i) {return match[i];}));
117
+ advance();
118
+ };
119
+ advance();
120
+ }
121
+ });
122
+ });
123
+ }
124
+
125
+ CodeMirror.commands.find = function(cm) {clearSearch(cm); doSearch(cm);};
126
+ CodeMirror.commands.findNext = doSearch;
127
+ CodeMirror.commands.findPrev = function(cm) {doSearch(cm, true);};
128
+ CodeMirror.commands.clearSearch = clearSearch;
129
+ CodeMirror.commands.replace = replace;
130
+ CodeMirror.commands.replaceAll = function(cm) {replace(cm, true);};
131
+ })();
{assets → vendor}/codemirror/addon/searchcursor.js RENAMED
@@ -1,130 +1,130 @@
1
- (function(){
2
- var Pos = CodeMirror.Pos;
3
-
4
- function SearchCursor(cm, query, pos, caseFold) {
5
- this.atOccurrence = false; this.cm = cm;
6
- if (caseFold == null && typeof query == "string") caseFold = false;
7
-
8
- pos = pos ? cm.clipPos(pos) : Pos(0, 0);
9
- this.pos = {from: pos, to: pos};
10
-
11
- // The matches method is filled in based on the type of query.
12
- // It takes a position and a direction, and returns an object
13
- // describing the next occurrence of the query, or null if no
14
- // more matches were found.
15
- if (typeof query != "string") { // Regexp match
16
- if (!query.global) query = new RegExp(query.source, query.ignoreCase ? "ig" : "g");
17
- this.matches = function(reverse, pos) {
18
- if (reverse) {
19
- query.lastIndex = 0;
20
- var line = cm.getLine(pos.line).slice(0, pos.ch), cutOff = 0, match, start;
21
- for (;;) {
22
- query.lastIndex = cutOff;
23
- var newMatch = query.exec(line);
24
- if (!newMatch) break;
25
- match = newMatch;
26
- start = match.index;
27
- cutOff = match.index + 1;
28
- }
29
- } else {
30
- query.lastIndex = pos.ch;
31
- var line = cm.getLine(pos.line), match = query.exec(line),
32
- start = match && match.index;
33
- }
34
- if (match && match[0])
35
- return {from: Pos(pos.line, start),
36
- to: Pos(pos.line, start + match[0].length),
37
- match: match};
38
- };
39
- } else { // String query
40
- if (caseFold) query = query.toLowerCase();
41
- var fold = caseFold ? function(str){return str.toLowerCase();} : function(str){return str;};
42
- var target = query.split("\n");
43
- // Different methods for single-line and multi-line queries
44
- if (target.length == 1) {
45
- if (!query.length) {
46
- // Empty string would match anything and never progress, so
47
- // we define it to match nothing instead.
48
- this.matches = function() {};
49
- } else {
50
- this.matches = function(reverse, pos) {
51
- var line = fold(cm.getLine(pos.line)), len = query.length, match;
52
- if (reverse ? (pos.ch >= len && (match = line.lastIndexOf(query, pos.ch - len)) != -1)
53
- : (match = line.indexOf(query, pos.ch)) != -1)
54
- return {from: Pos(pos.line, match),
55
- to: Pos(pos.line, match + len)};
56
- };
57
- }
58
- } else {
59
- this.matches = function(reverse, pos) {
60
- var ln = pos.line, idx = (reverse ? target.length - 1 : 0), match = target[idx], line = fold(cm.getLine(ln));
61
- var offsetA = (reverse ? line.indexOf(match) + match.length : line.lastIndexOf(match));
62
- if (reverse ? offsetA >= pos.ch || offsetA != match.length
63
- : offsetA <= pos.ch || offsetA != line.length - match.length)
64
- return;
65
- for (;;) {
66
- if (reverse ? !ln : ln == cm.lineCount() - 1) return;
67
- line = fold(cm.getLine(ln += reverse ? -1 : 1));
68
- match = target[reverse ? --idx : ++idx];
69
- if (idx > 0 && idx < target.length - 1) {
70
- if (line != match) return;
71
- else continue;
72
- }
73
- var offsetB = (reverse ? line.lastIndexOf(match) : line.indexOf(match) + match.length);
74
- if (reverse ? offsetB != line.length - match.length : offsetB != match.length)
75
- return;
76
- var start = Pos(pos.line, offsetA), end = Pos(ln, offsetB);
77
- return {from: reverse ? end : start, to: reverse ? start : end};
78
- }
79
- };
80
- }
81
- }
82
- }
83
-
84
- SearchCursor.prototype = {
85
- findNext: function() {return this.find(false);},
86
- findPrevious: function() {return this.find(true);},
87
-
88
- find: function(reverse) {
89
- var self = this, pos = this.cm.clipPos(reverse ? this.pos.from : this.pos.to);
90
- function savePosAndFail(line) {
91
- var pos = Pos(line, 0);
92
- self.pos = {from: pos, to: pos};
93
- self.atOccurrence = false;
94
- return false;
95
- }
96
-
97
- for (;;) {
98
- if (this.pos = this.matches(reverse, pos)) {
99
- if (!this.pos.from || !this.pos.to) { console.log(this.matches, this.pos); }
100
- this.atOccurrence = true;
101
- return this.pos.match || true;
102
- }
103
- if (reverse) {
104
- if (!pos.line) return savePosAndFail(0);
105
- pos = Pos(pos.line-1, this.cm.getLine(pos.line-1).length);
106
- }
107
- else {
108
- var maxLine = this.cm.lineCount();
109
- if (pos.line == maxLine - 1) return savePosAndFail(maxLine);
110
- pos = Pos(pos.line + 1, 0);
111
- }
112
- }
113
- },
114
-
115
- from: function() {if (this.atOccurrence) return this.pos.from;},
116
- to: function() {if (this.atOccurrence) return this.pos.to;},
117
-
118
- replace: function(newText) {
119
- if (!this.atOccurrence) return;
120
- var lines = CodeMirror.splitLines(newText);
121
- this.cm.replaceRange(lines, this.pos.from, this.pos.to);
122
- this.pos.to = Pos(this.pos.from.line + lines.length - 1,
123
- lines[lines.length - 1].length + (lines.length == 1 ? this.pos.from.ch : 0));
124
- }
125
- };
126
-
127
- CodeMirror.defineExtension("getSearchCursor", function(query, pos, caseFold) {
128
- return new SearchCursor(this, query, pos, caseFold);
129
- });
130
- })();
1
+ (function(){
2
+ var Pos = CodeMirror.Pos;
3
+
4
+ function SearchCursor(cm, query, pos, caseFold) {
5
+ this.atOccurrence = false; this.cm = cm;
6
+ if (caseFold == null && typeof query == "string") caseFold = false;
7
+
8
+ pos = pos ? cm.clipPos(pos) : Pos(0, 0);
9
+ this.pos = {from: pos, to: pos};
10
+
11
+ // The matches method is filled in based on the type of query.
12
+ // It takes a position and a direction, and returns an object
13
+ // describing the next occurrence of the query, or null if no
14
+ // more matches were found.
15
+ if (typeof query != "string") { // Regexp match
16
+ if (!query.global) query = new RegExp(query.source, query.ignoreCase ? "ig" : "g");
17
+ this.matches = function(reverse, pos) {
18
+ if (reverse) {
19
+ query.lastIndex = 0;
20
+ var line = cm.getLine(pos.line).slice(0, pos.ch), cutOff = 0, match, start;
21
+ for (;;) {
22
+ query.lastIndex = cutOff;
23
+ var newMatch = query.exec(line);
24
+ if (!newMatch) break;
25
+ match = newMatch;
26
+ start = match.index;
27
+ cutOff = match.index + 1;
28
+ }
29
+ } else {
30
+ query.lastIndex = pos.ch;
31
+ var line = cm.getLine(pos.line), match = query.exec(line),
32
+ start = match && match.index;
33
+ }
34
+ if (match && match[0])
35
+ return {from: Pos(pos.line, start),
36
+ to: Pos(pos.line, start + match[0].length),
37
+ match: match};
38
+ };
39
+ } else { // String query
40
+ if (caseFold) query = query.toLowerCase();
41
+ var fold = caseFold ? function(str){return str.toLowerCase();} : function(str){return str;};
42
+ var target = query.split("\n");
43
+ // Different methods for single-line and multi-line queries
44
+ if (target.length == 1) {
45
+ if (!query.length) {
46
+ // Empty string would match anything and never progress, so
47
+ // we define it to match nothing instead.
48
+ this.matches = function() {};
49
+ } else {
50
+ this.matches = function(reverse, pos) {
51
+ var line = fold(cm.getLine(pos.line)), len = query.length, match;
52
+ if (reverse ? (pos.ch >= len && (match = line.lastIndexOf(query, pos.ch - len)) != -1)
53
+ : (match = line.indexOf(query, pos.ch)) != -1)
54
+ return {from: Pos(pos.line, match),
55
+ to: Pos(pos.line, match + len)};
56
+ };
57
+ }
58
+ } else {
59
+ this.matches = function(reverse, pos) {
60
+ var ln = pos.line, idx = (reverse ? target.length - 1 : 0), match = target[idx], line = fold(cm.getLine(ln));
61
+ var offsetA = (reverse ? line.indexOf(match) + match.length : line.lastIndexOf(match));
62
+ if (reverse ? offsetA >= pos.ch || offsetA != match.length
63
+ : offsetA <= pos.ch || offsetA != line.length - match.length)
64
+ return;
65
+ for (;;) {
66
+ if (reverse ? !ln : ln == cm.lineCount() - 1) return;
67
+ line = fold(cm.getLine(ln += reverse ? -1 : 1));
68
+ match = target[reverse ? --idx : ++idx];
69
+ if (idx > 0 && idx < target.length - 1) {
70
+ if (line != match) return;
71
+ else continue;
72
+ }
73
+ var offsetB = (reverse ? line.lastIndexOf(match) : line.indexOf(match) + match.length);
74
+ if (reverse ? offsetB != line.length - match.length : offsetB != match.length)
75
+ return;
76
+ var start = Pos(pos.line, offsetA), end = Pos(ln, offsetB);
77
+ return {from: reverse ? end : start, to: reverse ? start : end};
78
+ }
79
+ };
80
+ }
81
+ }
82
+ }
83
+
84
+ SearchCursor.prototype = {
85
+ findNext: function() {return this.find(false);},
86
+ findPrevious: function() {return this.find(true);},
87
+
88
+ find: function(reverse) {
89
+ var self = this, pos = this.cm.clipPos(reverse ? this.pos.from : this.pos.to);
90
+ function savePosAndFail(line) {
91
+ var pos = Pos(line, 0);
92
+ self.pos = {from: pos, to: pos};
93
+ self.atOccurrence = false;
94
+ return false;
95
+ }
96
+
97
+ for (;;) {
98
+ if (this.pos = this.matches(reverse, pos)) {
99
+ if (!this.pos.from || !this.pos.to) { console.log(this.matches, this.pos); }
100
+ this.atOccurrence = true;
101
+ return this.pos.match || true;
102
+ }
103
+ if (reverse) {
104
+ if (!pos.line) return savePosAndFail(0);
105
+ pos = Pos(pos.line-1, this.cm.getLine(pos.line-1).length);
106
+ }
107
+ else {
108
+ var maxLine = this.cm.lineCount();
109
+ if (pos.line == maxLine - 1) return savePosAndFail(maxLine);
110
+ pos = Pos(pos.line + 1, 0);
111
+ }
112
+ }
113
+ },
114
+
115
+ from: function() {if (this.atOccurrence) return this.pos.from;},
116
+ to: function() {if (this.atOccurrence) return this.pos.to;},
117
+
118
+ replace: function(newText) {
119
+ if (!this.atOccurrence) return;
120
+ var lines = CodeMirror.splitLines(newText);
121
+ this.cm.replaceRange(lines, this.pos.from, this.pos.to);
122
+ this.pos.to = Pos(this.pos.from.line + lines.length - 1,
123
+ lines[lines.length - 1].length + (lines.length == 1 ? this.pos.from.ch : 0));
124
+ }
125
+ };
126
+
127
+ CodeMirror.defineExtension("getSearchCursor", function(query, pos, caseFold) {
128
+ return new SearchCursor(this, query, pos, caseFold);
129
+ });
130
+ })();
{assets → vendor}/codemirror/lib/codemirror.css RENAMED
@@ -1,246 +1,246 @@
1
- /* BASICS */
2
-
3
- .CodeMirror {
4
- /* Set height, width, borders, and global font properties here */
5
- font-family: monospace;
6
- height: 300px;
7
- }
8
- .CodeMirror-scroll {
9
- /* Set scrolling behaviour here */
10
- overflow: auto;
11
- }
12
-
13
- /* PADDING */
14
-
15
- .CodeMirror-lines {
16
- padding: 4px 0; /* Vertical padding around content */
17
- }
18
- .CodeMirror pre {
19
- padding: 0 4px; /* Horizontal padding of content */
20
- }
21
-
22
- .CodeMirror-scrollbar-filler {
23
- background-color: white; /* The little square between H and V scrollbars */
24
- }
25
-
26
- /* GUTTER */
27
-
28
- .CodeMirror-gutters {
29
- border-right: 1px solid #ddd;
30
- background-color: #f7f7f7;
31
- }
32
- .CodeMirror-linenumbers {}
33
- .CodeMirror-linenumber {
34
- padding: 0 3px 0 5px;
35
- min-width: 20px;
36
- text-align: right;
37
- color: #999;
38
- }
39
-
40
- /* CURSOR */
41
-
42
- .CodeMirror div.CodeMirror-cursor {
43
- border-left: 1px solid black;
44
- z-index: 3;
45
- }
46
- /* Shown when moving in bi-directional text */
47
- .CodeMirror div.CodeMirror-secondarycursor {
48
- border-left: 1px solid silver;
49
- }
50
- .CodeMirror.cm-keymap-fat-cursor div.CodeMirror-cursor {
51
- width: auto;
52
- border: 0;
53
- background: #7e7;
54
- z-index: 1;
55
- }
56
- /* Can style cursor different in overwrite (non-insert) mode */
57
- .CodeMirror div.CodeMirror-cursor.CodeMirror-overwrite {}
58
-
59
- .cm-tab { display: inline-block; }
60
-
61
- /* DEFAULT THEME */
62
-
63
- .cm-s-default .cm-keyword {color: #708;}
64
- .cm-s-default .cm-atom {color: #219;}
65
- .cm-s-default .cm-number {color: #164;}
66
- .cm-s-default .cm-def {color: #00f;}
67
- .cm-s-default .cm-variable {color: black;}
68
- .cm-s-default .cm-variable-2 {color: #05a;}
69
- .cm-s-default .cm-variable-3 {color: #085;}
70
- .cm-s-default .cm-property {color: black;}
71
- .cm-s-default .cm-operator {color: black;}
72
- .cm-s-default .cm-comment {color: #a50;}
73
- .cm-s-default .cm-string {color: #a11;}
74
- .cm-s-default .cm-string-2 {color: #f50;}
75
- .cm-s-default .cm-meta {color: #555;}
76
- .cm-s-default .cm-error {color: #f00;}
77
- .cm-s-default .cm-qualifier {color: #555;}
78
- .cm-s-default .cm-builtin {color: #30a;}
79
- .cm-s-default .cm-bracket {color: #997;}
80
- .cm-s-default .cm-tag {color: #170;}
81
- .cm-s-default .cm-attribute {color: #00c;}
82
- .cm-s-default .cm-header {color: blue;}
83
- .cm-s-default .cm-quote {color: #090;}
84
- .cm-s-default .cm-hr {color: #999;}
85
- .cm-s-default .cm-link {color: #00c;}
86
-
87
- .cm-negative {color: #d44;}
88
- .cm-positive {color: #292;}
89
- .cm-header, .cm-strong {font-weight: bold;}
90
- .cm-em {font-style: italic;}
91
- .cm-link {text-decoration: underline;}
92
-
93
- .cm-invalidchar {color: #f00;}
94
-
95
- div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
96
- div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
97
-
98
- /* STOP */
99
-
100
- /* The rest of this file contains styles related to the mechanics of
101
- the editor. You probably shouldn't touch them. */
102
-
103
- .CodeMirror {
104
- line-height: 1;
105
- position: relative;
106
- overflow: hidden;
107
- background: white;
108
- color: black;
109
- }
110
-
111
- .CodeMirror-scroll {
112
- /* 30px is the magic margin used to hide the element's real scrollbars */
113
- /* See overflow: hidden in .CodeMirror, and the paddings in .CodeMirror-sizer */
114
- margin-bottom: -30px; margin-right: -30px;
115
- padding-bottom: 30px; padding-right: 30px;
116
- height: 100%;
117
- outline: none; /* Prevent dragging from highlighting the element */
118
- position: relative;
119
- }
120
- .CodeMirror-sizer {
121
- position: relative;
122
- }
123
-
124
- /* The fake, visible scrollbars. Used to force redraw during scrolling
125
- before actuall scrolling happens, thus preventing shaking and
126
- flickering artifacts. */
127
- .CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler {
128
- position: absolute;
129
- z-index: 6;
130
- display: none;
131
- }
132
- .CodeMirror-vscrollbar {
133
- right: 0; top: 0;
134
- overflow-x: hidden;
135
- overflow-y: scroll;
136
- }
137
- .CodeMirror-hscrollbar {
138
- bottom: 0; left: 0;
139
- overflow-y: hidden;
140
- overflow-x: scroll;
141
- }
142
- .CodeMirror-scrollbar-filler {
143
- right: 0; bottom: 0;
144
- z-index: 6;
145
- }
146
-
147
- .CodeMirror-gutters {
148
- position: absolute; left: 0; top: 0;
149
- height: 100%;
150
- padding-bottom: 30px;
151
- z-index: 3;
152
- }
153
- .CodeMirror-gutter {
154
- height: 100%;
155
- padding-bottom: 30px;
156
- margin-bottom: -32px;
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; 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-widget {
205
- display: inline-block;
206
- }
207
-
208
- .CodeMirror-wrap .CodeMirror-scroll {
209
- overflow-x: hidden;
210
- }
211
-
212
- .CodeMirror-measure {
213
- position: absolute;
214
- width: 100%; height: 0px;
215
- overflow: hidden;
216
- visibility: hidden;
217
- }
218
- .CodeMirror-measure pre { position: static; }
219
-
220
- .CodeMirror div.CodeMirror-cursor {
221
- position: absolute;
222
- visibility: hidden;
223
- border-right: none;
224
- width: 0;
225
- }
226
- .CodeMirror-focused div.CodeMirror-cursor {
227
- visibility: visible;
228
- }
229
-
230
- .CodeMirror-selected { background: #d9d9d9; }
231
- .CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
232
-
233
- .cm-searching {
234
- background: #ffa;
235
- background: rgba(255, 255, 0, .4);
236
- }
237
-
238
- /* IE7 hack to prevent it from returning funny offsetTops on the spans */
239
- .CodeMirror span { *vertical-align: text-bottom; }
240
-
241
- @media print {
242
- /* Hide the cursor when printing */
243
- .CodeMirror div.CodeMirror-cursor {
244
- visibility: hidden;
245
- }
246
- }
1
+ /* BASICS */
2
+
3
+ .CodeMirror {
4
+ /* Set height, width, borders, and global font properties here */
5
+ font-family: monospace;
6
+ height: 300px;
7
+ }
8
+ .CodeMirror-scroll {
9
+ /* Set scrolling behaviour here */
10
+ overflow: auto;
11
+ }
12
+
13
+ /* PADDING */
14
+
15
+ .CodeMirror-lines {
16
+ padding: 4px 0; /* Vertical padding around content */
17
+ }
18
+ .CodeMirror pre {
19
+ padding: 0 4px; /* Horizontal padding of content */
20
+ }
21
+
22
+ .CodeMirror-scrollbar-filler {
23
+ background-color: white; /* The little square between H and V scrollbars */
24
+ }
25
+
26
+ /* GUTTER */
27
+
28
+ .CodeMirror-gutters {
29
+ border-right: 1px solid #ddd;
30
+ background-color: #f7f7f7;
31
+ }
32
+ .CodeMirror-linenumbers {}
33
+ .CodeMirror-linenumber {
34
+ padding: 0 3px 0 5px;
35
+ min-width: 20px;
36
+ text-align: right;
37
+ color: #999;
38
+ }
39
+
40
+ /* CURSOR */
41
+
42
+ .CodeMirror div.CodeMirror-cursor {
43
+ border-left: 1px solid black;
44
+ z-index: 3;
45
+ }
46
+ /* Shown when moving in bi-directional text */
47
+ .CodeMirror div.CodeMirror-secondarycursor {
48
+ border-left: 1px solid silver;
49
+ }
50
+ .CodeMirror.cm-keymap-fat-cursor div.CodeMirror-cursor {
51
+ width: auto;
52
+ border: 0;
53
+ background: #7e7;
54
+ z-index: 1;
55
+ }
56
+ /* Can style cursor different in overwrite (non-insert) mode */
57
+ .CodeMirror div.CodeMirror-cursor.CodeMirror-overwrite {}
58
+
59
+ .cm-tab { display: inline-block; }
60
+
61
+ /* DEFAULT THEME */
62
+
63
+ .cm-s-default .cm-keyword {color: #708;}
64
+ .cm-s-default .cm-atom {color: #219;}
65
+ .cm-s-default .cm-number {color: #164;}
66
+ .cm-s-default .cm-def {color: #00f;}
67
+ .cm-s-default .cm-variable {color: black;}
68
+ .cm-s-default .cm-variable-2 {color: #05a;}
69
+ .cm-s-default .cm-variable-3 {color: #085;}
70
+ .cm-s-default .cm-property {color: black;}
71
+ .cm-s-default .cm-operator {color: black;}
72
+ .cm-s-default .cm-comment {color: #a50;}
73
+ .cm-s-default .cm-string {color: #a11;}
74
+ .cm-s-default .cm-string-2 {color: #f50;}
75
+ .cm-s-default .cm-meta {color: #555;}
76
+ .cm-s-default .cm-error {color: #f00;}
77
+ .cm-s-default .cm-qualifier {color: #555;}
78
+ .cm-s-default .cm-builtin {color: #30a;}
79
+ .cm-s-default .cm-bracket {color: #997;}
80
+ .cm-s-default .cm-tag {color: #170;}
81
+ .cm-s-default .cm-attribute {color: #00c;}
82
+ .cm-s-default .cm-header {color: blue;}
83
+ .cm-s-default .cm-quote {color: #090;}
84
+ .cm-s-default .cm-hr {color: #999;}
85
+ .cm-s-default .cm-link {color: #00c;}
86
+
87
+ .cm-negative {color: #d44;}
88
+ .cm-positive {color: #292;}
89
+ .cm-header, .cm-strong {font-weight: bold;}
90
+ .cm-em {font-style: italic;}
91
+ .cm-link {text-decoration: underline;}
92
+
93
+ .cm-invalidchar {color: #f00;}
94
+
95
+ div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
96
+ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
97
+
98
+ /* STOP */
99
+
100
+ /* The rest of this file contains styles related to the mechanics of
101
+ the editor. You probably shouldn't touch them. */
102
+
103
+ .CodeMirror {
104
+ line-height: 1;
105
+ position: relative;
106
+ overflow: hidden;
107
+ background: white;
108
+ color: black;
109
+ }
110
+
111
+ .CodeMirror-scroll {
112
+ /* 30px is the magic margin used to hide the element's real scrollbars */
113
+ /* See overflow: hidden in .CodeMirror, and the paddings in .CodeMirror-sizer */
114
+ margin-bottom: -30px; margin-right: -30px;
115
+ padding-bottom: 30px; padding-right: 30px;
116
+ height: 100%;
117
+ outline: none; /* Prevent dragging from highlighting the element */
118
+ position: relative;
119
+ }
120
+ .CodeMirror-sizer {
121
+ position: relative;
122
+ }
123
+
124
+ /* The fake, visible scrollbars. Used to force redraw during scrolling
125
+ before actuall scrolling happens, thus preventing shaking and
126
+ flickering artifacts. */
127
+ .CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler {
128
+ position: absolute;
129
+ z-index: 6;
130
+ display: none;
131
+ }
132
+ .CodeMirror-vscrollbar {
133
+ right: 0; top: 0;
134
+ overflow-x: hidden;
135
+ overflow-y: scroll;
136
+ }
137
+ .CodeMirror-hscrollbar {
138
+ bottom: 0; left: 0;
139
+ overflow-y: hidden;
140
+ overflow-x: scroll;
141
+ }
142
+ .CodeMirror-scrollbar-filler {
143
+ right: 0; bottom: 0;
144
+ z-index: 6;
145
+ }
146
+
147
+ .CodeMirror-gutters {
148
+ position: absolute; left: 0; top: 0;
149
+ height: 100%;
150
+ padding-bottom: 30px;
151
+ z-index: 3;
152
+ }
153
+ .CodeMirror-gutter {
154
+ height: 100%;
155
+ padding-bottom: 30px;
156
+ margin-bottom: -32px;
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; 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-widget {
205
+ display: inline-block;
206
+ }
207
+
208
+ .CodeMirror-wrap .CodeMirror-scroll {
209
+ overflow-x: hidden;
210
+ }
211
+
212
+ .CodeMirror-measure {
213
+ position: absolute;
214
+ width: 100%; height: 0px;
215
+ overflow: hidden;
216
+ visibility: hidden;
217
+ }
218
+ .CodeMirror-measure pre { position: static; }
219
+
220
+ .CodeMirror div.CodeMirror-cursor {
221
+ position: absolute;
222
+ visibility: hidden;
223
+ border-right: none;
224
+ width: 0;
225
+ }
226
+ .CodeMirror-focused div.CodeMirror-cursor {
227
+ visibility: visible;
228
+ }
229
+
230
+ .CodeMirror-selected { background: #d9d9d9; }
231
+ .CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
232
+
233
+ .cm-searching {
234
+ background: #ffa;
235
+ background: rgba(255, 255, 0, .4);
236
+ }
237
+
238
+ /* IE7 hack to prevent it from returning funny offsetTops on the spans */
239
+ .CodeMirror span { *vertical-align: text-bottom; }
240
+
241
+ @media print {
242
+ /* Hide the cursor when printing */
243
+ .CodeMirror div.CodeMirror-cursor {
244
+ visibility: hidden;
245
+ }
246
+ }
{assets → vendor}/codemirror/lib/codemirror.js RENAMED
@@ -1,5516 +1,5516 @@
1
- // CodeMirror version 3.11
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
- var captureMiddleClick = gecko || (ie && !ie_lt9);
36
-
37
- // Optimize some code when these features are not used
38
- var sawReadOnlySpans = false, sawCollapsedSpans = false;
39
-
40
- // CONSTRUCTOR
41
-
42
- function CodeMirror(place, options) {
43
- if (!(this instanceof CodeMirror)) return new CodeMirror(place, options);
44
-
45
- this.options = options = options || {};
46
- // Determine effective options based on given values and defaults.
47
- for (var opt in defaults) if (!options.hasOwnProperty(opt) && defaults.hasOwnProperty(opt))
48
- options[opt] = defaults[opt];
49
- setGuttersForLineNumbers(options);
50
-
51
- var docStart = typeof options.value == "string" ? 0 : options.value.first;
52
- var display = this.display = makeDisplay(place, docStart);
53
- display.wrapper.CodeMirror = this;
54
- updateGutters(this);
55
- if (options.autofocus && !mobile) focusInput(this);
56
-
57
- this.state = {keyMaps: [],
58
- overlays: [],
59
- modeGen: 0,
60
- overwrite: false, focused: false,
61
- suppressEdits: false, pasteIncoming: false,
62
- draggingText: false,
63
- highlight: new Delayed()};
64
-
65
- themeChanged(this);
66
- if (options.lineWrapping)
67
- this.display.wrapper.className += " CodeMirror-wrap";
68
-
69
- var doc = options.value;
70
- if (typeof doc == "string") doc = new Doc(options.value, options.mode);
71
- operation(this, attachDoc)(this, doc);
72
-
73
- // Override magic textarea content restore that IE sometimes does
74
- // on our hidden textarea on reload
75
- if (ie) setTimeout(bind(resetInput, this, true), 20);
76
-
77
- registerEventHandlers(this);
78
- // IE throws unspecified error in certain cases, when
79
- // trying to access activeElement before onload
80
- var hasFocus; try { hasFocus = (document.activeElement == display.input); } catch(e) { }
81
- if (hasFocus || (options.autofocus && !mobile)) setTimeout(bind(onFocus, this), 20);
82
- else onBlur(this);
83
-
84
- operation(this, function() {
85
- for (var opt in optionHandlers)
86
- if (optionHandlers.propertyIsEnumerable(opt))
87
- optionHandlers[opt](this, options[opt], Init);
88
- for (var i = 0; i < initHooks.length; ++i) initHooks[i](this);
89
- })();
90
- }
91
-
92
- // DISPLAY CONSTRUCTOR
93
-
94
- function makeDisplay(place, docStart) {
95
- var d = {};
96
-
97
- var input = d.input = elt("textarea", null, null, "position: absolute; padding: 0; width: 1px; height: 1em; outline: none;");
98
- if (webkit) input.style.width = "1000px";
99
- else input.setAttribute("wrap", "off");
100
- // if border: 0; -- iOS fails to open keyboard (issue #1287)
101
- if (ios) input.style.border = "1px solid black";
102
- input.setAttribute("autocorrect", "off"); input.setAttribute("autocapitalize", "off");
103
-
104
- // Wraps and hides input textarea
105
- d.inputDiv = elt("div", [input], null, "overflow: hidden; position: relative; width: 3px; height: 0px;");
106
- // The actual fake scrollbars.
107
- d.scrollbarH = elt("div", [elt("div", null, null, "height: 1px")], "CodeMirror-hscrollbar");
108
- d.scrollbarV = elt("div", [elt("div", null, null, "width: 1px")], "CodeMirror-vscrollbar");
109
- d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler");
110
- // DIVs containing the selection and the actual code
111
- d.lineDiv = elt("div");
112
- d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1");
113
- // Blinky cursor, and element used to ensure cursor fits at the end of a line
114
- d.cursor = elt("div", "\u00a0", "CodeMirror-cursor");
115
- // Secondary cursor, shown when on a 'jump' in bi-directional text
116
- d.otherCursor = elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor");
117
- // Used to measure text size
118
- d.measure = elt("div", null, "CodeMirror-measure");
119
- // Wraps everything that needs to exist inside the vertically-padded coordinate system
120
- d.lineSpace = elt("div", [d.measure, d.selectionDiv, d.lineDiv, d.cursor, d.otherCursor],
121
- null, "position: relative; outline: none");
122
- // Moved around its parent to cover visible view
123
- d.mover = elt("div", [elt("div", [d.lineSpace], "CodeMirror-lines")], null, "position: relative");
124
- // Set to the height of the text, causes scrolling
125
- d.sizer = elt("div", [d.mover], "CodeMirror-sizer");
126
- // D is needed because behavior of elts with overflow: auto and padding is inconsistent across browsers
127
- d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerCutOff + "px; width: 1px;");
128
- // Will contain the gutters, if any
129
- d.gutters = elt("div", null, "CodeMirror-gutters");
130
- d.lineGutter = null;
131
- // Helper element to properly size the gutter backgrounds
132
- var scrollerInner = elt("div", [d.sizer, d.heightForcer, d.gutters], null, "position: relative; min-height: 100%");
133
- // Provides scrolling
134
- d.scroller = elt("div", [scrollerInner], "CodeMirror-scroll");
135
- d.scroller.setAttribute("tabIndex", "-1");
136
- // The element in which the editor lives.
137
- d.wrapper = elt("div", [d.inputDiv, d.scrollbarH, d.scrollbarV,
138
- d.scrollbarFiller, d.scroller], "CodeMirror");
139
- // Work around IE7 z-index bug
140
- if (ie_lt8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; }
141
- if (place.appendChild) place.appendChild(d.wrapper); else place(d.wrapper);
142
-
143
- // Needed to hide big blue blinking cursor on Mobile Safari
144
- if (ios) input.style.width = "0px";
145
- if (!webkit) d.scroller.draggable = true;
146
- // Needed to handle Tab key in KHTML
147
- if (khtml) { d.inputDiv.style.height = "1px"; d.inputDiv.style.position = "absolute"; }
148
- // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
149
- else if (ie_lt8) d.scrollbarH.style.minWidth = d.scrollbarV.style.minWidth = "18px";
150
-
151
- // Current visible range (may be bigger than the view window).
152
- d.viewOffset = d.lastSizeC = 0;
153
- d.showingFrom = d.showingTo = docStart;
154
-
155
- // Used to only resize the line number gutter when necessary (when
156
- // the amount of lines crosses a boundary that makes its width change)
157
- d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null;
158
- // See readInput and resetInput
159
- d.prevInput = "";
160
- // Set to true when a non-horizontal-scrolling widget is added. As
161
- // an optimization, widget aligning is skipped when d is false.
162
- d.alignWidgets = false;
163
- // Flag that indicates whether we currently expect input to appear
164
- // (after some event like 'keypress' or 'input') and are polling
165
- // intensively.
166
- d.pollingFast = false;
167
- // Self-resetting timeout for the poller
168
- d.poll = new Delayed();
169
- // True when a drag from the editor is active
170
- d.draggingText = false;
171
-
172
- d.cachedCharWidth = d.cachedTextHeight = null;
173
- d.measureLineCache = [];
174
- d.measureLineCachePos = 0;
175
-
176
- // Tracks when resetInput has punted to just putting a short
177
- // string instead of the (large) selection.
178
- d.inaccurateSelection = false;
179
-
180
- // Tracks the maximum line length so that the horizontal scrollbar
181
- // can be kept static when scrolling.
182
- d.maxLine = null;
183
- d.maxLineLength = 0;
184
- d.maxLineChanged = false;
185
-
186
- // Used for measuring wheel scrolling granularity
187
- d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null;
188
-
189
- return d;
190
- }
191
-
192
- // STATE UPDATES
193
-
194
- // Used to get the editor into a consistent state again when options change.
195
-
196
- function loadMode(cm) {
197
- cm.doc.mode = CodeMirror.getMode(cm.options, cm.doc.modeOption);
198
- cm.doc.iter(function(line) {
199
- if (line.stateAfter) line.stateAfter = null;
200
- if (line.styles) line.styles = null;
201
- });
202
- cm.doc.frontier = cm.doc.first;
203
- startWorker(cm, 100);
204
- cm.state.modeGen++;
205
- if (cm.curOp) regChange(cm);
206
- }
207
-
208
- function wrappingChanged(cm) {
209
- if (cm.options.lineWrapping) {
210
- cm.display.wrapper.className += " CodeMirror-wrap";
211
- cm.display.sizer.style.minWidth = "";
212
- } else {
213
- cm.display.wrapper.className = cm.display.wrapper.className.replace(" CodeMirror-wrap", "");
214
- computeMaxLength(cm);
215
- }
216
- estimateLineHeights(cm);
217
- regChange(cm);
218
- clearCaches(cm);
219
- setTimeout(function(){updateScrollbars(cm.display, cm.doc.height);}, 100);
220
- }
221
-
222
- function estimateHeight(cm) {
223
- var th = textHeight(cm.display), wrapping = cm.options.lineWrapping;
224
- var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3);
225
- return function(line) {
226
- if (lineIsHidden(cm.doc, line))
227
- return 0;
228
- else if (wrapping)
229
- return (Math.ceil(line.text.length / perLine) || 1) * th;
230
- else
231
- return th;
232
- };
233
- }
234
-
235
- function estimateLineHeights(cm) {
236
- var doc = cm.doc, est = estimateHeight(cm);
237
- doc.iter(function(line) {
238
- var estHeight = est(line);
239
- if (estHeight != line.height) updateLineHeight(line, estHeight);
240
- });
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
- regChange(cm);
258
- }
259
-
260
- function updateGutters(cm) {
261
- var gutters = cm.display.gutters, specs = cm.options.gutters;
262
- removeChildren(gutters);
263
- for (var i = 0; i < specs.length; ++i) {
264
- var gutterClass = specs[i];
265
- var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + gutterClass));
266
- if (gutterClass == "CodeMirror-linenumbers") {
267
- cm.display.lineGutter = gElt;
268
- gElt.style.width = (cm.display.lineNumWidth || 1) + "px";
269
- }
270
- }
271
- gutters.style.display = i ? "" : "none";
272
- }
273
-
274
- function lineLength(doc, line) {
275
- if (line.height == 0) return 0;
276
- var len = line.text.length, merged, cur = line;
277
- while (merged = collapsedSpanAtStart(cur)) {
278
- var found = merged.find();
279
- cur = getLine(doc, found.from.line);
280
- len += found.from.ch - found.to.ch;
281
- }
282
- cur = line;
283
- while (merged = collapsedSpanAtEnd(cur)) {
284
- var found = merged.find();
285
- len -= cur.text.length - found.from.ch;
286
- cur = getLine(doc, found.to.line);
287
- len += cur.text.length - found.to.ch;
288
- }
289
- return len;
290
- }
291
-
292
- function computeMaxLength(cm) {
293
- var d = cm.display, doc = cm.doc;
294
- d.maxLine = getLine(doc, doc.first);
295
- d.maxLineLength = lineLength(doc, d.maxLine);
296
- d.maxLineChanged = true;
297
- doc.iter(function(line) {
298
- var len = lineLength(doc, line);
299
- if (len > d.maxLineLength) {
300
- d.maxLineLength = len;
301
- d.maxLine = line;
302
- }
303
- });
304
- }
305
-
306
- // Make sure the gutters options contains the element
307
- // "CodeMirror-linenumbers" when the lineNumbers option is true.
308
- function setGuttersForLineNumbers(options) {
309
- var found = false;
310
- for (var i = 0; i < options.gutters.length; ++i) {
311
- if (options.gutters[i] == "CodeMirror-linenumbers") {
312
- if (options.lineNumbers) found = true;
313
- else options.gutters.splice(i--, 1);
314
- }
315
- }
316
- if (!found && options.lineNumbers)
317
- options.gutters.push("CodeMirror-linenumbers");
318
- }
319
-
320
- // SCROLLBARS
321
-
322
- // Re-synchronize the fake scrollbars with the actual size of the
323
- // content. Optionally force a scrollTop.
324
- function updateScrollbars(d /* display */, docHeight) {
325
- var totalHeight = docHeight + paddingVert(d);
326
- d.sizer.style.minHeight = d.heightForcer.style.top = totalHeight + "px";
327
- var scrollHeight = Math.max(totalHeight, d.scroller.scrollHeight);
328
- var needsH = d.scroller.scrollWidth > d.scroller.clientWidth;
329
- var needsV = scrollHeight > d.scroller.clientHeight;
330
- if (needsV) {
331
- d.scrollbarV.style.display = "block";
332
- d.scrollbarV.style.bottom = needsH ? scrollbarWidth(d.measure) + "px" : "0";
333
- d.scrollbarV.firstChild.style.height =
334
- (scrollHeight - d.scroller.clientHeight + d.scrollbarV.clientHeight) + "px";
335
- } else d.scrollbarV.style.display = "";
336
- if (needsH) {
337
- d.scrollbarH.style.display = "block";
338
- d.scrollbarH.style.right = needsV ? scrollbarWidth(d.measure) + "px" : "0";
339
- d.scrollbarH.firstChild.style.width =
340
- (d.scroller.scrollWidth - d.scroller.clientWidth + d.scrollbarH.clientWidth) + "px";
341
- } else d.scrollbarH.style.display = "";
342
- if (needsH && needsV) {
343
- d.scrollbarFiller.style.display = "block";
344
- d.scrollbarFiller.style.height = d.scrollbarFiller.style.width = scrollbarWidth(d.measure) + "px";
345
- } else d.scrollbarFiller.style.display = "";
346
-
347
- if (mac_geLion && scrollbarWidth(d.measure) === 0)
348
- d.scrollbarV.style.minWidth = d.scrollbarH.style.minHeight = mac_geMountainLion ? "18px" : "12px";
349
- }
350
-
351
- function visibleLines(display, doc, viewPort) {
352
- var top = display.scroller.scrollTop, height = display.wrapper.clientHeight;
353
- if (typeof viewPort == "number") top = viewPort;
354
- else if (viewPort) {top = viewPort.top; height = viewPort.bottom - viewPort.top;}
355
- top = Math.floor(top - paddingTop(display));
356
- var bottom = Math.ceil(top + height);
357
- return {from: lineAtHeight(doc, top), to: lineAtHeight(doc, bottom)};
358
- }
359
-
360
- // LINE NUMBERS
361
-
362
- function alignHorizontally(cm) {
363
- var display = cm.display;
364
- if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) return;
365
- var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft;
366
- var gutterW = display.gutters.offsetWidth, l = comp + "px";
367
- for (var n = display.lineDiv.firstChild; n; n = n.nextSibling) if (n.alignable) {
368
- for (var i = 0, a = n.alignable; i < a.length; ++i) a[i].style.left = l;
369
- }
370
- if (cm.options.fixedGutter)
371
- display.gutters.style.left = (comp + gutterW) + "px";
372
- }
373
-
374
- function maybeUpdateLineNumberWidth(cm) {
375
- if (!cm.options.lineNumbers) return false;
376
- var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display;
377
- if (last.length != display.lineNumChars) {
378
- var test = display.measure.appendChild(elt("div", [elt("div", last)],
379
- "CodeMirror-linenumber CodeMirror-gutter-elt"));
380
- var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW;
381
- display.lineGutter.style.width = "";
382
- display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding);
383
- display.lineNumWidth = display.lineNumInnerWidth + padding;
384
- display.lineNumChars = display.lineNumInnerWidth ? last.length : -1;
385
- display.lineGutter.style.width = display.lineNumWidth + "px";
386
- return true;
387
- }
388
- return false;
389
- }
390
-
391
- function lineNumberFor(options, i) {
392
- return String(options.lineNumberFormatter(i + options.firstLineNumber));
393
- }
394
- function compensateForHScroll(display) {
395
- return getRect(display.scroller).left - getRect(display.sizer).left;
396
- }
397
-
398
- // DISPLAY DRAWING
399
-
400
- function updateDisplay(cm, changes, viewPort) {
401
- var oldFrom = cm.display.showingFrom, oldTo = cm.display.showingTo;
402
- var updated = updateDisplayInner(cm, changes, viewPort);
403
- if (updated) {
404
- signalLater(cm, "update", cm);
405
- if (cm.display.showingFrom != oldFrom || cm.display.showingTo != oldTo)
406
- signalLater(cm, "viewportChange", cm, cm.display.showingFrom, cm.display.showingTo);
407
- }
408
- updateSelection(cm);
409
- updateScrollbars(cm.display, cm.doc.height);
410
-
411
- return updated;
412
- }
413
-
414
- // Uses a set of changes plus the current scroll position to
415
- // determine which DOM updates have to be made, and makes the
416
- // updates.
417
- function updateDisplayInner(cm, changes, viewPort) {
418
- var display = cm.display, doc = cm.doc;
419
- if (!display.wrapper.clientWidth) {
420
- display.showingFrom = display.showingTo = doc.first;
421
- display.viewOffset = 0;
422
- return;
423
- }
424
-
425
- // Compute the new visible window
426
- // If scrollTop is specified, use that to determine which lines
427
- // to render instead of the current scrollbar position.
428
- var visible = visibleLines(display, doc, viewPort);
429
- // Bail out if the visible area is already rendered and nothing changed.
430
- if (changes.length == 0 &&
431
- visible.from > display.showingFrom && visible.to < display.showingTo)
432
- return;
433
-
434
- if (maybeUpdateLineNumberWidth(cm))
435
- changes = [{from: doc.first, to: doc.first + doc.size}];
436
- var gutterW = display.sizer.style.marginLeft = display.gutters.offsetWidth + "px";
437
- display.scrollbarH.style.left = cm.options.fixedGutter ? gutterW : "0";
438
-
439
- // Used to determine which lines need their line numbers updated
440
- var positionsChangedFrom = Infinity;
441
- if (cm.options.lineNumbers)
442
- for (var i = 0; i < changes.length; ++i)
443
- if (changes[i].diff) { positionsChangedFrom = changes[i].from; break; }
444
-
445
- var end = doc.first + doc.size;
446
- var from = Math.max(visible.from - cm.options.viewportMargin, doc.first);
447
- var to = Math.min(end, visible.to + cm.options.viewportMargin);
448
- if (display.showingFrom < from && from - display.showingFrom < 20) from = Math.max(doc.first, display.showingFrom);
449
- if (display.showingTo > to && display.showingTo - to < 20) to = Math.min(end, display.showingTo);
450
- if (sawCollapsedSpans) {
451
- from = lineNo(visualLine(doc, getLine(doc, from)));
452
- while (to < end && lineIsHidden(doc, getLine(doc, to))) ++to;
453
- }
454
-
455
- // Create a range of theoretically intact lines, and punch holes
456
- // in that using the change info.
457
- var intact = [{from: Math.max(display.showingFrom, doc.first),
458
- to: Math.min(display.showingTo, end)}];
459
- if (intact[0].from >= intact[0].to) intact = [];
460
- else intact = computeIntact(intact, changes);
461
- // When merged lines are present, we might have to reduce the
462
- // intact ranges because changes in continued fragments of the
463
- // intact lines do require the lines to be redrawn.
464
- if (sawCollapsedSpans)
465
- for (var i = 0; i < intact.length; ++i) {
466
- var range = intact[i], merged;
467
- while (merged = collapsedSpanAtEnd(getLine(doc, range.to - 1))) {
468
- var newTo = merged.find().from.line;
469
- if (newTo > range.from) range.to = newTo;
470
- else { intact.splice(i--, 1); break; }
471
- }
472
- }
473
-
474
- // Clip off the parts that won't be visible
475
- var intactLines = 0;
476
- for (var i = 0; i < intact.length; ++i) {
477
- var range = intact[i];
478
- if (range.from < from) range.from = from;
479
- if (range.to > to) range.to = to;
480
- if (range.from >= range.to) intact.splice(i--, 1);
481
- else intactLines += range.to - range.from;
482
- }
483
- if (intactLines == to - from && from == display.showingFrom && to == display.showingTo) {
484
- updateViewOffset(cm);
485
- return;
486
- }
487
- intact.sort(function(a, b) {return a.from - b.from;});
488
-
489
- var focused = document.activeElement;
490
- if (intactLines < (to - from) * .7) display.lineDiv.style.display = "none";
491
- patchDisplay(cm, from, to, intact, positionsChangedFrom);
492
- display.lineDiv.style.display = "";
493
- if (document.activeElement != focused && focused.offsetHeight) focused.focus();
494
-
495
- var different = from != display.showingFrom || to != display.showingTo ||
496
- display.lastSizeC != display.wrapper.clientHeight;
497
- // This is just a bogus formula that detects when the editor is
498
- // resized or the font size changes.
499
- if (different) display.lastSizeC = display.wrapper.clientHeight;
500
- display.showingFrom = from; display.showingTo = to;
501
- startWorker(cm, 100);
502
-
503
- var prevBottom = display.lineDiv.offsetTop;
504
- for (var node = display.lineDiv.firstChild, height; node; node = node.nextSibling) if (node.lineObj) {
505
- if (ie_lt8) {
506
- var bot = node.offsetTop + node.offsetHeight;
507
- height = bot - prevBottom;
508
- prevBottom = bot;
509
- } else {
510
- var box = getRect(node);
511
- height = box.bottom - box.top;
512
- }
513
- var diff = node.lineObj.height - height;
514
- if (height < 2) height = textHeight(display);
515
- if (diff > .001 || diff < -.001) {
516
- updateLineHeight(node.lineObj, height);
517
- var widgets = node.lineObj.widgets;
518
- if (widgets) for (var i = 0; i < widgets.length; ++i)
519
- widgets[i].height = widgets[i].node.offsetHeight;
520
- }
521
- }
522
- updateViewOffset(cm);
523
-
524
- if (visibleLines(display, doc, viewPort).to > to)
525
- updateDisplayInner(cm, [], viewPort);
526
- return true;
527
- }
528
-
529
- function updateViewOffset(cm) {
530
- var off = cm.display.viewOffset = heightAtLine(cm, getLine(cm.doc, cm.display.showingFrom));
531
- // Position the mover div to align with the current virtual scroll position
532
- cm.display.mover.style.top = off + "px";
533
- }
534
-
535
- function computeIntact(intact, changes) {
536
- for (var i = 0, l = changes.length || 0; i < l; ++i) {
537
- var change = changes[i], intact2 = [], diff = change.diff || 0;
538
- for (var j = 0, l2 = intact.length; j < l2; ++j) {
539
- var range = intact[j];
540
- if (change.to <= range.from && change.diff) {
541
- intact2.push({from: range.from + diff, to: range.to + diff});
542
- } else if (change.to <= range.from || change.from >= range.to) {
543
- intact2.push(range);
544
- } else {
545
- if (change.from > range.from)
546
- intact2.push({from: range.from, to: change.from});
547
- if (change.to < range.to)
548
- intact2.push({from: change.to + diff, to: range.to + diff});
549
- }
550
- }
551
- intact = intact2;
552
- }
553
- return intact;
554
- }
555
-
556
- function getDimensions(cm) {
557
- var d = cm.display, left = {}, width = {};
558
- for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) {
559
- left[cm.options.gutters[i]] = n.offsetLeft;
560
- width[cm.options.gutters[i]] = n.offsetWidth;
561
- }
562
- return {fixedPos: compensateForHScroll(d),
563
- gutterTotalWidth: d.gutters.offsetWidth,
564
- gutterLeft: left,
565
- gutterWidth: width,
566
- wrapperWidth: d.wrapper.clientWidth};
567
- }
568
-
569
- function patchDisplay(cm, from, to, intact, updateNumbersFrom) {
570
- var dims = getDimensions(cm);
571
- var display = cm.display, lineNumbers = cm.options.lineNumbers;
572
- if (!intact.length && (!webkit || !cm.display.currentWheelTarget))
573
- removeChildren(display.lineDiv);
574
- var container = display.lineDiv, cur = container.firstChild;
575
-
576
- function rm(node) {
577
- var next = node.nextSibling;
578
- if (webkit && mac && cm.display.currentWheelTarget == node) {
579
- node.style.display = "none";
580
- node.lineObj = null;
581
- } else {
582
- node.parentNode.removeChild(node);
583
- }
584
- return next;
585
- }
586
-
587
- var nextIntact = intact.shift(), lineN = from;
588
- cm.doc.iter(from, to, function(line) {
589
- if (nextIntact && nextIntact.to == lineN) nextIntact = intact.shift();
590
- if (lineIsHidden(cm.doc, line)) {
591
- if (line.height != 0) updateLineHeight(line, 0);
592
- if (line.widgets && cur.previousSibling) for (var i = 0; i < line.widgets.length; ++i)
593
- if (line.widgets[i].showIfHidden) {
594
- var prev = cur.previousSibling;
595
- if (/pre/i.test(prev.nodeName)) {
596
- var wrap = elt("div", null, null, "position: relative");
597
- prev.parentNode.replaceChild(wrap, prev);
598
- wrap.appendChild(prev);
599
- prev = wrap;
600
- }
601
- var wnode = prev.appendChild(elt("div", [line.widgets[i].node], "CodeMirror-linewidget"));
602
- positionLineWidget(line.widgets[i], wnode, prev, dims);
603
- }
604
- } else if (nextIntact && nextIntact.from <= lineN && nextIntact.to > lineN) {
605
- // This line is intact. Skip to the actual node. Update its
606
- // line number if needed.
607
- while (cur.lineObj != line) cur = rm(cur);
608
- if (lineNumbers && updateNumbersFrom <= lineN && cur.lineNumber)
609
- setTextContent(cur.lineNumber, lineNumberFor(cm.options, lineN));
610
- cur = cur.nextSibling;
611
- } else {
612
- // For lines with widgets, make an attempt to find and reuse
613
- // the existing element, so that widgets aren't needlessly
614
- // removed and re-inserted into the dom
615
- if (line.widgets) for (var j = 0, search = cur, reuse; search && j < 20; ++j, search = search.nextSibling)
616
- if (search.lineObj == line && /div/i.test(search.nodeName)) { reuse = search; break; }
617
- // This line needs to be generated.
618
- var lineNode = buildLineElement(cm, line, lineN, dims, reuse);
619
- if (lineNode != reuse) {
620
- container.insertBefore(lineNode, cur);
621
- } else {
622
- while (cur != reuse) cur = rm(cur);
623
- cur = cur.nextSibling;
624
- }
625
-
626
- lineNode.lineObj = line;
627
- }
628
- ++lineN;
629
- });
630
- while (cur) cur = rm(cur);
631
- }
632
-
633
- function buildLineElement(cm, line, lineNo, dims, reuse) {
634
- var lineElement = lineContent(cm, line);
635
- var markers = line.gutterMarkers, display = cm.display, wrap;
636
-
637
- if (!cm.options.lineNumbers && !markers && !line.bgClass && !line.wrapClass && !line.widgets)
638
- return lineElement;
639
-
640
- // Lines with gutter elements, widgets or a background class need
641
- // to be wrapped again, and have the extra elements added to the
642
- // wrapper div
643
-
644
- if (reuse) {
645
- reuse.alignable = null;
646
- var isOk = true, widgetsSeen = 0;
647
- for (var n = reuse.firstChild, next; n; n = next) {
648
- next = n.nextSibling;
649
- if (!/\bCodeMirror-linewidget\b/.test(n.className)) {
650
- reuse.removeChild(n);
651
- } else {
652
- for (var i = 0, first = true; i < line.widgets.length; ++i) {
653
- var widget = line.widgets[i], isFirst = false;
654
- if (!widget.above) { isFirst = first; first = false; }
655
- if (widget.node == n.firstChild) {
656
- positionLineWidget(widget, n, reuse, dims);
657
- ++widgetsSeen;
658
- if (isFirst) reuse.insertBefore(lineElement, n);
659
- break;
660
- }
661
- }
662
- if (i == line.widgets.length) { isOk = false; break; }
663
- }
664
- }
665
- if (isOk && widgetsSeen == line.widgets.length) {
666
- wrap = reuse;
667
- reuse.className = line.wrapClass || "";
668
- }
669
- }
670
- if (!wrap) {
671
- wrap = elt("div", null, line.wrapClass, "position: relative");
672
- wrap.appendChild(lineElement);
673
- }
674
- // Kludge to make sure the styled element lies behind the selection (by z-index)
675
- if (line.bgClass)
676
- wrap.insertBefore(elt("div", null, line.bgClass + " CodeMirror-linebackground"), wrap.firstChild);
677
- if (cm.options.lineNumbers || markers) {
678
- var gutterWrap = wrap.insertBefore(elt("div", null, null, "position: absolute; left: " +
679
- (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px"),
680
- wrap.firstChild);
681
- if (cm.options.fixedGutter) (wrap.alignable || (wrap.alignable = [])).push(gutterWrap);
682
- if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"]))
683
- wrap.lineNumber = gutterWrap.appendChild(
684
- elt("div", lineNumberFor(cm.options, lineNo),
685
- "CodeMirror-linenumber CodeMirror-gutter-elt",
686
- "left: " + dims.gutterLeft["CodeMirror-linenumbers"] + "px; width: "
687
- + display.lineNumInnerWidth + "px"));
688
- if (markers)
689
- for (var k = 0; k < cm.options.gutters.length; ++k) {
690
- var id = cm.options.gutters[k], found = markers.hasOwnProperty(id) && markers[id];
691
- if (found)
692
- gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt", "left: " +
693
- dims.gutterLeft[id] + "px; width: " + dims.gutterWidth[id] + "px"));
694
- }
695
- }
696
- if (ie_lt8) wrap.style.zIndex = 2;
697
- if (line.widgets && wrap != reuse) for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
698
- var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget");
699
- positionLineWidget(widget, node, wrap, dims);
700
- if (widget.above)
701
- wrap.insertBefore(node, cm.options.lineNumbers && line.height != 0 ? gutterWrap : lineElement);
702
- else
703
- wrap.appendChild(node);
704
- signalLater(widget, "redraw");
705
- }
706
- return wrap;
707
- }
708
-
709
- function positionLineWidget(widget, node, wrap, dims) {
710
- if (widget.noHScroll) {
711
- (wrap.alignable || (wrap.alignable = [])).push(node);
712
- var width = dims.wrapperWidth;
713
- node.style.left = dims.fixedPos + "px";
714
- if (!widget.coverGutter) {
715
- width -= dims.gutterTotalWidth;
716
- node.style.paddingLeft = dims.gutterTotalWidth + "px";
717
- }
718
- node.style.width = width + "px";
719
- }
720
- if (widget.coverGutter) {
721
- node.style.zIndex = 5;
722
- node.style.position = "relative";
723
- if (!widget.noHScroll) node.style.marginLeft = -dims.gutterTotalWidth + "px";
724
- }
725
- }
726
-
727
- // SELECTION / CURSOR
728
-
729
- function updateSelection(cm) {
730
- var display = cm.display;
731
- var collapsed = posEq(cm.doc.sel.from, cm.doc.sel.to);
732
- if (collapsed || cm.options.showCursorWhenSelecting)
733
- updateSelectionCursor(cm);
734
- else
735
- display.cursor.style.display = display.otherCursor.style.display = "none";
736
- if (!collapsed)
737
- updateSelectionRange(cm);
738
- else
739
- display.selectionDiv.style.display = "none";
740
-
741
- // Move the hidden textarea near the cursor to prevent scrolling artifacts
742
- var headPos = cursorCoords(cm, cm.doc.sel.head, "div");
743
- var wrapOff = getRect(display.wrapper), lineOff = getRect(display.lineDiv);
744
- display.inputDiv.style.top = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
745
- headPos.top + lineOff.top - wrapOff.top)) + "px";
746
- display.inputDiv.style.left = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
747
- headPos.left + lineOff.left - wrapOff.left)) + "px";
748
- }
749
-
750
- // No selection, plain cursor
751
- function updateSelectionCursor(cm) {
752
- var display = cm.display, pos = cursorCoords(cm, cm.doc.sel.head, "div");
753
- display.cursor.style.left = pos.left + "px";
754
- display.cursor.style.top = pos.top + "px";
755
- display.cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px";
756
- display.cursor.style.display = "";
757
-
758
- if (pos.other) {
759
- display.otherCursor.style.display = "";
760
- display.otherCursor.style.left = pos.other.left + "px";
761
- display.otherCursor.style.top = pos.other.top + "px";
762
- display.otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px";
763
- } else { display.otherCursor.style.display = "none"; }
764
- }
765
-
766
- // Highlight selection
767
- function updateSelectionRange(cm) {
768
- var display = cm.display, doc = cm.doc, sel = cm.doc.sel;
769
- var fragment = document.createDocumentFragment();
770
- var clientWidth = display.lineSpace.offsetWidth, pl = paddingLeft(cm.display);
771
-
772
- function add(left, top, width, bottom) {
773
- if (top < 0) top = 0;
774
- fragment.appendChild(elt("div", null, "CodeMirror-selected", "position: absolute; left: " + left +
775
- "px; top: " + top + "px; width: " + (width == null ? clientWidth - left : width) +
776
- "px; height: " + (bottom - top) + "px"));
777
- }
778
-
779
- function drawForLine(line, fromArg, toArg, retTop) {
780
- var lineObj = getLine(doc, line);
781
- var lineLen = lineObj.text.length, rVal = retTop ? Infinity : -Infinity;
782
- function coords(ch) {
783
- return charCoords(cm, Pos(line, ch), "div", lineObj);
784
- }
785
-
786
- iterateBidiSections(getOrder(lineObj), fromArg || 0, toArg == null ? lineLen : toArg, function(from, to, dir) {
787
- var leftPos = coords(dir == "rtl" ? to - 1 : from);
788
- var rightPos = coords(dir == "rtl" ? from : to - 1);
789
- var left = leftPos.left, right = rightPos.right;
790
- if (rightPos.top - leftPos.top > 3) { // Different lines, draw top part
791
- add(left, leftPos.top, null, leftPos.bottom);
792
- left = pl;
793
- if (leftPos.bottom < rightPos.top) add(left, leftPos.bottom, null, rightPos.top);
794
- }
795
- if (toArg == null && to == lineLen) right = clientWidth;
796
- if (fromArg == null && from == 0) left = pl;
797
- rVal = retTop ? Math.min(rightPos.top, rVal) : Math.max(rightPos.bottom, rVal);
798
- if (left < pl + 1) left = pl;
799
- add(left, rightPos.top, right - left, rightPos.bottom);
800
- });
801
- return rVal;
802
- }
803
-
804
- if (sel.from.line == sel.to.line) {
805
- drawForLine(sel.from.line, sel.from.ch, sel.to.ch);
806
- } else {
807
- var fromObj = getLine(doc, sel.from.line);
808
- var cur = fromObj, merged, path = [sel.from.line, sel.from.ch], singleLine;
809
- while (merged = collapsedSpanAtEnd(cur)) {
810
- var found = merged.find();
811
- path.push(found.from.ch, found.to.line, found.to.ch);
812
- if (found.to.line == sel.to.line) {
813
- path.push(sel.to.ch);
814
- singleLine = true;
815
- break;
816
- }
817
- cur = getLine(doc, found.to.line);
818
- }
819
-
820
- // This is a single, merged line
821
- if (singleLine) {
822
- for (var i = 0; i < path.length; i += 3)
823
- drawForLine(path[i], path[i+1], path[i+2]);
824
- } else {
825
- var middleTop, middleBot, toObj = getLine(doc, sel.to.line);
826
- if (sel.from.ch)
827
- // Draw the first line of selection.
828
- middleTop = drawForLine(sel.from.line, sel.from.ch, null, false);
829
- else
830
- // Simply include it in the middle block.
831
- middleTop = heightAtLine(cm, fromObj) - display.viewOffset;
832
-
833
- if (!sel.to.ch)
834
- middleBot = heightAtLine(cm, toObj) - display.viewOffset;
835
- else
836
- middleBot = drawForLine(sel.to.line, collapsedSpanAtStart(toObj) ? null : 0, sel.to.ch, true);
837
-
838
- if (middleTop < middleBot) add(pl, middleTop, null, middleBot);
839
- }
840
- }
841
-
842
- removeChildrenAndAdd(display.selectionDiv, fragment);
843
- display.selectionDiv.style.display = "";
844
- }
845
-
846
- // Cursor-blinking
847
- function restartBlink(cm) {
848
- var display = cm.display;
849
- clearInterval(display.blinker);
850
- var on = true;
851
- display.cursor.style.visibility = display.otherCursor.style.visibility = "";
852
- display.blinker = setInterval(function() {
853
- if (!display.cursor.offsetHeight) return;
854
- display.cursor.style.visibility = display.otherCursor.style.visibility = (on = !on) ? "" : "hidden";
855
- }, cm.options.cursorBlinkRate);
856
- }
857
-
858
- // HIGHLIGHT WORKER
859
-
860
- function startWorker(cm, time) {
861
- if (cm.doc.mode.startState && cm.doc.frontier < cm.display.showingTo)
862
- cm.state.highlight.set(time, bind(highlightWorker, cm));
863
- }
864
-
865
- function highlightWorker(cm) {
866
- var doc = cm.doc;
867
- if (doc.frontier < doc.first) doc.frontier = doc.first;
868
- if (doc.frontier >= cm.display.showingTo) return;
869
- var end = +new Date + cm.options.workTime;
870
- var state = copyState(doc.mode, getStateBefore(cm, doc.frontier));
871
- var changed = [], prevChange;
872
- doc.iter(doc.frontier, Math.min(doc.first + doc.size, cm.display.showingTo + 500), function(line) {
873
- if (doc.frontier >= cm.display.showingFrom) { // Visible
874
- var oldStyles = line.styles;
875
- line.styles = highlightLine(cm, line, state);
876
- var ischange = !oldStyles || oldStyles.length != line.styles.length;
877
- for (var i = 0; !ischange && i < oldStyles.length; ++i) ischange = oldStyles[i] != line.styles[i];
878
- if (ischange) {
879
- if (prevChange && prevChange.end == doc.frontier) prevChange.end++;
880
- else changed.push(prevChange = {start: doc.frontier, end: doc.frontier + 1});
881
- }
882
- line.stateAfter = copyState(doc.mode, state);
883
- } else {
884
- processLine(cm, line, state);
885
- line.stateAfter = doc.frontier % 5 == 0 ? copyState(doc.mode, state) : null;
886
- }
887
- ++doc.frontier;
888
- if (+new Date > end) {
889
- startWorker(cm, cm.options.workDelay);
890
- return true;
891
- }
892
- });
893
- if (changed.length)
894
- operation(cm, function() {
895
- for (var i = 0; i < changed.length; ++i)
896
- regChange(this, changed[i].start, changed[i].end);
897
- })();
898
- }
899
-
900
- // Finds the line to start with when starting a parse. Tries to
901
- // find a line with a stateAfter, so that it can start with a
902
- // valid state. If that fails, it returns the line with the
903
- // smallest indentation, which tends to need the least context to
904
- // parse correctly.
905
- function findStartLine(cm, n) {
906
- var minindent, minline, doc = cm.doc;
907
- for (var search = n, lim = n - 100; search > lim; --search) {
908
- if (search <= doc.first) return doc.first;
909
- var line = getLine(doc, search - 1);
910
- if (line.stateAfter) return search;
911
- var indented = countColumn(line.text, null, cm.options.tabSize);
912
- if (minline == null || minindent > indented) {
913
- minline = search - 1;
914
- minindent = indented;
915
- }
916
- }
917
- return minline;
918
- }
919
-
920
- function getStateBefore(cm, n) {
921
- var doc = cm.doc, display = cm.display;
922
- if (!doc.mode.startState) return true;
923
- var pos = findStartLine(cm, n), state = pos > doc.first && getLine(doc, pos-1).stateAfter;
924
- if (!state) state = startState(doc.mode);
925
- else state = copyState(doc.mode, state);
926
- doc.iter(pos, n, function(line) {
927
- processLine(cm, line, state);
928
- var save = pos == n - 1 || pos % 5 == 0 || pos >= display.showingFrom && pos < display.showingTo;
929
- line.stateAfter = save ? copyState(doc.mode, state) : null;
930
- ++pos;
931
- });
932
- return state;
933
- }
934
-
935
- // POSITION MEASUREMENT
936
-
937
- function paddingTop(display) {return display.lineSpace.offsetTop;}
938
- function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight;}
939
- function paddingLeft(display) {
940
- var e = removeChildrenAndAdd(display.measure, elt("pre", null, null, "text-align: left")).appendChild(elt("span", "x"));
941
- return e.offsetLeft;
942
- }
943
-
944
- function measureChar(cm, line, ch, data) {
945
- var dir = -1;
946
- data = data || measureLine(cm, line);
947
-
948
- for (var pos = ch;; pos += dir) {
949
- var r = data[pos];
950
- if (r) break;
951
- if (dir < 0 && pos == 0) dir = 1;
952
- }
953
- return {left: pos < ch ? r.right : r.left,
954
- right: pos > ch ? r.left : r.right,
955
- top: r.top, bottom: r.bottom};
956
- }
957
-
958
- function findCachedMeasurement(cm, line) {
959
- var cache = cm.display.measureLineCache;
960
- for (var i = 0; i < cache.length; ++i) {
961
- var memo = cache[i];
962
- if (memo.text == line.text && memo.markedSpans == line.markedSpans &&
963
- cm.display.scroller.clientWidth == memo.width &&
964
- memo.classes == line.textClass + "|" + line.bgClass + "|" + line.wrapClass)
965
- return memo.measure;
966
- }
967
- }
968
-
969
- function measureLine(cm, line) {
970
- // First look in the cache
971
- var measure = findCachedMeasurement(cm, line);
972
- if (!measure) {
973
- // Failing that, recompute and store result in cache
974
- measure = measureLineInner(cm, line);
975
- var cache = cm.display.measureLineCache;
976
- var memo = {text: line.text, width: cm.display.scroller.clientWidth,
977
- markedSpans: line.markedSpans, measure: measure,
978
- classes: line.textClass + "|" + line.bgClass + "|" + line.wrapClass};
979
- if (cache.length == 16) cache[++cm.display.measureLineCachePos % 16] = memo;
980
- else cache.push(memo);
981
- }
982
- return measure;
983
- }
984
-
985
- function measureLineInner(cm, line) {
986
- var display = cm.display, measure = emptyArray(line.text.length);
987
- var pre = lineContent(cm, line, measure);
988
-
989
- // IE does not cache element positions of inline elements between
990
- // calls to getBoundingClientRect. This makes the loop below,
991
- // which gathers the positions of all the characters on the line,
992
- // do an amount of layout work quadratic to the number of
993
- // characters. When line wrapping is off, we try to improve things
994
- // by first subdividing the line into a bunch of inline blocks, so
995
- // that IE can reuse most of the layout information from caches
996
- // for those blocks. This does interfere with line wrapping, so it
997
- // doesn't work when wrapping is on, but in that case the
998
- // situation is slightly better, since IE does cache line-wrapping
999
- // information and only recomputes per-line.
1000
- if (ie && !ie_lt8 && !cm.options.lineWrapping && pre.childNodes.length > 100) {
1001
- var fragment = document.createDocumentFragment();
1002
- var chunk = 10, n = pre.childNodes.length;
1003
- for (var i = 0, chunks = Math.ceil(n / chunk); i < chunks; ++i) {
1004
- var wrap = elt("div", null, null, "display: inline-block");
1005
- for (var j = 0; j < chunk && n; ++j) {
1006
- wrap.appendChild(pre.firstChild);
1007
- --n;
1008
- }
1009
- fragment.appendChild(wrap);
1010
- }
1011
- pre.appendChild(fragment);
1012
- }
1013
-
1014
- removeChildrenAndAdd(display.measure, pre);
1015
-
1016
- var outer = getRect(display.lineDiv);
1017
- var vranges = [], data = emptyArray(line.text.length), maxBot = pre.offsetHeight;
1018
- // Work around an IE7/8 bug where it will sometimes have randomly
1019
- // replaced our pre with a clone at this point.
1020
- if (ie_lt9 && display.measure.first != pre)
1021
- removeChildrenAndAdd(display.measure, pre);
1022
-
1023
- for (var i = 0, cur; i < measure.length; ++i) if (cur = measure[i]) {
1024
- var size = getRect(cur);
1025
- var top = Math.max(0, size.top - outer.top), bot = Math.min(size.bottom - outer.top, maxBot);
1026
- for (var j = 0; j < vranges.length; j += 2) {
1027
- var rtop = vranges[j], rbot = vranges[j+1];
1028
- if (rtop > bot || rbot < top) continue;
1029
- if (rtop <= top && rbot >= bot ||
1030
- top <= rtop && bot >= rbot ||
1031
- Math.min(bot, rbot) - Math.max(top, rtop) >= (bot - top) >> 1) {
1032
- vranges[j] = Math.min(top, rtop);
1033
- vranges[j+1] = Math.max(bot, rbot);
1034
- break;
1035
- }
1036
- }
1037
- if (j == vranges.length) vranges.push(top, bot);
1038
- var right = size.right;
1039
- if (cur.measureRight) right = getRect(cur.measureRight).left;
1040
- data[i] = {left: size.left - outer.left, right: right - outer.left, top: j};
1041
- }
1042
- for (var i = 0, cur; i < data.length; ++i) if (cur = data[i]) {
1043
- var vr = cur.top;
1044
- cur.top = vranges[vr]; cur.bottom = vranges[vr+1];
1045
- }
1046
-
1047
- return data;
1048
- }
1049
-
1050
- function measureLineWidth(cm, line) {
1051
- var hasBadSpan = false;
1052
- if (line.markedSpans) for (var i = 0; i < line.markedSpans; ++i) {
1053
- var sp = line.markedSpans[i];
1054
- if (sp.collapsed && (sp.to == null || sp.to == line.text.length)) hasBadSpan = true;
1055
- }
1056
- var cached = !hasBadSpan && findCachedMeasurement(cm, line);
1057
- if (cached) return measureChar(cm, line, line.text.length, cached).right;
1058
-
1059
- var pre = lineContent(cm, line);
1060
- var end = pre.appendChild(zeroWidthElement(cm.display.measure));
1061
- removeChildrenAndAdd(cm.display.measure, pre);
1062
- return getRect(end).right - getRect(cm.display.lineDiv).left;
1063
- }
1064
-
1065
- function clearCaches(cm) {
1066
- cm.display.measureLineCache.length = cm.display.measureLineCachePos = 0;
1067
- cm.display.cachedCharWidth = cm.display.cachedTextHeight = null;
1068
- cm.display.maxLineChanged = true;
1069
- cm.display.lineNumChars = null;
1070
- }
1071
-
1072
- // Context is one of "line", "div" (display.lineDiv), "local"/null (editor), or "page"
1073
- function intoCoordSystem(cm, lineObj, rect, context) {
1074
- if (lineObj.widgets) for (var i = 0; i < lineObj.widgets.length; ++i) if (lineObj.widgets[i].above) {
1075
- var size = widgetHeight(lineObj.widgets[i]);
1076
- rect.top += size; rect.bottom += size;
1077
- }
1078
- if (context == "line") return rect;
1079
- if (!context) context = "local";
1080
- var yOff = heightAtLine(cm, lineObj);
1081
- if (context != "local") yOff -= cm.display.viewOffset;
1082
- if (context == "page") {
1083
- var lOff = getRect(cm.display.lineSpace);
1084
- yOff += lOff.top + (window.pageYOffset || (document.documentElement || document.body).scrollTop);
1085
- var xOff = lOff.left + (window.pageXOffset || (document.documentElement || document.body).scrollLeft);
1086
- rect.left += xOff; rect.right += xOff;
1087
- }
1088
- rect.top += yOff; rect.bottom += yOff;
1089
- return rect;
1090
- }
1091
-
1092
- // Context may be "window", "page", "div", or "local"/null
1093
- // Result is in local coords
1094
- function fromCoordSystem(cm, coords, context) {
1095
- if (context == "div") return coords;
1096
- var left = coords.left, top = coords.top;
1097
- if (context == "page") {
1098
- left -= window.pageXOffset || (document.documentElement || document.body).scrollLeft;
1099
- top -= window.pageYOffset || (document.documentElement || document.body).scrollTop;
1100
- }
1101
- var lineSpaceBox = getRect(cm.display.lineSpace);
1102
- left -= lineSpaceBox.left;
1103
- top -= lineSpaceBox.top;
1104
- if (context == "local" || !context) {
1105
- var editorBox = getRect(cm.display.wrapper);
1106
- left -= editorBox.left;
1107
- top -= editorBox.top;
1108
- }
1109
- return {left: left, top: top};
1110
- }
1111
-
1112
- function charCoords(cm, pos, context, lineObj) {
1113
- if (!lineObj) lineObj = getLine(cm.doc, pos.line);
1114
- return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch), context);
1115
- }
1116
-
1117
- function cursorCoords(cm, pos, context, lineObj, measurement) {
1118
- lineObj = lineObj || getLine(cm.doc, pos.line);
1119
- if (!measurement) measurement = measureLine(cm, lineObj);
1120
- function get(ch, right) {
1121
- var m = measureChar(cm, lineObj, ch, measurement);
1122
- if (right) m.left = m.right; else m.right = m.left;
1123
- return intoCoordSystem(cm, lineObj, m, context);
1124
- }
1125
- var order = getOrder(lineObj), ch = pos.ch;
1126
- if (!order) return get(ch);
1127
- var main, other, linedir = order[0].level;
1128
- for (var i = 0; i < order.length; ++i) {
1129
- var part = order[i], rtl = part.level % 2, nb, here;
1130
- if (part.from < ch && part.to > ch) return get(ch, rtl);
1131
- var left = rtl ? part.to : part.from, right = rtl ? part.from : part.to;
1132
- if (left == ch) {
1133
- // IE returns bogus offsets and widths for edges where the
1134
- // direction flips, but only for the side with the lower
1135
- // level. So we try to use the side with the higher level.
1136
- if (i && part.level < (nb = order[i-1]).level) here = get(nb.level % 2 ? nb.from : nb.to - 1, true);
1137
- else here = get(rtl && part.from != part.to ? ch - 1 : ch);
1138
- if (rtl == linedir) main = here; else other = here;
1139
- } else if (right == ch) {
1140
- var nb = i < order.length - 1 && order[i+1];
1141
- if (!rtl && nb && nb.from == nb.to) continue;
1142
- if (nb && part.level < nb.level) here = get(nb.level % 2 ? nb.to - 1 : nb.from);
1143
- else here = get(rtl ? ch : ch - 1, true);
1144
- if (rtl == linedir) main = here; else other = here;
1145
- }
1146
- }
1147
- if (linedir && !ch) other = get(order[0].to - 1);
1148
- if (!main) return other;
1149
- if (other) main.other = other;
1150
- return main;
1151
- }
1152
-
1153
- function PosMaybeOutside(line, ch, outside) {
1154
- var pos = new Pos(line, ch);
1155
- if (outside) pos.outside = true;
1156
- return pos;
1157
- }
1158
-
1159
- // Coords must be lineSpace-local
1160
- function coordsChar(cm, x, y) {
1161
- var doc = cm.doc;
1162
- y += cm.display.viewOffset;
1163
- if (y < 0) return PosMaybeOutside(doc.first, 0, true);
1164
- var lineNo = lineAtHeight(doc, y), last = doc.first + doc.size - 1;
1165
- if (lineNo > last)
1166
- return PosMaybeOutside(doc.first + doc.size - 1, getLine(doc, last).text.length, true);
1167
- if (x < 0) x = 0;
1168
-
1169
- for (;;) {
1170
- var lineObj = getLine(doc, lineNo);
1171
- var found = coordsCharInner(cm, lineObj, lineNo, x, y);
1172
- var merged = collapsedSpanAtEnd(lineObj);
1173
- var mergedPos = merged && merged.find();
1174
- if (merged && found.ch >= mergedPos.from.ch)
1175
- lineNo = mergedPos.to.line;
1176
- else
1177
- return found;
1178
- }
1179
- }
1180
-
1181
- function coordsCharInner(cm, lineObj, lineNo, x, y) {
1182
- var innerOff = y - heightAtLine(cm, lineObj);
1183
- var wrongLine = false, adjust = 2 * cm.display.wrapper.clientWidth;
1184
- var measurement = measureLine(cm, lineObj);
1185
-
1186
- function getX(ch) {
1187
- var sp = cursorCoords(cm, Pos(lineNo, ch), "line",
1188
- lineObj, measurement);
1189
- wrongLine = true;
1190
- if (innerOff > sp.bottom) return sp.left - adjust;
1191
- else if (innerOff < sp.top) return sp.left + adjust;
1192
- else wrongLine = false;
1193
- return sp.left;
1194
- }
1195
-
1196
- var bidi = getOrder(lineObj), dist = lineObj.text.length;
1197
- var from = lineLeft(lineObj), to = lineRight(lineObj);
1198
- var fromX = getX(from), fromOutside = wrongLine, toX = getX(to), toOutside = wrongLine;
1199
-
1200
- if (x > toX) return PosMaybeOutside(lineNo, to, toOutside);
1201
- // Do a binary search between these bounds.
1202
- for (;;) {
1203
- if (bidi ? to == from || to == moveVisually(lineObj, from, 1) : to - from <= 1) {
1204
- var after = x - fromX < toX - x, ch = after ? from : to;
1205
- while (isExtendingChar.test(lineObj.text.charAt(ch))) ++ch;
1206
- var pos = PosMaybeOutside(lineNo, ch, after ? fromOutside : toOutside);
1207
- pos.after = after;
1208
- return pos;
1209
- }
1210
- var step = Math.ceil(dist / 2), middle = from + step;
1211
- if (bidi) {
1212
- middle = from;
1213
- for (var i = 0; i < step; ++i) middle = moveVisually(lineObj, middle, 1);
1214
- }
1215
- var middleX = getX(middle);
1216
- if (middleX > x) {to = middle; toX = middleX; if (toOutside = wrongLine) toX += 1000; dist -= step;}
1217
- else {from = middle; fromX = middleX; fromOutside = wrongLine; dist = step;}
1218
- }
1219
- }
1220
-
1221
- var measureText;
1222
- function textHeight(display) {
1223
- if (display.cachedTextHeight != null) return display.cachedTextHeight;
1224
- if (measureText == null) {
1225
- measureText = elt("pre");
1226
- // Measure a bunch of lines, for browsers that compute
1227
- // fractional heights.
1228
- for (var i = 0; i < 49; ++i) {
1229
- measureText.appendChild(document.createTextNode("x"));
1230
- measureText.appendChild(elt("br"));
1231
- }
1232
- measureText.appendChild(document.createTextNode("x"));
1233
- }
1234
- removeChildrenAndAdd(display.measure, measureText);
1235
- var height = measureText.offsetHeight / 50;
1236
- if (height > 3) display.cachedTextHeight = height;
1237
- removeChildren(display.measure);
1238
- return height || 1;
1239
- }
1240
-
1241
- function charWidth(display) {
1242
- if (display.cachedCharWidth != null) return display.cachedCharWidth;
1243
- var anchor = elt("span", "x");
1244
- var pre = elt("pre", [anchor]);
1245
- removeChildrenAndAdd(display.measure, pre);
1246
- var width = anchor.offsetWidth;
1247
- if (width > 2) display.cachedCharWidth = width;
1248
- return width || 10;
1249
- }
1250
-
1251
- // OPERATIONS
1252
-
1253
- // Operations are used to wrap changes in such a way that each
1254
- // change won't have to update the cursor and display (which would
1255
- // be awkward, slow, and error-prone), but instead updates are
1256
- // batched and then all combined and executed at once.
1257
-
1258
- var nextOpId = 0;
1259
- function startOperation(cm) {
1260
- cm.curOp = {
1261
- // An array of ranges of lines that have to be updated. See
1262
- // updateDisplay.
1263
- changes: [],
1264
- updateInput: null,
1265
- userSelChange: null,
1266
- textChanged: null,
1267
- selectionChanged: false,
1268
- updateMaxLine: false,
1269
- updateScrollPos: false,
1270
- id: ++nextOpId
1271
- };
1272
- if (!delayedCallbackDepth++) delayedCallbacks = [];
1273
- }
1274
-
1275
- function endOperation(cm) {
1276
- var op = cm.curOp, doc = cm.doc, display = cm.display;
1277
- cm.curOp = null;
1278
-
1279
- if (op.updateMaxLine) computeMaxLength(cm);
1280
- if (display.maxLineChanged && !cm.options.lineWrapping) {
1281
- var width = measureLineWidth(cm, display.maxLine);
1282
- display.sizer.style.minWidth = Math.max(0, width + 3 + scrollerCutOff) + "px";
1283
- display.maxLineChanged = false;
1284
- var maxScrollLeft = Math.max(0, display.sizer.offsetLeft + display.sizer.offsetWidth - display.scroller.clientWidth);
1285
- if (maxScrollLeft < doc.scrollLeft && !op.updateScrollPos)
1286
- setScrollLeft(cm, Math.min(display.scroller.scrollLeft, maxScrollLeft), true);
1287
- }
1288
- var newScrollPos, updated;
1289
- if (op.updateScrollPos) {
1290
- newScrollPos = op.updateScrollPos;
1291
- } else if (op.selectionChanged && display.scroller.clientHeight) { // don't rescroll if not visible
1292
- var coords = cursorCoords(cm, doc.sel.head);
1293
- newScrollPos = calculateScrollPos(cm, coords.left, coords.top, coords.left, coords.bottom);
1294
- }
1295
- if (op.changes.length || newScrollPos && newScrollPos.scrollTop != null) {
1296
- updated = updateDisplay(cm, op.changes, newScrollPos && newScrollPos.scrollTop);
1297
- if (cm.display.scroller.offsetHeight) cm.doc.scrollTop = cm.display.scroller.scrollTop;
1298
- }
1299
- if (!updated && op.selectionChanged) updateSelection(cm);
1300
- if (op.updateScrollPos) {
1301
- display.scroller.scrollTop = display.scrollbarV.scrollTop = doc.scrollTop = newScrollPos.scrollTop;
1302
- display.scroller.scrollLeft = display.scrollbarH.scrollLeft = doc.scrollLeft = newScrollPos.scrollLeft;
1303
- alignHorizontally(cm);
1304
- } else if (newScrollPos) {
1305
- scrollCursorIntoView(cm);
1306
- }
1307
- if (op.selectionChanged) restartBlink(cm);
1308
-
1309
- if (cm.state.focused && op.updateInput)
1310
- resetInput(cm, op.userSelChange);
1311
-
1312
- var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers;
1313
- if (hidden) for (var i = 0; i < hidden.length; ++i)
1314
- if (!hidden[i].lines.length) signal(hidden[i], "hide");
1315
- if (unhidden) for (var i = 0; i < unhidden.length; ++i)
1316
- if (unhidden[i].lines.length) signal(unhidden[i], "unhide");
1317
-
1318
- var delayed;
1319
- if (!--delayedCallbackDepth) {
1320
- delayed = delayedCallbacks;
1321
- delayedCallbacks = null;
1322
- }
1323
- if (op.textChanged)
1324
- signal(cm, "change", cm, op.textChanged);
1325
- if (op.selectionChanged) signal(cm, "cursorActivity", cm);
1326
- if (delayed) for (var i = 0; i < delayed.length; ++i) delayed[i]();
1327
- }
1328
-
1329
- // Wraps a function in an operation. Returns the wrapped function.
1330
- function operation(cm1, f) {
1331
- return function() {
1332
- var cm = cm1 || this, withOp = !cm.curOp;
1333
- if (withOp) startOperation(cm);
1334
- try { var result = f.apply(cm, arguments); }
1335
- finally { if (withOp) endOperation(cm); }
1336
- return result;
1337
- };
1338
- }
1339
- function docOperation(f) {
1340
- return function() {
1341
- var withOp = this.cm && !this.cm.curOp, result;
1342
- if (withOp) startOperation(this.cm);
1343
- try { result = f.apply(this, arguments); }
1344
- finally { if (withOp) endOperation(this.cm); }
1345
- return result;
1346
- };
1347
- }
1348
- function runInOp(cm, f) {
1349
- var withOp = !cm.curOp, result;
1350
- if (withOp) startOperation(cm);
1351
- try { result = f(); }
1352
- finally { if (withOp) endOperation(cm); }
1353
- return result;
1354
- }
1355
-
1356
- function regChange(cm, from, to, lendiff) {
1357
- if (from == null) from = cm.doc.first;
1358
- if (to == null) to = cm.doc.first + cm.doc.size;
1359
- cm.curOp.changes.push({from: from, to: to, diff: lendiff});
1360
- }
1361
-
1362
- // INPUT HANDLING
1363
-
1364
- function slowPoll(cm) {
1365
- if (cm.display.pollingFast) return;
1366
- cm.display.poll.set(cm.options.pollInterval, function() {
1367
- readInput(cm);
1368
- if (cm.state.focused) slowPoll(cm);
1369
- });
1370
- }
1371
-
1372
- function fastPoll(cm) {
1373
- var missed = false;
1374
- cm.display.pollingFast = true;
1375
- function p() {
1376
- var changed = readInput(cm);
1377
- if (!changed && !missed) {missed = true; cm.display.poll.set(60, p);}
1378
- else {cm.display.pollingFast = false; slowPoll(cm);}
1379
- }
1380
- cm.display.poll.set(20, p);
1381
- }
1382
-
1383
- // prevInput is a hack to work with IME. If we reset the textarea
1384
- // on every change, that breaks IME. So we look for changes
1385
- // compared to the previous content instead. (Modern browsers have
1386
- // events that indicate IME taking place, but these are not widely
1387
- // supported or compatible enough yet to rely on.)
1388
- function readInput(cm) {
1389
- var input = cm.display.input, prevInput = cm.display.prevInput, doc = cm.doc, sel = doc.sel;
1390
- if (!cm.state.focused || hasSelection(input) || isReadOnly(cm)) return false;
1391
- var text = input.value;
1392
- if (text == prevInput && posEq(sel.from, sel.to)) return false;
1393
- // IE enjoys randomly deselecting our input's text when
1394
- // re-focusing. If the selection is gone but the cursor is at the
1395
- // start of the input, that's probably what happened.
1396
- if (ie && text && input.selectionStart === 0) {
1397
- resetInput(cm, true);
1398
- return false;
1399
- }
1400
- var withOp = !cm.curOp;
1401
- if (withOp) startOperation(cm);
1402
- sel.shift = false;
1403
- var same = 0, l = Math.min(prevInput.length, text.length);
1404
- while (same < l && prevInput[same] == text[same]) ++same;
1405
- var from = sel.from, to = sel.to;
1406
- if (same < prevInput.length)
1407
- from = Pos(from.line, from.ch - (prevInput.length - same));
1408
- else if (cm.state.overwrite && posEq(from, to) && !cm.state.pasteIncoming)
1409
- to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + (text.length - same)));
1410
- var updateInput = cm.curOp.updateInput;
1411
- makeChange(cm.doc, {from: from, to: to, text: splitLines(text.slice(same)),
1412
- origin: cm.state.pasteIncoming ? "paste" : "+input"}, "end");
1413
-
1414
- cm.curOp.updateInput = updateInput;
1415
- if (text.length > 1000 || text.indexOf("\n") > -1) input.value = cm.display.prevInput = "";
1416
- else cm.display.prevInput = text;
1417
- if (withOp) endOperation(cm);
1418
- cm.state.pasteIncoming = false;
1419
- return true;
1420
- }
1421
-
1422
- function resetInput(cm, user) {
1423
- var minimal, selected, doc = cm.doc;
1424
- if (!posEq(doc.sel.from, doc.sel.to)) {
1425
- cm.display.prevInput = "";
1426
- minimal = hasCopyEvent &&
1427
- (doc.sel.to.line - doc.sel.from.line > 100 || (selected = cm.getSelection()).length > 1000);
1428
- if (minimal) cm.display.input.value = "-";
1429
- else cm.display.input.value = selected || cm.getSelection();
1430
- if (cm.state.focused) selectInput(cm.display.input);
1431
- } else if (user) cm.display.prevInput = cm.display.input.value = "";
1432
- cm.display.inaccurateSelection = minimal;
1433
- }
1434
-
1435
- function focusInput(cm) {
1436
- if (cm.options.readOnly != "nocursor" && (!mobile || document.activeElement != cm.display.input))
1437
- cm.display.input.focus();
1438
- }
1439
-
1440
- function isReadOnly(cm) {
1441
- return cm.options.readOnly || cm.doc.cantEdit;
1442
- }
1443
-
1444
- // EVENT HANDLERS
1445
-
1446
- function registerEventHandlers(cm) {
1447
- var d = cm.display;
1448
- on(d.scroller, "mousedown", operation(cm, onMouseDown));
1449
- on(d.scroller, "dblclick", operation(cm, e_preventDefault));
1450
- on(d.lineSpace, "selectstart", function(e) {
1451
- if (!eventInWidget(d, e)) e_preventDefault(e);
1452
- });
1453
- // Gecko browsers fire contextmenu *after* opening the menu, at
1454
- // which point we can't mess with it anymore. Context menu is
1455
- // handled in onMouseDown for Gecko.
1456
- if (!captureMiddleClick) on(d.scroller, "contextmenu", function(e) {onContextMenu(cm, e);});
1457
-
1458
- on(d.scroller, "scroll", function() {
1459
- if (d.scroller.clientHeight) {
1460
- setScrollTop(cm, d.scroller.scrollTop);
1461
- setScrollLeft(cm, d.scroller.scrollLeft, true);
1462
- signal(cm, "scroll", cm);
1463
- }
1464
- });
1465
- on(d.scrollbarV, "scroll", function() {
1466
- if (d.scroller.clientHeight) setScrollTop(cm, d.scrollbarV.scrollTop);
1467
- });
1468
- on(d.scrollbarH, "scroll", function() {
1469
- if (d.scroller.clientHeight) setScrollLeft(cm, d.scrollbarH.scrollLeft);
1470
- });
1471
-
1472
- on(d.scroller, "mousewheel", function(e){onScrollWheel(cm, e);});
1473
- on(d.scroller, "DOMMouseScroll", function(e){onScrollWheel(cm, e);});
1474
-
1475
- function reFocus() { if (cm.state.focused) setTimeout(bind(focusInput, cm), 0); }
1476
- on(d.scrollbarH, "mousedown", reFocus);
1477
- on(d.scrollbarV, "mousedown", reFocus);
1478
- // Prevent wrapper from ever scrolling
1479
- on(d.wrapper, "scroll", function() { d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; });
1480
-
1481
- function onResize() {
1482
- // Might be a text scaling operation, clear size caches.
1483
- d.cachedCharWidth = d.cachedTextHeight = null;
1484
- clearCaches(cm);
1485
- runInOp(cm, bind(regChange, cm));
1486
- }
1487
- on(window, "resize", onResize);
1488
- // Above handler holds on to the editor and its data structures.
1489
- // Here we poll to unregister it when the editor is no longer in
1490
- // the document, so that it can be garbage-collected.
1491
- function unregister() {
1492
- for (var p = d.wrapper.parentNode; p && p != document.body; p = p.parentNode) {}
1493
- if (p) setTimeout(unregister, 5000);
1494
- else off(window, "resize", onResize);
1495
- }
1496
- setTimeout(unregister, 5000);
1497
-
1498
- on(d.input, "keyup", operation(cm, function(e) {
1499
- if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
1500
- if (e.keyCode == 16) cm.doc.sel.shift = false;
1501
- }));
1502
- on(d.input, "input", bind(fastPoll, cm));
1503
- on(d.input, "keydown", operation(cm, onKeyDown));
1504
- on(d.input, "keypress", operation(cm, onKeyPress));
1505
- on(d.input, "focus", bind(onFocus, cm));
1506
- on(d.input, "blur", bind(onBlur, cm));
1507
-
1508
- function drag_(e) {
1509
- if (cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e))) return;
1510
- e_stop(e);
1511
- }
1512
- if (cm.options.dragDrop) {
1513
- on(d.scroller, "dragstart", function(e){onDragStart(cm, e);});
1514
- on(d.scroller, "dragenter", drag_);
1515
- on(d.scroller, "dragover", drag_);
1516
- on(d.scroller, "drop", operation(cm, onDrop));
1517
- }
1518
- on(d.scroller, "paste", function(e){
1519
- if (eventInWidget(d, e)) return;
1520
- focusInput(cm);
1521
- fastPoll(cm);
1522
- });
1523
- on(d.input, "paste", function() {
1524
- cm.state.pasteIncoming = true;
1525
- fastPoll(cm);
1526
- });
1527
-
1528
- function prepareCopy() {
1529
- if (d.inaccurateSelection) {
1530
- d.prevInput = "";
1531
- d.inaccurateSelection = false;
1532
- d.input.value = cm.getSelection();
1533
- selectInput(d.input);
1534
- }
1535
- }
1536
- on(d.input, "cut", prepareCopy);
1537
- on(d.input, "copy", prepareCopy);
1538
-
1539
- // Needed to handle Tab key in KHTML
1540
- if (khtml) on(d.sizer, "mouseup", function() {
1541
- if (document.activeElement == d.input) d.input.blur();
1542
- focusInput(cm);
1543
- });
1544
- }
1545
-
1546
- function eventInWidget(display, e) {
1547
- for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {
1548
- if (!n) return true;
1549
- if (/\bCodeMirror-(?:line)?widget\b/.test(n.className) ||
1550
- n.parentNode == display.sizer && n != display.mover) return true;
1551
- }
1552
- }
1553
-
1554
- function posFromMouse(cm, e, liberal) {
1555
- var display = cm.display;
1556
- if (!liberal) {
1557
- var target = e_target(e);
1558
- if (target == display.scrollbarH || target == display.scrollbarH.firstChild ||
1559
- target == display.scrollbarV || target == display.scrollbarV.firstChild ||
1560
- target == display.scrollbarFiller) return null;
1561
- }
1562
- var x, y, space = getRect(display.lineSpace);
1563
- // Fails unpredictably on IE[67] when mouse is dragged around quickly.
1564
- try { x = e.clientX; y = e.clientY; } catch (e) { return null; }
1565
- return coordsChar(cm, x - space.left, y - space.top);
1566
- }
1567
-
1568
- var lastClick, lastDoubleClick;
1569
- function onMouseDown(e) {
1570
- var cm = this, display = cm.display, doc = cm.doc, sel = doc.sel;
1571
- sel.shift = e.shiftKey;
1572
-
1573
- if (eventInWidget(display, e)) {
1574
- if (!webkit) {
1575
- display.scroller.draggable = false;
1576
- setTimeout(function(){display.scroller.draggable = true;}, 100);
1577
- }
1578
- return;
1579
- }
1580
- if (clickInGutter(cm, e)) return;
1581
- var start = posFromMouse(cm, e);
1582
-
1583
- switch (e_button(e)) {
1584
- case 3:
1585
- if (captureMiddleClick) onContextMenu.call(cm, cm, e);
1586
- return;
1587
- case 2:
1588
- if (start) extendSelection(cm.doc, start);
1589
- setTimeout(bind(focusInput, cm), 20);
1590
- e_preventDefault(e);
1591
- return;
1592
- }
1593
- // For button 1, if it was clicked inside the editor
1594
- // (posFromMouse returning non-null), we have to adjust the
1595
- // selection.
1596
- if (!start) {if (e_target(e) == display.scroller) e_preventDefault(e); return;}
1597
-
1598
- if (!cm.state.focused) onFocus(cm);
1599
-
1600
- var now = +new Date, type = "single";
1601
- if (lastDoubleClick && lastDoubleClick.time > now - 400 && posEq(lastDoubleClick.pos, start)) {
1602
- type = "triple";
1603
- e_preventDefault(e);
1604
- setTimeout(bind(focusInput, cm), 20);
1605
- selectLine(cm, start.line);
1606
- } else if (lastClick && lastClick.time > now - 400 && posEq(lastClick.pos, start)) {
1607
- type = "double";
1608
- lastDoubleClick = {time: now, pos: start};
1609
- e_preventDefault(e);
1610
- var word = findWordAt(getLine(doc, start.line).text, start);
1611
- extendSelection(cm.doc, word.from, word.to);
1612
- } else { lastClick = {time: now, pos: start}; }
1613
-
1614
- var last = start;
1615
- if (cm.options.dragDrop && dragAndDrop && !isReadOnly(cm) && !posEq(sel.from, sel.to) &&
1616
- !posLess(start, sel.from) && !posLess(sel.to, start) && type == "single") {
1617
- var dragEnd = operation(cm, function(e2) {
1618
- if (webkit) display.scroller.draggable = false;
1619
- cm.state.draggingText = false;
1620
- off(document, "mouseup", dragEnd);
1621
- off(display.scroller, "drop", dragEnd);
1622
- if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) {
1623
- e_preventDefault(e2);
1624
- extendSelection(cm.doc, start);
1625
- focusInput(cm);
1626
- }
1627
- });
1628
- // Let the drag handler handle this.
1629
- if (webkit) display.scroller.draggable = true;
1630
- cm.state.draggingText = dragEnd;
1631
- // IE's approach to draggable
1632
- if (display.scroller.dragDrop) display.scroller.dragDrop();
1633
- on(document, "mouseup", dragEnd);
1634
- on(display.scroller, "drop", dragEnd);
1635
- return;
1636
- }
1637
- e_preventDefault(e);
1638
- if (type == "single") extendSelection(cm.doc, clipPos(doc, start));
1639
-
1640
- var startstart = sel.from, startend = sel.to;
1641
-
1642
- function doSelect(cur) {
1643
- if (type == "single") {
1644
- extendSelection(cm.doc, clipPos(doc, start), cur);
1645
- return;
1646
- }
1647
-
1648
- startstart = clipPos(doc, startstart);
1649
- startend = clipPos(doc, startend);
1650
- if (type == "double") {
1651
- var word = findWordAt(getLine(doc, cur.line).text, cur);
1652
- if (posLess(cur, startstart)) extendSelection(cm.doc, word.from, startend);
1653
- else extendSelection(cm.doc, startstart, word.to);
1654
- } else if (type == "triple") {
1655
- if (posLess(cur, startstart)) extendSelection(cm.doc, startend, clipPos(doc, Pos(cur.line, 0)));
1656
- else extendSelection(cm.doc, startstart, clipPos(doc, Pos(cur.line + 1, 0)));
1657
- }
1658
- }
1659
-
1660
- var editorSize = getRect(display.wrapper);
1661
- // Used to ensure timeout re-tries don't fire when another extend
1662
- // happened in the meantime (clearTimeout isn't reliable -- at
1663
- // least on Chrome, the timeouts still happen even when cleared,
1664
- // if the clear happens after their scheduled firing time).
1665
- var counter = 0;
1666
-
1667
- function extend(e) {
1668
- var curCount = ++counter;
1669
- var cur = posFromMouse(cm, e, true);
1670
- if (!cur) return;
1671
- if (!posEq(cur, last)) {
1672
- if (!cm.state.focused) onFocus(cm);
1673
- last = cur;
1674
- doSelect(cur);
1675
- var visible = visibleLines(display, doc);
1676
- if (cur.line >= visible.to || cur.line < visible.from)
1677
- setTimeout(operation(cm, function(){if (counter == curCount) extend(e);}), 150);
1678
- } else {
1679
- var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0;
1680
- if (outside) setTimeout(operation(cm, function() {
1681
- if (counter != curCount) return;
1682
- display.scroller.scrollTop += outside;
1683
- extend(e);
1684
- }), 50);
1685
- }
1686
- }
1687
-
1688
- function done(e) {
1689
- counter = Infinity;
1690
- var cur = posFromMouse(cm, e);
1691
- if (cur) doSelect(cur);
1692
- e_preventDefault(e);
1693
- focusInput(cm);
1694
- off(document, "mousemove", move);
1695
- off(document, "mouseup", up);
1696
- }
1697
-
1698
- var move = operation(cm, function(e) {
1699
- if (!ie && !e_button(e)) done(e);
1700
- else extend(e);
1701
- });
1702
- var up = operation(cm, done);
1703
- on(document, "mousemove", move);
1704
- on(document, "mouseup", up);
1705
- }
1706
-
1707
- function onDrop(e) {
1708
- var cm = this;
1709
- if (eventInWidget(cm.display, e) || (cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e))))
1710
- return;
1711
- e_preventDefault(e);
1712
- var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files;
1713
- if (!pos || isReadOnly(cm)) return;
1714
- if (files && files.length && window.FileReader && window.File) {
1715
- var n = files.length, text = Array(n), read = 0;
1716
- var loadFile = function(file, i) {
1717
- var reader = new FileReader;
1718
- reader.onload = function() {
1719
- text[i] = reader.result;
1720
- if (++read == n) {
1721
- pos = clipPos(cm.doc, pos);
1722
- makeChange(cm.doc, {from: pos, to: pos, text: splitLines(text.join("\n")), origin: "paste"}, "around");
1723
- }
1724
- };
1725
- reader.readAsText(file);
1726
- };
1727
- for (var i = 0; i < n; ++i) loadFile(files[i], i);
1728
- } else {
1729
- // Don't do a replace if the drop happened inside of the selected text.
1730
- if (cm.state.draggingText && !(posLess(pos, cm.doc.sel.from) || posLess(cm.doc.sel.to, pos))) {
1731
- cm.state.draggingText(e);
1732
- // Ensure the editor is re-focused
1733
- setTimeout(bind(focusInput, cm), 20);
1734
- return;
1735
- }
1736
- try {
1737
- var text = e.dataTransfer.getData("Text");
1738
- if (text) {
1739
- var curFrom = cm.doc.sel.from, curTo = cm.doc.sel.to;
1740
- setSelection(cm.doc, pos, pos);
1741
- if (cm.state.draggingText) replaceRange(cm.doc, "", curFrom, curTo, "paste");
1742
- cm.replaceSelection(text, null, "paste");
1743
- focusInput(cm);
1744
- onFocus(cm);
1745
- }
1746
- }
1747
- catch(e){}
1748
- }
1749
- }
1750
-
1751
- function clickInGutter(cm, e) {
1752
- var display = cm.display;
1753
- try { var mX = e.clientX, mY = e.clientY; }
1754
- catch(e) { return false; }
1755
-
1756
- if (mX >= Math.floor(getRect(display.gutters).right)) return false;
1757
- e_preventDefault(e);
1758
- if (!hasHandler(cm, "gutterClick")) return true;
1759
-
1760
- var lineBox = getRect(display.lineDiv);
1761
- if (mY > lineBox.bottom) return true;
1762
- mY -= lineBox.top - display.viewOffset;
1763
-
1764
- for (var i = 0; i < cm.options.gutters.length; ++i) {
1765
- var g = display.gutters.childNodes[i];
1766
- if (g && getRect(g).right >= mX) {
1767
- var line = lineAtHeight(cm.doc, mY);
1768
- var gutter = cm.options.gutters[i];
1769
- signalLater(cm, "gutterClick", cm, line, gutter, e);
1770
- break;
1771
- }
1772
- }
1773
- return true;
1774
- }
1775
-
1776
- function onDragStart(cm, e) {
1777
- if (eventInWidget(cm.display, e)) return;
1778
-
1779
- var txt = cm.getSelection();
1780
- e.dataTransfer.setData("Text", txt);
1781
-
1782
- // Use dummy image instead of default browsers image.
1783
- // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
1784
- if (e.dataTransfer.setDragImage) {
1785
- var img = elt("img", null, null, "position: fixed; left: 0; top: 0;");
1786
- if (opera) {
1787
- img.width = img.height = 1;
1788
- cm.display.wrapper.appendChild(img);
1789
- // Force a relayout, or Opera won't use our image for some obscure reason
1790
- img._top = img.offsetTop;
1791
- }
1792
- if (safari) {
1793
- if (cm.display.dragImg) {
1794
- img = cm.display.dragImg;
1795
- } else {
1796
- cm.display.dragImg = img;
1797
- img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
1798
- cm.display.wrapper.appendChild(img);
1799
- }
1800
- }
1801
- e.dataTransfer.setDragImage(img, 0, 0);
1802
- if (opera) img.parentNode.removeChild(img);
1803
- }
1804
- }
1805
-
1806
- function setScrollTop(cm, val) {
1807
- if (Math.abs(cm.doc.scrollTop - val) < 2) return;
1808
- cm.doc.scrollTop = val;
1809
- if (!gecko) updateDisplay(cm, [], val);
1810
- if (cm.display.scroller.scrollTop != val) cm.display.scroller.scrollTop = val;
1811
- if (cm.display.scrollbarV.scrollTop != val) cm.display.scrollbarV.scrollTop = val;
1812
- if (gecko) updateDisplay(cm, []);
1813
- }
1814
- function setScrollLeft(cm, val, isScroller) {
1815
- if (isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) return;
1816
- val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth);
1817
- cm.doc.scrollLeft = val;
1818
- alignHorizontally(cm);
1819
- if (cm.display.scroller.scrollLeft != val) cm.display.scroller.scrollLeft = val;
1820
- if (cm.display.scrollbarH.scrollLeft != val) cm.display.scrollbarH.scrollLeft = val;
1821
- }
1822
-
1823
- // Since the delta values reported on mouse wheel events are
1824
- // unstandardized between browsers and even browser versions, and
1825
- // generally horribly unpredictable, this code starts by measuring
1826
- // the scroll effect that the first few mouse wheel events have,
1827
- // and, from that, detects the way it can convert deltas to pixel
1828
- // offsets afterwards.
1829
- //
1830
- // The reason we want to know the amount a wheel event will scroll
1831
- // is that it gives us a chance to update the display before the
1832
- // actual scrolling happens, reducing flickering.
1833
-
1834
- var wheelSamples = 0, wheelPixelsPerUnit = null;
1835
- // Fill in a browser-detected starting value on browsers where we
1836
- // know one. These don't have to be accurate -- the result of them
1837
- // being wrong would just be a slight flicker on the first wheel
1838
- // scroll (if it is large enough).
1839
- if (ie) wheelPixelsPerUnit = -.53;
1840
- else if (gecko) wheelPixelsPerUnit = 15;
1841
- else if (chrome) wheelPixelsPerUnit = -.7;
1842
- else if (safari) wheelPixelsPerUnit = -1/3;
1843
-
1844
- function onScrollWheel(cm, e) {
1845
- var dx = e.wheelDeltaX, dy = e.wheelDeltaY;
1846
- if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) dx = e.detail;
1847
- if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) dy = e.detail;
1848
- else if (dy == null) dy = e.wheelDelta;
1849
-
1850
- // Webkit browsers on OS X abort momentum scrolls when the target
1851
- // of the scroll event is removed from the scrollable element.
1852
- // This hack (see related code in patchDisplay) makes sure the
1853
- // element is kept around.
1854
- if (dy && mac && webkit) {
1855
- for (var cur = e.target; cur != scroll; cur = cur.parentNode) {
1856
- if (cur.lineObj) {
1857
- cm.display.currentWheelTarget = cur;
1858
- break;
1859
- }
1860
- }
1861
- }
1862
-
1863
- var display = cm.display, scroll = display.scroller;
1864
- // On some browsers, horizontal scrolling will cause redraws to
1865
- // happen before the gutter has been realigned, causing it to
1866
- // wriggle around in a most unseemly way. When we have an
1867
- // estimated pixels/delta value, we just handle horizontal
1868
- // scrolling entirely here. It'll be slightly off from native, but
1869
- // better than glitching out.
1870
- if (dx && !gecko && !opera && wheelPixelsPerUnit != null) {
1871
- if (dy)
1872
- setScrollTop(cm, Math.max(0, Math.min(scroll.scrollTop + dy * wheelPixelsPerUnit, scroll.scrollHeight - scroll.clientHeight)));
1873
- setScrollLeft(cm, Math.max(0, Math.min(scroll.scrollLeft + dx * wheelPixelsPerUnit, scroll.scrollWidth - scroll.clientWidth)));
1874
- e_preventDefault(e);
1875
- display.wheelStartX = null; // Abort measurement, if in progress
1876
- return;
1877
- }
1878
-
1879
- if (dy && wheelPixelsPerUnit != null) {
1880
- var pixels = dy * wheelPixelsPerUnit;
1881
- var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight;
1882
- if (pixels < 0) top = Math.max(0, top + pixels - 50);
1883
- else bot = Math.min(cm.doc.height, bot + pixels + 50);
1884
- updateDisplay(cm, [], {top: top, bottom: bot});
1885
- }
1886
-
1887
- if (wheelSamples < 20) {
1888
- if (display.wheelStartX == null) {
1889
- display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop;
1890
- display.wheelDX = dx; display.wheelDY = dy;
1891
- setTimeout(function() {
1892
- if (display.wheelStartX == null) return;
1893
- var movedX = scroll.scrollLeft - display.wheelStartX;
1894
- var movedY = scroll.scrollTop - display.wheelStartY;
1895
- var sample = (movedY && display.wheelDY && movedY / display.wheelDY) ||
1896
- (movedX && display.wheelDX && movedX / display.wheelDX);
1897
- display.wheelStartX = display.wheelStartY = null;
1898
- if (!sample) return;
1899
- wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1);
1900
- ++wheelSamples;
1901
- }, 200);
1902
- } else {
1903
- display.wheelDX += dx; display.wheelDY += dy;
1904
- }
1905
- }
1906
- }
1907
-
1908
- function doHandleBinding(cm, bound, dropShift) {
1909
- if (typeof bound == "string") {
1910
- bound = commands[bound];
1911
- if (!bound) return false;
1912
- }
1913
- // Ensure previous input has been read, so that the handler sees a
1914
- // consistent view of the document
1915
- if (cm.display.pollingFast && readInput(cm)) cm.display.pollingFast = false;
1916
- var doc = cm.doc, prevShift = doc.sel.shift, done = false;
1917
- try {
1918
- if (isReadOnly(cm)) cm.state.suppressEdits = true;
1919
- if (dropShift) doc.sel.shift = false;
1920
- done = bound(cm) != Pass;
1921
- } finally {
1922
- doc.sel.shift = prevShift;
1923
- cm.state.suppressEdits = false;
1924
- }
1925
- return done;
1926
- }
1927
-
1928
- function allKeyMaps(cm) {
1929
- var maps = cm.state.keyMaps.slice(0);
1930
- if (cm.options.extraKeys) maps.push(cm.options.extraKeys);
1931
- maps.push(cm.options.keyMap);
1932
- return maps;
1933
- }
1934
-
1935
- var maybeTransition;
1936
- function handleKeyBinding(cm, e) {
1937
- // Handle auto keymap transitions
1938
- var startMap = getKeyMap(cm.options.keyMap), next = startMap.auto;
1939
- clearTimeout(maybeTransition);
1940
- if (next && !isModifierKey(e)) maybeTransition = setTimeout(function() {
1941
- if (getKeyMap(cm.options.keyMap) == startMap)
1942
- cm.options.keyMap = (next.call ? next.call(null, cm) : next);
1943
- }, 50);
1944
-
1945
- var name = keyName(e, true), handled = false;
1946
- if (!name) return false;
1947
- var keymaps = allKeyMaps(cm);
1948
-
1949
- if (e.shiftKey) {
1950
- // First try to resolve full name (including 'Shift-'). Failing
1951
- // that, see if there is a cursor-motion command (starting with
1952
- // 'go') bound to the keyname without 'Shift-'.
1953
- handled = lookupKey("Shift-" + name, keymaps, function(b) {return doHandleBinding(cm, b, true);})
1954
- || lookupKey(name, keymaps, function(b) {
1955
- if (typeof b == "string" && /^go[A-Z]/.test(b)) return doHandleBinding(cm, b);
1956
- });
1957
- } else {
1958
- handled = lookupKey(name, keymaps, function(b) { return doHandleBinding(cm, b); });
1959
- }
1960
- if (handled == "stop") handled = false;
1961
-
1962
- if (handled) {
1963
- e_preventDefault(e);
1964
- restartBlink(cm);
1965
- if (ie_lt9) { e.oldKeyCode = e.keyCode; e.keyCode = 0; }
1966
- }
1967
- return handled;
1968
- }
1969
-
1970
- function handleCharBinding(cm, e, ch) {
1971
- var handled = lookupKey("'" + ch + "'", allKeyMaps(cm),
1972
- function(b) { return doHandleBinding(cm, b, true); });
1973
- if (handled) {
1974
- e_preventDefault(e);
1975
- restartBlink(cm);
1976
- }
1977
- return handled;
1978
- }
1979
-
1980
- var lastStoppedKey = null;
1981
- function onKeyDown(e) {
1982
- var cm = this;
1983
- if (!cm.state.focused) onFocus(cm);
1984
- if (ie && e.keyCode == 27) { e.returnValue = false; }
1985
- if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
1986
- var code = e.keyCode;
1987
- // IE does strange things with escape.
1988
- cm.doc.sel.shift = code == 16 || e.shiftKey;
1989
- // First give onKeyEvent option a chance to handle this.
1990
- var handled = handleKeyBinding(cm, e);
1991
- if (opera) {
1992
- lastStoppedKey = handled ? code : null;
1993
- // Opera has no cut event... we try to at least catch the key combo
1994
- if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey))
1995
- cm.replaceSelection("");
1996
- }
1997
- }
1998
-
1999
- function onKeyPress(e) {
2000
- var cm = this;
2001
- if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
2002
- var keyCode = e.keyCode, charCode = e.charCode;
2003
- if (opera && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;}
2004
- if (((opera && (!e.which || e.which < 10)) || khtml) && handleKeyBinding(cm, e)) return;
2005
- var ch = String.fromCharCode(charCode == null ? keyCode : charCode);
2006
- if (this.options.electricChars && this.doc.mode.electricChars &&
2007
- this.options.smartIndent && !isReadOnly(this) &&
2008
- this.doc.mode.electricChars.indexOf(ch) > -1)
2009
- setTimeout(operation(cm, function() {indentLine(cm, cm.doc.sel.to.line, "smart");}), 75);
2010
- if (handleCharBinding(cm, e, ch)) return;
2011
- fastPoll(cm);
2012
- }
2013
-
2014
- function onFocus(cm) {
2015
- if (cm.options.readOnly == "nocursor") return;
2016
- if (!cm.state.focused) {
2017
- signal(cm, "focus", cm);
2018
- cm.state.focused = true;
2019
- if (cm.display.wrapper.className.search(/\bCodeMirror-focused\b/) == -1)
2020
- cm.display.wrapper.className += " CodeMirror-focused";
2021
- resetInput(cm, true);
2022
- }
2023
- slowPoll(cm);
2024
- restartBlink(cm);
2025
- }
2026
- function onBlur(cm) {
2027
- if (cm.state.focused) {
2028
- signal(cm, "blur", cm);
2029
- cm.state.focused = false;
2030
- cm.display.wrapper.className = cm.display.wrapper.className.replace(" CodeMirror-focused", "");
2031
- }
2032
- clearInterval(cm.display.blinker);
2033
- setTimeout(function() {if (!cm.state.focused) cm.doc.sel.shift = false;}, 150);
2034
- }
2035
-
2036
- var detectingSelectAll;
2037
- function onContextMenu(cm, e) {
2038
- var display = cm.display, sel = cm.doc.sel;
2039
- if (eventInWidget(display, e)) return;
2040
-
2041
- var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop;
2042
- if (!pos || opera) return; // Opera is difficult.
2043
- if (posEq(sel.from, sel.to) || posLess(pos, sel.from) || !posLess(pos, sel.to))
2044
- operation(cm, setSelection)(cm.doc, pos, pos);
2045
-
2046
- var oldCSS = display.input.style.cssText;
2047
- display.inputDiv.style.position = "absolute";
2048
- display.input.style.cssText = "position: fixed; width: 30px; height: 30px; top: " + (e.clientY - 5) +
2049
- "px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: white; outline: none;" +
2050
- "border-width: 0; outline: none; overflow: hidden; opacity: .05; -ms-opacity: .05; filter: alpha(opacity=5);";
2051
- focusInput(cm);
2052
- resetInput(cm, true);
2053
- // Adds "Select all" to context menu in FF
2054
- if (posEq(sel.from, sel.to)) display.input.value = display.prevInput = " ";
2055
-
2056
- function rehide() {
2057
- display.inputDiv.style.position = "relative";
2058
- display.input.style.cssText = oldCSS;
2059
- if (ie_lt9) display.scrollbarV.scrollTop = display.scroller.scrollTop = scrollPos;
2060
- slowPoll(cm);
2061
-
2062
- // Try to detect the user choosing select-all
2063
- if (display.input.selectionStart != null && (!ie || ie_lt9)) {
2064
- clearTimeout(detectingSelectAll);
2065
- var extval = display.input.value = " " + (posEq(sel.from, sel.to) ? "" : display.input.value), i = 0;
2066
- display.prevInput = " ";
2067
- display.input.selectionStart = 1; display.input.selectionEnd = extval.length;
2068
- var poll = function(){
2069
- if (display.prevInput == " " && display.input.selectionStart == 0)
2070
- operation(cm, commands.selectAll)(cm);
2071
- else if (i++ < 10) detectingSelectAll = setTimeout(poll, 500);
2072
- else resetInput(cm);
2073
- };
2074
- detectingSelectAll = setTimeout(poll, 200);
2075
- }
2076
- }
2077
-
2078
- if (captureMiddleClick) {
2079
- e_stop(e);
2080
- var mouseup = function() {
2081
- off(window, "mouseup", mouseup);
2082
- setTimeout(rehide, 20);
2083
- };
2084
- on(window, "mouseup", mouseup);
2085
- } else {
2086
- setTimeout(rehide, 50);
2087
- }
2088
- }
2089
-
2090
- // UPDATING
2091
-
2092
- function changeEnd(change) {
2093
- return Pos(change.from.line + change.text.length - 1,
2094
- lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0));
2095
- }
2096
-
2097
- // Make sure a position will be valid after the given change.
2098
- function clipPostChange(doc, change, pos) {
2099
- if (!posLess(change.from, pos)) return clipPos(doc, pos);
2100
- var diff = (change.text.length - 1) - (change.to.line - change.from.line);
2101
- if (pos.line > change.to.line + diff) {
2102
- var preLine = pos.line - diff, lastLine = doc.first + doc.size - 1;
2103
- if (preLine > lastLine) return Pos(lastLine, getLine(doc, lastLine).text.length);
2104
- return clipToLen(pos, getLine(doc, preLine).text.length);
2105
- }
2106
- if (pos.line == change.to.line + diff)
2107
- return clipToLen(pos, lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0) +
2108
- getLine(doc, change.to.line).text.length - change.to.ch);
2109
- var inside = pos.line - change.from.line;
2110
- return clipToLen(pos, change.text[inside].length + (inside ? 0 : change.from.ch));
2111
- }
2112
-
2113
- // Hint can be null|"end"|"start"|"around"|{anchor,head}
2114
- function computeSelAfterChange(doc, change, hint) {
2115
- if (hint && typeof hint == "object") // Assumed to be {anchor, head} object
2116
- return {anchor: clipPostChange(doc, change, hint.anchor),
2117
- head: clipPostChange(doc, change, hint.head)};
2118
-
2119
- if (hint == "start") return {anchor: change.from, head: change.from};
2120
-
2121
- var end = changeEnd(change);
2122
- if (hint == "around") return {anchor: change.from, head: end};
2123
- if (hint == "end") return {anchor: end, head: end};
2124
-
2125
- // hint is null, leave the selection alone as much as possible
2126
- var adjustPos = function(pos) {
2127
- if (posLess(pos, change.from)) return pos;
2128
- if (!posLess(change.to, pos)) return end;
2129
-
2130
- var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch;
2131
- if (pos.line == change.to.line) ch += end.ch - change.to.ch;
2132
- return Pos(line, ch);
2133
- };
2134
- return {anchor: adjustPos(doc.sel.anchor), head: adjustPos(doc.sel.head)};
2135
- }
2136
-
2137
- function filterChange(doc, change) {
2138
- var obj = {
2139
- canceled: false,
2140
- from: change.from,
2141
- to: change.to,
2142
- text: change.text,
2143
- origin: change.origin,
2144
- update: function(from, to, text, origin) {
2145
- if (from) this.from = clipPos(doc, from);
2146
- if (to) this.to = clipPos(doc, to);
2147
- if (text) this.text = text;
2148
- if (origin !== undefined) this.origin = origin;
2149
- },
2150
- cancel: function() { this.canceled = true; }
2151
- };
2152
- signal(doc, "beforeChange", doc, obj);
2153
- if (doc.cm) signal(doc.cm, "beforeChange", doc.cm, obj);
2154
-
2155
- if (obj.canceled) return null;
2156
- return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin};
2157
- }
2158
-
2159
- // Replace the range from from to to by the strings in replacement.
2160
- // change is a {from, to, text [, origin]} object
2161
- function makeChange(doc, change, selUpdate, ignoreReadOnly) {
2162
- if (doc.cm) {
2163
- if (!doc.cm.curOp) return operation(doc.cm, makeChange)(doc, change, selUpdate, ignoreReadOnly);
2164
- if (doc.cm.state.suppressEdits) return;
2165
- }
2166
-
2167
- if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) {
2168
- change = filterChange(doc, change);
2169
- if (!change) return;
2170
- }
2171
-
2172
- // Possibly split or suppress the update based on the presence
2173
- // of read-only spans in its range.
2174
- var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to);
2175
- if (split) {
2176
- for (var i = split.length - 1; i >= 1; --i)
2177
- makeChangeNoReadonly(doc, {from: split[i].from, to: split[i].to, text: [""]});
2178
- if (split.length)
2179
- makeChangeNoReadonly(doc, {from: split[0].from, to: split[0].to, text: change.text}, selUpdate);
2180
- } else {
2181
- makeChangeNoReadonly(doc, change, selUpdate);
2182
- }
2183
- }
2184
-
2185
- function makeChangeNoReadonly(doc, change, selUpdate) {
2186
- var selAfter = computeSelAfterChange(doc, change, selUpdate);
2187
- addToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN);
2188
-
2189
- makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change));
2190
- var rebased = [];
2191
-
2192
- linkedDocs(doc, function(doc, sharedHist) {
2193
- if (!sharedHist && indexOf(rebased, doc.history) == -1) {
2194
- rebaseHist(doc.history, change);
2195
- rebased.push(doc.history);
2196
- }
2197
- makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change));
2198
- });
2199
- }
2200
-
2201
- function makeChangeFromHistory(doc, type) {
2202
- if (doc.cm && doc.cm.state.suppressEdits) return;
2203
-
2204
- var hist = doc.history;
2205
- var event = (type == "undo" ? hist.done : hist.undone).pop();
2206
- if (!event) return;
2207
- hist.dirtyCounter += type == "undo" ? -1 : 1;
2208
-
2209
- var anti = {changes: [], anchorBefore: event.anchorAfter, headBefore: event.headAfter,
2210
- anchorAfter: event.anchorBefore, headAfter: event.headBefore};
2211
- (type == "undo" ? hist.undone : hist.done).push(anti);
2212
-
2213
- for (var i = event.changes.length - 1; i >= 0; --i) {
2214
- var change = event.changes[i];
2215
- change.origin = type;
2216
- anti.changes.push(historyChangeFromChange(doc, change));
2217
-
2218
- var after = i ? computeSelAfterChange(doc, change, null)
2219
- : {anchor: event.anchorBefore, head: event.headBefore};
2220
- makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change));
2221
- var rebased = [];
2222
-
2223
- linkedDocs(doc, function(doc, sharedHist) {
2224
- if (!sharedHist && indexOf(rebased, doc.history) == -1) {
2225
- rebaseHist(doc.history, change);
2226
- rebased.push(doc.history);
2227
- }
2228
- makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change));
2229
- });
2230
- }
2231
- }
2232
-
2233
- function shiftDoc(doc, distance) {
2234
- function shiftPos(pos) {return Pos(pos.line + distance, pos.ch);}
2235
- doc.first += distance;
2236
- if (doc.cm) regChange(doc.cm, doc.first, doc.first, distance);
2237
- doc.sel.head = shiftPos(doc.sel.head); doc.sel.anchor = shiftPos(doc.sel.anchor);
2238
- doc.sel.from = shiftPos(doc.sel.from); doc.sel.to = shiftPos(doc.sel.to);
2239
- }
2240
-
2241
- function makeChangeSingleDoc(doc, change, selAfter, spans) {
2242
- if (doc.cm && !doc.cm.curOp)
2243
- return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans);
2244
-
2245
- if (change.to.line < doc.first) {
2246
- shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line));
2247
- return;
2248
- }
2249
- if (change.from.line > doc.lastLine()) return;
2250
-
2251
- // Clip the change to the size of this doc
2252
- if (change.from.line < doc.first) {
2253
- var shift = change.text.length - 1 - (doc.first - change.from.line);
2254
- shiftDoc(doc, shift);
2255
- change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch),
2256
- text: [lst(change.text)], origin: change.origin};
2257
- }
2258
- var last = doc.lastLine();
2259
- if (change.to.line > last) {
2260
- change = {from: change.from, to: Pos(last, getLine(doc, last).text.length),
2261
- text: [change.text[0]], origin: change.origin};
2262
- }
2263
-
2264
- change.removed = getBetween(doc, change.from, change.to);
2265
-
2266
- if (!selAfter) selAfter = computeSelAfterChange(doc, change, null);
2267
- if (doc.cm) makeChangeSingleDocInEditor(doc.cm, change, spans, selAfter);
2268
- else updateDoc(doc, change, spans, selAfter);
2269
- }
2270
-
2271
- function makeChangeSingleDocInEditor(cm, change, spans, selAfter) {
2272
- var doc = cm.doc, display = cm.display, from = change.from, to = change.to;
2273
-
2274
- var recomputeMaxLength = false, checkWidthStart = from.line;
2275
- if (!cm.options.lineWrapping) {
2276
- checkWidthStart = lineNo(visualLine(doc, getLine(doc, from.line)));
2277
- doc.iter(checkWidthStart, to.line + 1, function(line) {
2278
- if (line == display.maxLine) {
2279
- recomputeMaxLength = true;
2280
- return true;
2281
- }
2282
- });
2283
- }
2284
-
2285
- updateDoc(doc, change, spans, selAfter, estimateHeight(cm));
2286
-
2287
- if (!cm.options.lineWrapping) {
2288
- doc.iter(checkWidthStart, from.line + change.text.length, function(line) {
2289
- var len = lineLength(doc, line);
2290
- if (len > display.maxLineLength) {
2291
- display.maxLine = line;
2292
- display.maxLineLength = len;
2293
- display.maxLineChanged = true;
2294
- recomputeMaxLength = false;
2295
- }
2296
- });
2297
- if (recomputeMaxLength) cm.curOp.updateMaxLine = true;
2298
- }
2299
-
2300
- // Adjust frontier, schedule worker
2301
- doc.frontier = Math.min(doc.frontier, from.line);
2302
- startWorker(cm, 400);
2303
-
2304
- var lendiff = change.text.length - (to.line - from.line) - 1;
2305
- // Remember that these lines changed, for updating the display
2306
- regChange(cm, from.line, to.line + 1, lendiff);
2307
-
2308
- if (hasHandler(cm, "change")) {
2309
- var changeObj = {from: from, to: to,
2310
- text: change.text,
2311
- removed: change.removed,
2312
- origin: change.origin};
2313
- if (cm.curOp.textChanged) {
2314
- for (var cur = cm.curOp.textChanged; cur.next; cur = cur.next) {}
2315
- cur.next = changeObj;
2316
- } else cm.curOp.textChanged = changeObj;
2317
- }
2318
- }
2319
-
2320
- function replaceRange(doc, code, from, to, origin) {
2321
- if (!to) to = from;
2322
- if (posLess(to, from)) { var tmp = to; to = from; from = tmp; }
2323
- if (typeof code == "string") code = splitLines(code);
2324
- makeChange(doc, {from: from, to: to, text: code, origin: origin}, null);
2325
- }
2326
-
2327
- // POSITION OBJECT
2328
-
2329
- function Pos(line, ch) {
2330
- if (!(this instanceof Pos)) return new Pos(line, ch);
2331
- this.line = line; this.ch = ch;
2332
- }
2333
- CodeMirror.Pos = Pos;
2334
-
2335
- function posEq(a, b) {return a.line == b.line && a.ch == b.ch;}
2336
- function posLess(a, b) {return a.line < b.line || (a.line == b.line && a.ch < b.ch);}
2337
- function copyPos(x) {return Pos(x.line, x.ch);}
2338
-
2339
- // SELECTION
2340
-
2341
- function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1));}
2342
- function clipPos(doc, pos) {
2343
- if (pos.line < doc.first) return Pos(doc.first, 0);
2344
- var last = doc.first + doc.size - 1;
2345
- if (pos.line > last) return Pos(last, getLine(doc, last).text.length);
2346
- return clipToLen(pos, getLine(doc, pos.line).text.length);
2347
- }
2348
- function clipToLen(pos, linelen) {
2349
- var ch = pos.ch;
2350
- if (ch == null || ch > linelen) return Pos(pos.line, linelen);
2351
- else if (ch < 0) return Pos(pos.line, 0);
2352
- else return pos;
2353
- }
2354
- function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size;}
2355
-
2356
- // If shift is held, this will move the selection anchor. Otherwise,
2357
- // it'll set the whole selection.
2358
- function extendSelection(doc, pos, other, bias) {
2359
- if (doc.sel.shift || doc.sel.extend) {
2360
- var anchor = doc.sel.anchor;
2361
- if (other) {
2362
- var posBefore = posLess(pos, anchor);
2363
- if (posBefore != posLess(other, anchor)) {
2364
- anchor = pos;
2365
- pos = other;
2366
- } else if (posBefore != posLess(pos, other)) {
2367
- pos = other;
2368
- }
2369
- }
2370
- setSelection(doc, anchor, pos, bias);
2371
- } else {
2372
- setSelection(doc, pos, other || pos, bias);
2373
- }
2374
- if (doc.cm) doc.cm.curOp.userSelChange = true;
2375
- }
2376
-
2377
- function filterSelectionChange(doc, anchor, head) {
2378
- var obj = {anchor: anchor, head: head};
2379
- signal(doc, "beforeSelectionChange", doc, obj);
2380
- if (doc.cm) signal(doc.cm, "beforeSelectionChange", doc.cm, obj);
2381
- obj.anchor = clipPos(doc, obj.anchor); obj.head = clipPos(doc, obj.head);
2382
- return obj;
2383
- }
2384
-
2385
- // Update the selection. Last two args are only used by
2386
- // updateDoc, since they have to be expressed in the line
2387
- // numbers before the update.
2388
- function setSelection(doc, anchor, head, bias, checkAtomic) {
2389
- if (!checkAtomic && hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange")) {
2390
- var filtered = filterSelectionChange(doc, anchor, head);
2391
- head = filtered.head;
2392
- anchor = filtered.anchor;
2393
- }
2394
-
2395
- var sel = doc.sel;
2396
- sel.goalColumn = null;
2397
- // Skip over atomic spans.
2398
- if (checkAtomic || !posEq(anchor, sel.anchor))
2399
- anchor = skipAtomic(doc, anchor, bias, checkAtomic != "push");
2400
- if (checkAtomic || !posEq(head, sel.head))
2401
- head = skipAtomic(doc, head, bias, checkAtomic != "push");
2402
-
2403
- if (posEq(sel.anchor, anchor) && posEq(sel.head, head)) return;
2404
-
2405
- sel.anchor = anchor; sel.head = head;
2406
- var inv = posLess(head, anchor);
2407
- sel.from = inv ? head : anchor;
2408
- sel.to = inv ? anchor : head;
2409
-
2410
- if (doc.cm)
2411
- doc.cm.curOp.updateInput = doc.cm.curOp.selectionChanged = true;
2412
-
2413
- signalLater(doc, "cursorActivity", doc);
2414
- }
2415
-
2416
- function reCheckSelection(cm) {
2417
- setSelection(cm.doc, cm.doc.sel.from, cm.doc.sel.to, null, "push");
2418
- }
2419
-
2420
- function skipAtomic(doc, pos, bias, mayClear) {
2421
- var flipped = false, curPos = pos;
2422
- var dir = bias || 1;
2423
- doc.cantEdit = false;
2424
- search: for (;;) {
2425
- var line = getLine(doc, curPos.line);
2426
- if (line.markedSpans) {
2427
- for (var i = 0; i < line.markedSpans.length; ++i) {
2428
- var sp = line.markedSpans[i], m = sp.marker;
2429
- if ((sp.from == null || (m.inclusiveLeft ? sp.from <= curPos.ch : sp.from < curPos.ch)) &&
2430
- (sp.to == null || (m.inclusiveRight ? sp.to >= curPos.ch : sp.to > curPos.ch))) {
2431
- if (mayClear) {
2432
- signal(m, "beforeCursorEnter");
2433
- if (m.explicitlyCleared) {
2434
- if (!line.markedSpans) break;
2435
- else {--i; continue;}
2436
- }
2437
- }
2438
- if (!m.atomic) continue;
2439
- var newPos = m.find()[dir < 0 ? "from" : "to"];
2440
- if (posEq(newPos, curPos)) {
2441
- newPos.ch += dir;
2442
- if (newPos.ch < 0) {
2443
- if (newPos.line > doc.first) newPos = clipPos(doc, Pos(newPos.line - 1));
2444
- else newPos = null;
2445
- } else if (newPos.ch > line.text.length) {
2446
- if (newPos.line < doc.first + doc.size - 1) newPos = Pos(newPos.line + 1, 0);
2447
- else newPos = null;
2448
- }
2449
- if (!newPos) {
2450
- if (flipped) {
2451
- // Driven in a corner -- no valid cursor position found at all
2452
- // -- try again *with* clearing, if we didn't already
2453
- if (!mayClear) return skipAtomic(doc, pos, bias, true);
2454
- // Otherwise, turn off editing until further notice, and return the start of the doc
2455
- doc.cantEdit = true;
2456
- return Pos(doc.first, 0);
2457
- }
2458
- flipped = true; newPos = pos; dir = -dir;
2459
- }
2460
- }
2461
- curPos = newPos;
2462
- continue search;
2463
- }
2464
- }
2465
- }
2466
- return curPos;
2467
- }
2468
- }
2469
-
2470
- // SCROLLING
2471
-
2472
- function scrollCursorIntoView(cm) {
2473
- var coords = scrollPosIntoView(cm, cm.doc.sel.head);
2474
- if (!cm.state.focused) return;
2475
- var display = cm.display, box = getRect(display.sizer), doScroll = null, pTop = paddingTop(cm.display);
2476
- if (coords.top + pTop + box.top < 0) doScroll = true;
2477
- else if (coords.bottom + pTop + box.top > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false;
2478
- if (doScroll != null && !phantom) {
2479
- var hidden = display.cursor.style.display == "none";
2480
- if (hidden) {
2481
- display.cursor.style.display = "";
2482
- display.cursor.style.left = coords.left + "px";
2483
- display.cursor.style.top = (coords.top - display.viewOffset) + "px";
2484
- }
2485
- display.cursor.scrollIntoView(doScroll);
2486
- if (hidden) display.cursor.style.display = "none";
2487
- }
2488
- }
2489
-
2490
- function scrollPosIntoView(cm, pos, margin) {
2491
- if (margin == null) margin = 0;
2492
- for (;;) {
2493
- var changed = false, coords = cursorCoords(cm, pos);
2494
- var scrollPos = calculateScrollPos(cm, coords.left, coords.top - margin, coords.left, coords.bottom + margin);
2495
- var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft;
2496
- if (scrollPos.scrollTop != null) {
2497
- setScrollTop(cm, scrollPos.scrollTop);
2498
- if (Math.abs(cm.doc.scrollTop - startTop) > 1) changed = true;
2499
- }
2500
- if (scrollPos.scrollLeft != null) {
2501
- setScrollLeft(cm, scrollPos.scrollLeft);
2502
- if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) changed = true;
2503
- }
2504
- if (!changed) return coords;
2505
- }
2506
- }
2507
-
2508
- function scrollIntoView(cm, x1, y1, x2, y2) {
2509
- var scrollPos = calculateScrollPos(cm, x1, y1, x2, y2);
2510
- if (scrollPos.scrollTop != null) setScrollTop(cm, scrollPos.scrollTop);
2511
- if (scrollPos.scrollLeft != null) setScrollLeft(cm, scrollPos.scrollLeft);
2512
- }
2513
-
2514
- function calculateScrollPos(cm, x1, y1, x2, y2) {
2515
- var display = cm.display, pt = paddingTop(display);
2516
- y1 += pt; y2 += pt;
2517
- var screen = display.scroller.clientHeight - scrollerCutOff, screentop = display.scroller.scrollTop, result = {};
2518
- var docBottom = cm.doc.height + paddingVert(display);
2519
- var atTop = y1 < pt + 10, atBottom = y2 + pt > docBottom - 10;
2520
- if (y1 < screentop) result.scrollTop = atTop ? 0 : Math.max(0, y1);
2521
- else if (y2 > screentop + screen) result.scrollTop = (atBottom ? docBottom : y2) - screen;
2522
-
2523
- var screenw = display.scroller.clientWidth - scrollerCutOff, screenleft = display.scroller.scrollLeft;
2524
- x1 += display.gutters.offsetWidth; x2 += display.gutters.offsetWidth;
2525
- var gutterw = display.gutters.offsetWidth;
2526
- var atLeft = x1 < gutterw + 10;
2527
- if (x1 < screenleft + gutterw || atLeft) {
2528
- if (atLeft) x1 = 0;
2529
- result.scrollLeft = Math.max(0, x1 - 10 - gutterw);
2530
- } else if (x2 > screenw + screenleft - 3) {
2531
- result.scrollLeft = x2 + 10 - screenw;
2532
- }
2533
- return result;
2534
- }
2535
-
2536
- function updateScrollPos(cm, left, top) {
2537
- cm.curOp.updateScrollPos = {scrollLeft: left, scrollTop: top};
2538
- }
2539
-
2540
- function addToScrollPos(cm, left, top) {
2541
- var pos = cm.curOp.updateScrollPos || (cm.curOp.updateScrollPos = {scrollLeft: cm.doc.scrollLeft, scrollTop: cm.doc.scrollTop});
2542
- var scroll = cm.display.scroller;
2543
- pos.scrollTop = Math.max(0, Math.min(scroll.scrollHeight - scroll.clientHeight, pos.scrollTop + top));
2544
- pos.scrollLeft = Math.max(0, Math.min(scroll.scrollWidth - scroll.clientWidth, pos.scrollLeft + left));
2545
- }
2546
-
2547
- // API UTILITIES
2548
-
2549
- function indentLine(cm, n, how, aggressive) {
2550
- var doc = cm.doc;
2551
- if (!how) how = "add";
2552
- if (how == "smart") {
2553
- if (!cm.doc.mode.indent) how = "prev";
2554
- else var state = getStateBefore(cm, n);
2555
- }
2556
-
2557
- var tabSize = cm.options.tabSize;
2558
- var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize);
2559
- var curSpaceString = line.text.match(/^\s*/)[0], indentation;
2560
- if (how == "smart") {
2561
- indentation = cm.doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text);
2562
- if (indentation == Pass) {
2563
- if (!aggressive) return;
2564
- how = "prev";
2565
- }
2566
- }
2567
- if (how == "prev") {
2568
- if (n > doc.first) indentation = countColumn(getLine(doc, n-1).text, null, tabSize);
2569
- else indentation = 0;
2570
- } else if (how == "add") {
2571
- indentation = curSpace + cm.options.indentUnit;
2572
- } else if (how == "subtract") {
2573
- indentation = curSpace - cm.options.indentUnit;
2574
- }
2575
- indentation = Math.max(0, indentation);
2576
-
2577
- var indentString = "", pos = 0;
2578
- if (cm.options.indentWithTabs)
2579
- for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";}
2580
- if (pos < indentation) indentString += spaceStr(indentation - pos);
2581
-
2582
- if (indentString != curSpaceString)
2583
- replaceRange(cm.doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input");
2584
- line.stateAfter = null;
2585
- }
2586
-
2587
- function changeLine(cm, handle, op) {
2588
- var no = handle, line = handle, doc = cm.doc;
2589
- if (typeof handle == "number") line = getLine(doc, clipLine(doc, handle));
2590
- else no = lineNo(handle);
2591
- if (no == null) return null;
2592
- if (op(line, no)) regChange(cm, no, no + 1);
2593
- else return null;
2594
- return line;
2595
- }
2596
-
2597
- function findPosH(doc, pos, dir, unit, visually) {
2598
- var line = pos.line, ch = pos.ch;
2599
- var lineObj = getLine(doc, line);
2600
- var possible = true;
2601
- function findNextLine() {
2602
- var l = line + dir;
2603
- if (l < doc.first || l >= doc.first + doc.size) return (possible = false);
2604
- line = l;
2605
- return lineObj = getLine(doc, l);
2606
- }
2607
- function moveOnce(boundToLine) {
2608
- var next = (visually ? moveVisually : moveLogically)(lineObj, ch, dir, true);
2609
- if (next == null) {
2610
- if (!boundToLine && findNextLine()) {
2611
- if (visually) ch = (dir < 0 ? lineRight : lineLeft)(lineObj);
2612
- else ch = dir < 0 ? lineObj.text.length : 0;
2613
- } else return (possible = false);
2614
- } else ch = next;
2615
- return true;
2616
- }
2617
-
2618
- if (unit == "char") moveOnce();
2619
- else if (unit == "column") moveOnce(true);
2620
- else if (unit == "word" || unit == "group") {
2621
- var sawType = null, group = unit == "group";
2622
- for (var first = true;; first = false) {
2623
- if (dir < 0 && !moveOnce(!first)) break;
2624
- var cur = lineObj.text.charAt(ch) || "\n";
2625
- var type = isWordChar(cur) ? "w"
2626
- : !group ? null
2627
- : /\s/.test(cur) ? null
2628
- : "p";
2629
- if (sawType && sawType != type) {
2630
- if (dir < 0) {dir = 1; moveOnce();}
2631
- break;
2632
- }
2633
- if (type) sawType = type;
2634
- if (dir > 0 && !moveOnce(!first)) break;
2635
- }
2636
- }
2637
- var result = skipAtomic(doc, Pos(line, ch), dir, true);
2638
- if (!possible) result.hitSide = true;
2639
- return result;
2640
- }
2641
-
2642
- function findPosV(cm, pos, dir, unit) {
2643
- var doc = cm.doc, x = pos.left, y;
2644
- if (unit == "page") {
2645
- var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight);
2646
- y = pos.top + dir * (pageSize - (dir < 0 ? 1.5 : .5) * textHeight(cm.display));
2647
- } else if (unit == "line") {
2648
- y = dir > 0 ? pos.bottom + 3 : pos.top - 3;
2649
- }
2650
- for (;;) {
2651
- var target = coordsChar(cm, x, y);
2652
- if (!target.outside) break;
2653
- if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break; }
2654
- y += dir * 5;
2655
- }
2656
- return target;
2657
- }
2658
-
2659
- function findWordAt(line, pos) {
2660
- var start = pos.ch, end = pos.ch;
2661
- if (line) {
2662
- if (pos.after === false || end == line.length) --start; else ++end;
2663
- var startChar = line.charAt(start);
2664
- var check = isWordChar(startChar) ? isWordChar
2665
- : /\s/.test(startChar) ? function(ch) {return /\s/.test(ch);}
2666
- : function(ch) {return !/\s/.test(ch) && !isWordChar(ch);};
2667
- while (start > 0 && check(line.charAt(start - 1))) --start;
2668
- while (end < line.length && check(line.charAt(end))) ++end;
2669
- }
2670
- return {from: Pos(pos.line, start), to: Pos(pos.line, end)};
2671
- }
2672
-
2673
- function selectLine(cm, line) {
2674
- extendSelection(cm.doc, Pos(line, 0), clipPos(cm.doc, Pos(line + 1, 0)));
2675
- }
2676
-
2677
- // PROTOTYPE
2678
-
2679
- // The publicly visible API. Note that operation(null, f) means
2680
- // 'wrap f in an operation, performed on its `this` parameter'
2681
-
2682
- CodeMirror.prototype = {
2683
- focus: function(){window.focus(); focusInput(this); onFocus(this); fastPoll(this);},
2684
-
2685
- setOption: function(option, value) {
2686
- var options = this.options, old = options[option];
2687
- if (options[option] == value && option != "mode") return;
2688
- options[option] = value;
2689
- if (optionHandlers.hasOwnProperty(option))
2690
- operation(this, optionHandlers[option])(this, value, old);
2691
- },
2692
-
2693
- getOption: function(option) {return this.options[option];},
2694
- getDoc: function() {return this.doc;},
2695
-
2696
- addKeyMap: function(map, bottom) {
2697
- this.state.keyMaps[bottom ? "push" : "unshift"](map);
2698
- },
2699
- removeKeyMap: function(map) {
2700
- var maps = this.state.keyMaps;
2701
- for (var i = 0; i < maps.length; ++i)
2702
- if ((typeof map == "string" ? maps[i].name : maps[i]) == map) {
2703
- maps.splice(i, 1);
2704
- return true;
2705
- }
2706
- },
2707
-
2708
- addOverlay: operation(null, function(spec, options) {
2709
- var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec);
2710
- if (mode.startState) throw new Error("Overlays may not be stateful.");
2711
- this.state.overlays.push({mode: mode, modeSpec: spec, opaque: options && options.opaque});
2712
- this.state.modeGen++;
2713
- regChange(this);
2714
- }),
2715
- removeOverlay: operation(null, function(spec) {
2716
- var overlays = this.state.overlays;
2717
- for (var i = 0; i < overlays.length; ++i) {
2718
- if (overlays[i].modeSpec == spec) {
2719
- overlays.splice(i, 1);
2720
- this.state.modeGen++;
2721
- regChange(this);
2722
- return;
2723
- }
2724
- }
2725
- }),
2726
-
2727
- indentLine: operation(null, function(n, dir, aggressive) {
2728
- if (typeof dir != "string") {
2729
- if (dir == null) dir = this.options.smartIndent ? "smart" : "prev";
2730
- else dir = dir ? "add" : "subtract";
2731
- }
2732
- if (isLine(this.doc, n)) indentLine(this, n, dir, aggressive);
2733
- }),
2734
- indentSelection: operation(null, function(how) {
2735
- var sel = this.doc.sel;
2736
- if (posEq(sel.from, sel.to)) return indentLine(this, sel.from.line, how);
2737
- var e = sel.to.line - (sel.to.ch ? 0 : 1);
2738
- for (var i = sel.from.line; i <= e; ++i) indentLine(this, i, how);
2739
- }),
2740
-
2741
- // Fetch the parser token for a given character. Useful for hacks
2742
- // that want to inspect the mode state (say, for completion).
2743
- getTokenAt: function(pos) {
2744
- var doc = this.doc;
2745
- pos = clipPos(doc, pos);
2746
- var state = getStateBefore(this, pos.line), mode = this.doc.mode;
2747
- var line = getLine(doc, pos.line);
2748
- var stream = new StringStream(line.text, this.options.tabSize);
2749
- while (stream.pos < pos.ch && !stream.eol()) {
2750
- stream.start = stream.pos;
2751
- var style = mode.token(stream, state);
2752
- }
2753
- return {start: stream.start,
2754
- end: stream.pos,
2755
- string: stream.current(),
2756
- className: style || null, // Deprecated, use 'type' instead
2757
- type: style || null,
2758
- state: state};
2759
- },
2760
-
2761
- getStateAfter: function(line) {
2762
- var doc = this.doc;
2763
- line = clipLine(doc, line == null ? doc.first + doc.size - 1: line);
2764
- return getStateBefore(this, line + 1);
2765
- },
2766
-
2767
- cursorCoords: function(start, mode) {
2768
- var pos, sel = this.doc.sel;
2769
- if (start == null) pos = sel.head;
2770
- else if (typeof start == "object") pos = clipPos(this.doc, start);
2771
- else pos = start ? sel.from : sel.to;
2772
- return cursorCoords(this, pos, mode || "page");
2773
- },
2774
-
2775
- charCoords: function(pos, mode) {
2776
- return charCoords(this, clipPos(this.doc, pos), mode || "page");
2777
- },
2778
-
2779
- coordsChar: function(coords, mode) {
2780
- coords = fromCoordSystem(this, coords, mode || "page");
2781
- return coordsChar(this, coords.left, coords.top);
2782
- },
2783
-
2784
- defaultTextHeight: function() { return textHeight(this.display); },
2785
- defaultCharWidth: function() { return charWidth(this.display); },
2786
-
2787
- setGutterMarker: operation(null, function(line, gutterID, value) {
2788
- return changeLine(this, line, function(line) {
2789
- var markers = line.gutterMarkers || (line.gutterMarkers = {});
2790
- markers[gutterID] = value;
2791
- if (!value && isEmpty(markers)) line.gutterMarkers = null;
2792
- return true;
2793
- });
2794
- }),
2795
-
2796
- clearGutter: operation(null, function(gutterID) {
2797
- var cm = this, doc = cm.doc, i = doc.first;
2798
- doc.iter(function(line) {
2799
- if (line.gutterMarkers && line.gutterMarkers[gutterID]) {
2800
- line.gutterMarkers[gutterID] = null;
2801
- regChange(cm, i, i + 1);
2802
- if (isEmpty(line.gutterMarkers)) line.gutterMarkers = null;
2803
- }
2804
- ++i;
2805
- });
2806
- }),
2807
-
2808
- addLineClass: operation(null, function(handle, where, cls) {
2809
- return changeLine(this, handle, function(line) {
2810
- var prop = where == "text" ? "textClass" : where == "background" ? "bgClass" : "wrapClass";
2811
- if (!line[prop]) line[prop] = cls;
2812
- else if (new RegExp("\\b" + cls + "\\b").test(line[prop])) return false;
2813
- else line[prop] += " " + cls;
2814
- return true;
2815
- });
2816
- }),
2817
-
2818
- removeLineClass: operation(null, function(handle, where, cls) {
2819
- return changeLine(this, handle, function(line) {
2820
- var prop = where == "text" ? "textClass" : where == "background" ? "bgClass" : "wrapClass";
2821
- var cur = line[prop];
2822
- if (!cur) return false;
2823
- else if (cls == null) line[prop] = null;
2824
- else {
2825
- var upd = cur.replace(new RegExp("^" + cls + "\\b\\s*|\\s*\\b" + cls + "\\b"), "");
2826
- if (upd == cur) return false;
2827
- line[prop] = upd || null;
2828
- }
2829
- return true;
2830
- });
2831
- }),
2832
-
2833
- addLineWidget: operation(null, function(handle, node, options) {
2834
- return addLineWidget(this, handle, node, options);
2835
- }),
2836
-
2837
- removeLineWidget: function(widget) { widget.clear(); },
2838
-
2839
- lineInfo: function(line) {
2840
- if (typeof line == "number") {
2841
- if (!isLine(this.doc, line)) return null;
2842
- var n = line;
2843
- line = getLine(this.doc, line);
2844
- if (!line) return null;
2845
- } else {
2846
- var n = lineNo(line);
2847
- if (n == null) return null;
2848
- }
2849
- return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers,
2850
- textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass,
2851
- widgets: line.widgets};
2852
- },
2853
-
2854
- getViewport: function() { return {from: this.display.showingFrom, to: this.display.showingTo};},
2855
-
2856
- addWidget: function(pos, node, scroll, vert, horiz) {
2857
- var display = this.display;
2858
- pos = cursorCoords(this, clipPos(this.doc, pos));
2859
- var top = pos.bottom, left = pos.left;
2860
- node.style.position = "absolute";
2861
- display.sizer.appendChild(node);
2862
- if (vert == "over") {
2863
- top = pos.top;
2864
- } else if (vert == "above" || vert == "near") {
2865
- var vspace = Math.max(display.wrapper.clientHeight, this.doc.height),
2866
- hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth);
2867
- // Default to positioning above (if specified and possible); otherwise default to positioning below
2868
- if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight)
2869
- top = pos.top - node.offsetHeight;
2870
- else if (pos.bottom + node.offsetHeight <= vspace)
2871
- top = pos.bottom;
2872
- if (left + node.offsetWidth > hspace)
2873
- left = hspace - node.offsetWidth;
2874
- }
2875
- node.style.top = (top + paddingTop(display)) + "px";
2876
- node.style.left = node.style.right = "";
2877
- if (horiz == "right") {
2878
- left = display.sizer.clientWidth - node.offsetWidth;
2879
- node.style.right = "0px";
2880
- } else {
2881
- if (horiz == "left") left = 0;
2882
- else if (horiz == "middle") left = (display.sizer.clientWidth - node.offsetWidth) / 2;
2883
- node.style.left = left + "px";
2884
- }
2885
- if (scroll)
2886
- scrollIntoView(this, left, top, left + node.offsetWidth, top + node.offsetHeight);
2887
- },
2888
-
2889
- triggerOnKeyDown: operation(null, onKeyDown),
2890
-
2891
- execCommand: function(cmd) {return commands[cmd](this);},
2892
-
2893
- findPosH: function(from, amount, unit, visually) {
2894
- var dir = 1;
2895
- if (amount < 0) { dir = -1; amount = -amount; }
2896
- for (var i = 0, cur = clipPos(this.doc, from); i < amount; ++i) {
2897
- cur = findPosH(this.doc, cur, dir, unit, visually);
2898
- if (cur.hitSide) break;
2899
- }
2900
- return cur;
2901
- },
2902
-
2903
- moveH: operation(null, function(dir, unit) {
2904
- var sel = this.doc.sel, pos;
2905
- if (sel.shift || sel.extend || posEq(sel.from, sel.to))
2906
- pos = findPosH(this.doc, sel.head, dir, unit, this.options.rtlMoveVisually);
2907
- else
2908
- pos = dir < 0 ? sel.from : sel.to;
2909
- extendSelection(this.doc, pos, pos, dir);
2910
- }),
2911
-
2912
- deleteH: operation(null, function(dir, unit) {
2913
- var sel = this.doc.sel;
2914
- if (!posEq(sel.from, sel.to)) replaceRange(this.doc, "", sel.from, sel.to, "+delete");
2915
- else replaceRange(this.doc, "", sel.from, findPosH(this.doc, sel.head, dir, unit, false), "+delete");
2916
- this.curOp.userSelChange = true;
2917
- }),
2918
-
2919
- findPosV: function(from, amount, unit, goalColumn) {
2920
- var dir = 1, x = goalColumn;
2921
- if (amount < 0) { dir = -1; amount = -amount; }
2922
- for (var i = 0, cur = clipPos(this.doc, from); i < amount; ++i) {
2923
- var coords = cursorCoords(this, cur, "div");
2924
- if (x == null) x = coords.left;
2925
- else coords.left = x;
2926
- cur = findPosV(this, coords, dir, unit);
2927
- if (cur.hitSide) break;
2928
- }
2929
- return cur;
2930
- },
2931
-
2932
- moveV: operation(null, function(dir, unit) {
2933
- var sel = this.doc.sel;
2934
- var pos = cursorCoords(this, sel.head, "div");
2935
- if (sel.goalColumn != null) pos.left = sel.goalColumn;
2936
- var target = findPosV(this, pos, dir, unit);
2937
-
2938
- if (unit == "page") addToScrollPos(this, 0, charCoords(this, target, "div").top - pos.top);
2939
- extendSelection(this.doc, target, target, dir);
2940
- sel.goalColumn = pos.left;
2941
- }),
2942
-
2943
- toggleOverwrite: function() {
2944
- if (this.state.overwrite = !this.state.overwrite)
2945
- this.display.cursor.className += " CodeMirror-overwrite";
2946
- else
2947
- this.display.cursor.className = this.display.cursor.className.replace(" CodeMirror-overwrite", "");
2948
- },
2949
- hasFocus: function() { return this.state.focused; },
2950
-
2951
- scrollTo: operation(null, function(x, y) {
2952
- updateScrollPos(this, x, y);
2953
- }),
2954
- getScrollInfo: function() {
2955
- var scroller = this.display.scroller, co = scrollerCutOff;
2956
- return {left: scroller.scrollLeft, top: scroller.scrollTop,
2957
- height: scroller.scrollHeight - co, width: scroller.scrollWidth - co,
2958
- clientHeight: scroller.clientHeight - co, clientWidth: scroller.clientWidth - co};
2959
- },
2960
-
2961
- scrollIntoView: function(pos, margin) {
2962
- if (typeof pos == "number") pos = Pos(pos, 0);
2963
- if (!pos || pos.line != null) {
2964
- pos = pos ? clipPos(this.doc, pos) : this.doc.sel.head;
2965
- scrollPosIntoView(this, pos, margin);
2966
- } else {
2967
- scrollIntoView(this, pos.left, pos.top - margin, pos.right, pos.bottom + margin);
2968
- }
2969
- },
2970
-
2971
- setSize: function(width, height) {
2972
- function interpret(val) {
2973
- return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val;
2974
- }
2975
- if (width != null) this.display.wrapper.style.width = interpret(width);
2976
- if (height != null) this.display.wrapper.style.height = interpret(height);
2977
- this.refresh();
2978
- },
2979
-
2980
- on: function(type, f) {on(this, type, f);},
2981
- off: function(type, f) {off(this, type, f);},
2982
-
2983
- operation: function(f){return runInOp(this, f);},
2984
-
2985
- refresh: operation(null, function() {
2986
- clearCaches(this);
2987
- updateScrollPos(this, this.doc.scrollLeft, this.doc.scrollTop);
2988
- regChange(this);
2989
- }),
2990
-
2991
- swapDoc: operation(null, function(doc) {
2992
- var old = this.doc;
2993
- old.cm = null;
2994
- attachDoc(this, doc);
2995
- clearCaches(this);
2996
- updateScrollPos(this, doc.scrollLeft, doc.scrollTop);
2997
- return old;
2998
- }),
2999
-
3000
- getInputField: function(){return this.display.input;},
3001
- getWrapperElement: function(){return this.display.wrapper;},
3002
- getScrollerElement: function(){return this.display.scroller;},
3003
- getGutterElement: function(){return this.display.gutters;}
3004
- };
3005
-
3006
- // OPTION DEFAULTS
3007
-
3008
- var optionHandlers = CodeMirror.optionHandlers = {};
3009
-
3010
- // The default configuration options.
3011
- var defaults = CodeMirror.defaults = {};
3012
-
3013
- function option(name, deflt, handle, notOnInit) {
3014
- CodeMirror.defaults[name] = deflt;
3015
- if (handle) optionHandlers[name] =
3016
- notOnInit ? function(cm, val, old) {if (old != Init) handle(cm, val, old);} : handle;
3017
- }
3018
-
3019
- var Init = CodeMirror.Init = {toString: function(){return "CodeMirror.Init";}};
3020
-
3021
- // These two are, on init, called from the constructor because they
3022
- // have to be initialized before the editor can start at all.
3023
- option("value", "", function(cm, val) {
3024
- cm.setValue(val);
3025
- }, true);
3026
- option("mode", null, function(cm, val) {
3027
- cm.doc.modeOption = val;
3028
- loadMode(cm);
3029
- }, true);
3030
-
3031
- option("indentUnit", 2, loadMode, true);
3032
- option("indentWithTabs", false);
3033
- option("smartIndent", true);
3034
- option("tabSize", 4, function(cm) {
3035
- loadMode(cm);
3036
- clearCaches(cm);
3037
- regChange(cm);
3038
- }, true);
3039
- option("electricChars", true);
3040
- option("rtlMoveVisually", !windows);
3041
-
3042
- option("theme", "default", function(cm) {
3043
- themeChanged(cm);
3044
- guttersChanged(cm);
3045
- }, true);
3046
- option("keyMap", "default", keyMapChanged);
3047
- option("extraKeys", null);
3048
-
3049
- option("onKeyEvent", null);
3050
- option("onDragEvent", null);
3051
-
3052
- option("lineWrapping", false, wrappingChanged, true);
3053
- option("gutters", [], function(cm) {
3054
- setGuttersForLineNumbers(cm.options);
3055
- guttersChanged(cm);
3056
- }, true);
3057
- option("fixedGutter", true, function(cm, val) {
3058
- cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0";
3059
- cm.refresh();
3060
- }, true);
3061
- option("lineNumbers", false, function(cm) {
3062
- setGuttersForLineNumbers(cm.options);
3063
- guttersChanged(cm);
3064
- }, true);
3065
- option("firstLineNumber", 1, guttersChanged, true);
3066
- option("lineNumberFormatter", function(integer) {return integer;}, guttersChanged, true);
3067
- option("showCursorWhenSelecting", false, updateSelection, true);
3068
-
3069
- option("readOnly", false, function(cm, val) {
3070
- if (val == "nocursor") {onBlur(cm); cm.display.input.blur();}
3071
- else if (!val) resetInput(cm, true);
3072
- });
3073
- option("dragDrop", true);
3074
-
3075
- option("cursorBlinkRate", 530);
3076
- option("cursorHeight", 1);
3077
- option("workTime", 100);
3078
- option("workDelay", 100);
3079
- option("flattenSpans", true);
3080
- option("pollInterval", 100);
3081
- option("undoDepth", 40, function(cm, val){cm.doc.history.undoDepth = val;});
3082
- option("viewportMargin", 10, function(cm){cm.refresh();}, true);
3083
-
3084
- option("tabindex", null, function(cm, val) {
3085
- cm.display.input.tabIndex = val || "";
3086
- });
3087
- option("autofocus", null);
3088
-
3089
- // MODE DEFINITION AND QUERYING
3090
-
3091
- // Known modes, by name and by MIME
3092
- var modes = CodeMirror.modes = {}, mimeModes = CodeMirror.mimeModes = {};
3093
-
3094
- CodeMirror.defineMode = function(name, mode) {
3095
- if (!CodeMirror.defaults.mode && name != "null") CodeMirror.defaults.mode = name;
3096
- if (arguments.length > 2) {
3097
- mode.dependencies = [];
3098
- for (var i = 2; i < arguments.length; ++i) mode.dependencies.push(arguments[i]);
3099
- }
3100
- modes[name] = mode;
3101
- };
3102
-
3103
- CodeMirror.defineMIME = function(mime, spec) {
3104
- mimeModes[mime] = spec;
3105
- };
3106
-
3107
- CodeMirror.resolveMode = function(spec) {
3108
- if (typeof spec == "string" && mimeModes.hasOwnProperty(spec))
3109
- spec = mimeModes[spec];
3110
- else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec))
3111
- return CodeMirror.resolveMode("application/xml");
3112
- if (typeof spec == "string") return {name: spec};
3113
- else return spec || {name: "null"};
3114
- };
3115
-
3116
- CodeMirror.getMode = function(options, spec) {
3117
- spec = CodeMirror.resolveMode(spec);
3118
- var mfactory = modes[spec.name];
3119
- if (!mfactory) return CodeMirror.getMode(options, "text/plain");
3120
- var modeObj = mfactory(options, spec);
3121
- if (modeExtensions.hasOwnProperty(spec.name)) {
3122
- var exts = modeExtensions[spec.name];
3123
- for (var prop in exts) {
3124
- if (!exts.hasOwnProperty(prop)) continue;
3125
- if (modeObj.hasOwnProperty(prop)) modeObj["_" + prop] = modeObj[prop];
3126
- modeObj[prop] = exts[prop];
3127
- }
3128
- }
3129
- modeObj.name = spec.name;
3130
- return modeObj;
3131
- };
3132
-
3133
- CodeMirror.defineMode("null", function() {
3134
- return {token: function(stream) {stream.skipToEnd();}};
3135
- });
3136
- CodeMirror.defineMIME("text/plain", "null");
3137
-
3138
- var modeExtensions = CodeMirror.modeExtensions = {};
3139
- CodeMirror.extendMode = function(mode, properties) {
3140
- var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});
3141
- copyObj(properties, exts);
3142
- };
3143
-
3144
- // EXTENSIONS
3145
-
3146
- CodeMirror.defineExtension = function(name, func) {
3147
- CodeMirror.prototype[name] = func;
3148
- };
3149
-
3150
- CodeMirror.defineOption = option;
3151
-
3152
- var initHooks = [];
3153
- CodeMirror.defineInitHook = function(f) {initHooks.push(f);};
3154
-
3155
- // MODE STATE HANDLING
3156
-
3157
- // Utility functions for working with state. Exported because modes
3158
- // sometimes need to do this.
3159
- function copyState(mode, state) {
3160
- if (state === true) return state;
3161
- if (mode.copyState) return mode.copyState(state);
3162
- var nstate = {};
3163
- for (var n in state) {
3164
- var val = state[n];
3165
- if (val instanceof Array) val = val.concat([]);
3166
- nstate[n] = val;
3167
- }
3168
- return nstate;
3169
- }
3170
- CodeMirror.copyState = copyState;
3171
-
3172
- function startState(mode, a1, a2) {
3173
- return mode.startState ? mode.startState(a1, a2) : true;
3174
- }
3175
- CodeMirror.startState = startState;
3176
-
3177
- CodeMirror.innerMode = function(mode, state) {
3178
- while (mode.innerMode) {
3179
- var info = mode.innerMode(state);
3180
- state = info.state;
3181
- mode = info.mode;
3182
- }
3183
- return info || {mode: mode, state: state};
3184
- };
3185
-
3186
- // STANDARD COMMANDS
3187
-
3188
- var commands = CodeMirror.commands = {
3189
- selectAll: function(cm) {cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()));},
3190
- killLine: function(cm) {
3191
- var from = cm.getCursor(true), to = cm.getCursor(false), sel = !posEq(from, to);
3192
- if (!sel && cm.getLine(from.line).length == from.ch)
3193
- cm.replaceRange("", from, Pos(from.line + 1, 0), "+delete");
3194
- else cm.replaceRange("", from, sel ? to : Pos(from.line), "+delete");
3195
- },
3196
- deleteLine: function(cm) {
3197
- var l = cm.getCursor().line;
3198
- cm.replaceRange("", Pos(l, 0), Pos(l), "+delete");
3199
- },
3200
- undo: function(cm) {cm.undo();},
3201
- redo: function(cm) {cm.redo();},
3202
- goDocStart: function(cm) {cm.extendSelection(Pos(cm.firstLine(), 0));},
3203
- goDocEnd: function(cm) {cm.extendSelection(Pos(cm.lastLine()));},
3204
- goLineStart: function(cm) {
3205
- cm.extendSelection(lineStart(cm, cm.getCursor().line));
3206
- },
3207
- goLineStartSmart: function(cm) {
3208
- var cur = cm.getCursor(), start = lineStart(cm, cur.line);
3209
- var line = cm.getLineHandle(start.line);
3210
- var order = getOrder(line);
3211
- if (!order || order[0].level == 0) {
3212
- var firstNonWS = Math.max(0, line.text.search(/\S/));
3213
- var inWS = cur.line == start.line && cur.ch <= firstNonWS && cur.ch;
3214
- cm.extendSelection(Pos(start.line, inWS ? 0 : firstNonWS));
3215
- } else cm.extendSelection(start);
3216
- },
3217
- goLineEnd: function(cm) {
3218
- cm.extendSelection(lineEnd(cm, cm.getCursor().line));
3219
- },
3220
- goLineRight: function(cm) {
3221
- var top = cm.charCoords(cm.getCursor(), "div").top + 5;
3222
- cm.extendSelection(cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div"));
3223
- },
3224
- goLineLeft: function(cm) {
3225
- var top = cm.charCoords(cm.getCursor(), "div").top + 5;
3226
- cm.extendSelection(cm.coordsChar({left: 0, top: top}, "div"));
3227
- },
3228
- goLineUp: function(cm) {cm.moveV(-1, "line");},
3229
- goLineDown: function(cm) {cm.moveV(1, "line");},
3230
- goPageUp: function(cm) {cm.moveV(-1, "page");},
3231
- goPageDown: function(cm) {cm.moveV(1, "page");},
3232
- goCharLeft: function(cm) {cm.moveH(-1, "char");},
3233
- goCharRight: function(cm) {cm.moveH(1, "char");},
3234
- goColumnLeft: function(cm) {cm.moveH(-1, "column");},
3235
- goColumnRight: function(cm) {cm.moveH(1, "column");},
3236
- goWordLeft: function(cm) {cm.moveH(-1, "word");},
3237
- goGroupRight: function(cm) {cm.moveH(1, "group");},
3238
- goGroupLeft: function(cm) {cm.moveH(-1, "group");},
3239
- goWordRight: function(cm) {cm.moveH(1, "word");},
3240
- delCharBefore: function(cm) {cm.deleteH(-1, "char");},
3241
- delCharAfter: function(cm) {cm.deleteH(1, "char");},
3242
- delWordBefore: function(cm) {cm.deleteH(-1, "word");},
3243
- delWordAfter: function(cm) {cm.deleteH(1, "word");},
3244
- delGroupBefore: function(cm) {cm.deleteH(-1, "group");},
3245
- delGroupAfter: function(cm) {cm.deleteH(1, "group");},
3246
- indentAuto: function(cm) {cm.indentSelection("smart");},
3247
- indentMore: function(cm) {cm.indentSelection("add");},
3248
- indentLess: function(cm) {cm.indentSelection("subtract");},
3249
- insertTab: function(cm) {cm.replaceSelection("\t", "end", "+input");},
3250
- defaultTab: function(cm) {
3251
- if (cm.somethingSelected()) cm.indentSelection("add");
3252
- else cm.replaceSelection("\t", "end", "+input");
3253
- },
3254
- transposeChars: function(cm) {
3255
- var cur = cm.getCursor(), line = cm.getLine(cur.line);
3256
- if (cur.ch > 0 && cur.ch < line.length - 1)
3257
- cm.replaceRange(line.charAt(cur.ch) + line.charAt(cur.ch - 1),
3258
- Pos(cur.line, cur.ch - 1), Pos(cur.line, cur.ch + 1));
3259
- },
3260
- newlineAndIndent: function(cm) {
3261
- operation(cm, function() {
3262
- cm.replaceSelection("\n", "end", "+input");
3263
- cm.indentLine(cm.getCursor().line, null, true);
3264
- })();
3265
- },
3266
- toggleOverwrite: function(cm) {cm.toggleOverwrite();}
3267
- };
3268
-
3269
- // STANDARD KEYMAPS
3270
-
3271
- var keyMap = CodeMirror.keyMap = {};
3272
- keyMap.basic = {
3273
- "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
3274
- "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
3275
- "Delete": "delCharAfter", "Backspace": "delCharBefore", "Tab": "defaultTab", "Shift-Tab": "indentAuto",
3276
- "Enter": "newlineAndIndent", "Insert": "toggleOverwrite"
3277
- };
3278
- // Note that the save and find-related commands aren't defined by
3279
- // default. Unknown commands are simply ignored.
3280
- keyMap.pcDefault = {
3281
- "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
3282
- "Ctrl-Home": "goDocStart", "Alt-Up": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Down": "goDocEnd",
3283
- "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
3284
- "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find",
3285
- "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
3286
- "Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
3287
- fallthrough: "basic"
3288
- };
3289
- keyMap.macDefault = {
3290
- "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
3291
- "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft",
3292
- "Alt-Right": "goGroupRight", "Cmd-Left": "goLineStart", "Cmd-Right": "goLineEnd", "Alt-Backspace": "delGroupBefore",
3293
- "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find",
3294
- "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
3295
- "Cmd-[": "indentLess", "Cmd-]": "indentMore",
3296
- fallthrough: ["basic", "emacsy"]
3297
- };
3298
- keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault;
3299
- keyMap.emacsy = {
3300
- "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
3301
- "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
3302
- "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore",
3303
- "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars"
3304
- };
3305
-
3306
- // KEYMAP DISPATCH
3307
-
3308
- function getKeyMap(val) {
3309
- if (typeof val == "string") return keyMap[val];
3310
- else return val;
3311
- }
3312
-
3313
- function lookupKey(name, maps, handle) {
3314
- function lookup(map) {
3315
- map = getKeyMap(map);
3316
- var found = map[name];
3317
- if (found === false) return "stop";
3318
- if (found != null && handle(found)) return true;
3319
- if (map.nofallthrough) return "stop";
3320
-
3321
- var fallthrough = map.fallthrough;
3322
- if (fallthrough == null) return false;
3323
- if (Object.prototype.toString.call(fallthrough) != "[object Array]")
3324
- return lookup(fallthrough);
3325
- for (var i = 0, e = fallthrough.length; i < e; ++i) {
3326
- var done = lookup(fallthrough[i]);
3327
- if (done) return done;
3328
- }
3329
- return false;
3330
- }
3331
-
3332
- for (var i = 0; i < maps.length; ++i) {
3333
- var done = lookup(maps[i]);
3334
- if (done) return done;
3335
- }
3336
- }
3337
- function isModifierKey(event) {
3338
- var name = keyNames[event.keyCode];
3339
- return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod";
3340
- }
3341
- function keyName(event, noShift) {
3342
- var name = keyNames[event.keyCode];
3343
- if (name == null || event.altGraphKey) return false;
3344
- if (event.altKey) name = "Alt-" + name;
3345
- if (flipCtrlCmd ? event.metaKey : event.ctrlKey) name = "Ctrl-" + name;
3346
- if (flipCtrlCmd ? event.ctrlKey : event.metaKey) name = "Cmd-" + name;
3347
- if (!noShift && event.shiftKey) name = "Shift-" + name;
3348
- return name;
3349
- }
3350
- CodeMirror.lookupKey = lookupKey;
3351
- CodeMirror.isModifierKey = isModifierKey;
3352
- CodeMirror.keyName = keyName;
3353
-
3354
- // FROMTEXTAREA
3355
-
3356
- CodeMirror.fromTextArea = function(textarea, options) {
3357
- if (!options) options = {};
3358
- options.value = textarea.value;
3359
- if (!options.tabindex && textarea.tabindex)
3360
- options.tabindex = textarea.tabindex;
3361
- if (!options.placeholder && textarea.placeholder)
3362
- options.placeholder = textarea.placeholder;
3363
- // Set autofocus to true if this textarea is focused, or if it has
3364
- // autofocus and no other element is focused.
3365
- if (options.autofocus == null) {
3366
- var hasFocus = document.body;
3367
- // doc.activeElement occasionally throws on IE
3368
- try { hasFocus = document.activeElement; } catch(e) {}
3369
- options.autofocus = hasFocus == textarea ||
3370
- textarea.getAttribute("autofocus") != null && hasFocus == document.body;
3371
- }
3372
-
3373
- function save() {textarea.value = cm.getValue();}
3374
- if (textarea.form) {
3375
- on(textarea.form, "submit", save);
3376
- // Deplorable hack to make the submit method do the right thing.
3377
- if (!options.leaveSubmitMethodAlone) {
3378
- var form = textarea.form, realSubmit = form.submit;
3379
- try {
3380
- var wrappedSubmit = form.submit = function() {
3381
- save();
3382
- form.submit = realSubmit;
3383
- form.submit();
3384
- form.submit = wrappedSubmit;
3385
- };
3386
- } catch(e) {}
3387
- }
3388
- }
3389
-
3390
- textarea.style.display = "none";
3391
- var cm = CodeMirror(function(node) {
3392
- textarea.parentNode.insertBefore(node, textarea.nextSibling);
3393
- }, options);
3394
- cm.save = save;
3395
- cm.getTextArea = function() { return textarea; };
3396
- cm.toTextArea = function() {
3397
- save();
3398
- textarea.parentNode.removeChild(cm.getWrapperElement());
3399
- textarea.style.display = "";
3400
- if (textarea.form) {
3401
- off(textarea.form, "submit", save);
3402
- if (typeof textarea.form.submit == "function")
3403
- textarea.form.submit = realSubmit;
3404
- }
3405
- };
3406
- return cm;
3407
- };
3408
-
3409
- // STRING STREAM
3410
-
3411
- // Fed to the mode parsers, provides helper functions to make
3412
- // parsers more succinct.
3413
-
3414
- // The character stream used by a mode's parser.
3415
- function StringStream(string, tabSize) {
3416
- this.pos = this.start = 0;
3417
- this.string = string;
3418
- this.tabSize = tabSize || 8;
3419
- this.lastColumnPos = this.lastColumnValue = 0;
3420
- }
3421
-
3422
- StringStream.prototype = {
3423
- eol: function() {return this.pos >= this.string.length;},
3424
- sol: function() {return this.pos == 0;},
3425
- peek: function() {return this.string.charAt(this.pos) || undefined;},
3426
- next: function() {
3427
- if (this.pos < this.string.length)
3428
- return this.string.charAt(this.pos++);
3429
- },
3430
- eat: function(match) {
3431
- var ch = this.string.charAt(this.pos);
3432
- if (typeof match == "string") var ok = ch == match;
3433
- else var ok = ch && (match.test ? match.test(ch) : match(ch));
3434
- if (ok) {++this.pos; return ch;}
3435
- },
3436
- eatWhile: function(match) {
3437
- var start = this.pos;
3438
- while (this.eat(match)){}
3439
- return this.pos > start;
3440
- },
3441
- eatSpace: function() {
3442
- var start = this.pos;
3443
- while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) ++this.pos;
3444
- return this.pos > start;
3445
- },
3446
- skipToEnd: function() {this.pos = this.string.length;},
3447
- skipTo: function(ch) {
3448
- var found = this.string.indexOf(ch, this.pos);
3449
- if (found > -1) {this.pos = found; return true;}
3450
- },
3451
- backUp: function(n) {this.pos -= n;},
3452
- column: function() {
3453
- if (this.lastColumnPos < this.start) {
3454
- this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue);
3455
- this.lastColumnPos = this.start;
3456
- }
3457
- return this.lastColumnValue;
3458
- },
3459
- indentation: function() {return countColumn(this.string, null, this.tabSize);},
3460
- match: function(pattern, consume, caseInsensitive) {
3461
- if (typeof pattern == "string") {
3462
- var cased = function(str) {return caseInsensitive ? str.toLowerCase() : str;};
3463
- var substr = this.string.substr(this.pos, pattern.length);
3464
- if (cased(substr) == cased(pattern)) {
3465
- if (consume !== false) this.pos += pattern.length;
3466
- return true;
3467
- }
3468
- } else {
3469
- var match = this.string.slice(this.pos).match(pattern);
3470
- if (match && match.index > 0) return null;
3471
- if (match && consume !== false) this.pos += match[0].length;
3472
- return match;
3473
- }
3474
- },
3475
- current: function(){return this.string.slice(this.start, this.pos);}
3476
- };
3477
- CodeMirror.StringStream = StringStream;
3478
-
3479
- // TEXTMARKERS
3480
-
3481
- function TextMarker(doc, type) {
3482
- this.lines = [];
3483
- this.type = type;
3484
- this.doc = doc;
3485
- }
3486
- CodeMirror.TextMarker = TextMarker;
3487
-
3488
- TextMarker.prototype.clear = function() {
3489
- if (this.explicitlyCleared) return;
3490
- var cm = this.doc.cm, withOp = cm && !cm.curOp;
3491
- if (withOp) startOperation(cm);
3492
- var min = null, max = null;
3493
- for (var i = 0; i < this.lines.length; ++i) {
3494
- var line = this.lines[i];
3495
- var span = getMarkedSpanFor(line.markedSpans, this);
3496
- if (span.to != null) max = lineNo(line);
3497
- line.markedSpans = removeMarkedSpan(line.markedSpans, span);
3498
- if (span.from != null)
3499
- min = lineNo(line);
3500
- else if (this.collapsed && !lineIsHidden(this.doc, line) && cm)
3501
- updateLineHeight(line, textHeight(cm.display));
3502
- }
3503
- if (cm && this.collapsed && !cm.options.lineWrapping) for (var i = 0; i < this.lines.length; ++i) {
3504
- var visual = visualLine(cm.doc, this.lines[i]), len = lineLength(cm.doc, visual);
3505
- if (len > cm.display.maxLineLength) {
3506
- cm.display.maxLine = visual;
3507
- cm.display.maxLineLength = len;
3508
- cm.display.maxLineChanged = true;
3509
- }
3510
- }
3511
-
3512
- if (min != null && cm) regChange(cm, min, max + 1);
3513
- this.lines.length = 0;
3514
- this.explicitlyCleared = true;
3515
- if (this.collapsed && this.doc.cantEdit) {
3516
- this.doc.cantEdit = false;
3517
- if (cm) reCheckSelection(cm);
3518
- }
3519
- if (withOp) endOperation(cm);
3520
- signalLater(this, "clear");
3521
- };
3522
-
3523
- TextMarker.prototype.find = function() {
3524
- var from, to;
3525
- for (var i = 0; i < this.lines.length; ++i) {
3526
- var line = this.lines[i];
3527
- var span = getMarkedSpanFor(line.markedSpans, this);
3528
- if (span.from != null || span.to != null) {
3529
- var found = lineNo(line);
3530
- if (span.from != null) from = Pos(found, span.from);
3531
- if (span.to != null) to = Pos(found, span.to);
3532
- }
3533
- }
3534
- if (this.type == "bookmark") return from;
3535
- return from && {from: from, to: to};
3536
- };
3537
-
3538
- TextMarker.prototype.getOptions = function(copyWidget) {
3539
- var repl = this.replacedWith;
3540
- return {className: this.className,
3541
- inclusiveLeft: this.inclusiveLeft, inclusiveRight: this.inclusiveRight,
3542
- atomic: this.atomic,
3543
- collapsed: this.collapsed,
3544
- replacedWith: copyWidget ? repl && repl.cloneNode(true) : repl,
3545
- readOnly: this.readOnly,
3546
- startStyle: this.startStyle, endStyle: this.endStyle};
3547
- };
3548
-
3549
- TextMarker.prototype.attachLine = function(line) {
3550
- if (!this.lines.length && this.doc.cm) {
3551
- var op = this.doc.cm.curOp;
3552
- if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1)
3553
- (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this);
3554
- }
3555
- this.lines.push(line);
3556
- };
3557
- TextMarker.prototype.detachLine = function(line) {
3558
- this.lines.splice(indexOf(this.lines, line), 1);
3559
- if (!this.lines.length && this.doc.cm) {
3560
- var op = this.doc.cm.curOp;
3561
- (op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this);
3562
- }
3563
- };
3564
-
3565
- function markText(doc, from, to, options, type) {
3566
- if (options && options.shared) return markTextShared(doc, from, to, options, type);
3567
- if (doc.cm && !doc.cm.curOp) return operation(doc.cm, markText)(doc, from, to, options, type);
3568
-
3569
- var marker = new TextMarker(doc, type);
3570
- if (type == "range" && !posLess(from, to)) return marker;
3571
- if (options) copyObj(options, marker);
3572
- if (marker.replacedWith) {
3573
- marker.collapsed = true;
3574
- marker.replacedWith = elt("span", [marker.replacedWith], "CodeMirror-widget");
3575
- }
3576
- if (marker.collapsed) sawCollapsedSpans = true;
3577
-
3578
- var curLine = from.line, size = 0, collapsedAtStart, collapsedAtEnd, cm = doc.cm, updateMaxLine;
3579
- doc.iter(curLine, to.line + 1, function(line) {
3580
- if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(doc, line) == cm.display.maxLine)
3581
- updateMaxLine = true;
3582
- var span = {from: null, to: null, marker: marker};
3583
- size += line.text.length;
3584
- if (curLine == from.line) {span.from = from.ch; size -= from.ch;}
3585
- if (curLine == to.line) {span.to = to.ch; size -= line.text.length - to.ch;}
3586
- if (marker.collapsed) {
3587
- if (curLine == to.line) collapsedAtEnd = collapsedSpanAt(line, to.ch);
3588
- if (curLine == from.line) collapsedAtStart = collapsedSpanAt(line, from.ch);
3589
- else updateLineHeight(line, 0);
3590
- }
3591
- addMarkedSpan(line, span);
3592
- ++curLine;
3593
- });
3594
- if (marker.collapsed) doc.iter(from.line, to.line + 1, function(line) {
3595
- if (lineIsHidden(doc, line)) updateLineHeight(line, 0);
3596
- });
3597
-
3598
- if (marker.clearOnEnter) on(marker, "beforeCursorEnter", function() { marker.clear(); });
3599
-
3600
- if (marker.readOnly) {
3601
- sawReadOnlySpans = true;
3602
- if (doc.history.done.length || doc.history.undone.length)
3603
- doc.clearHistory();
3604
- }
3605
- if (marker.collapsed) {
3606
- if (collapsedAtStart != collapsedAtEnd)
3607
- throw new Error("Inserting collapsed marker overlapping an existing one");
3608
- marker.size = size;
3609
- marker.atomic = true;
3610
- }
3611
- if (cm) {
3612
- if (updateMaxLine) cm.curOp.updateMaxLine = true;
3613
- if (marker.className || marker.startStyle || marker.endStyle || marker.collapsed)
3614
- regChange(cm, from.line, to.line + 1);
3615
- if (marker.atomic) reCheckSelection(cm);
3616
- }
3617
- return marker;
3618
- }
3619
-
3620
- // SHARED TEXTMARKERS
3621
-
3622
- function SharedTextMarker(markers, primary) {
3623
- this.markers = markers;
3624
- this.primary = primary;
3625
- for (var i = 0, me = this; i < markers.length; ++i) {
3626
- markers[i].parent = this;
3627
- on(markers[i], "clear", function(){me.clear();});
3628
- }
3629
- }
3630
- CodeMirror.SharedTextMarker = SharedTextMarker;
3631
-
3632
- SharedTextMarker.prototype.clear = function() {
3633
- if (this.explicitlyCleared) return;
3634
- this.explicitlyCleared = true;
3635
- for (var i = 0; i < this.markers.length; ++i)
3636
- this.markers[i].clear();
3637
- signalLater(this, "clear");
3638
- };
3639
- SharedTextMarker.prototype.find = function() {
3640
- return this.primary.find();
3641
- };
3642
- SharedTextMarker.prototype.getOptions = function(copyWidget) {
3643
- var inner = this.primary.getOptions(copyWidget);
3644
- inner.shared = true;
3645
- return inner;
3646
- };
3647
-
3648
- function markTextShared(doc, from, to, options, type) {
3649
- options = copyObj(options);
3650
- options.shared = false;
3651
- var markers = [markText(doc, from, to, options, type)], primary = markers[0];
3652
- var widget = options.replacedWith;
3653
- linkedDocs(doc, function(doc) {
3654
- if (widget) options.replacedWith = widget.cloneNode(true);
3655
- markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type));
3656
- for (var i = 0; i < doc.linked.length; ++i)
3657
- if (doc.linked[i].isParent) return;
3658
- primary = lst(markers);
3659
- });
3660
- return new SharedTextMarker(markers, primary);
3661
- }
3662
-
3663
- // TEXTMARKER SPANS
3664
-
3665
- function getMarkedSpanFor(spans, marker) {
3666
- if (spans) for (var i = 0; i < spans.length; ++i) {
3667
- var span = spans[i];
3668
- if (span.marker == marker) return span;
3669
- }
3670
- }
3671
- function removeMarkedSpan(spans, span) {
3672
- for (var r, i = 0; i < spans.length; ++i)
3673
- if (spans[i] != span) (r || (r = [])).push(spans[i]);
3674
- return r;
3675
- }
3676
- function addMarkedSpan(line, span) {
3677
- line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span];
3678
- span.marker.attachLine(line);
3679
- }
3680
-
3681
- function markedSpansBefore(old, startCh, isInsert) {
3682
- if (old) for (var i = 0, nw; i < old.length; ++i) {
3683
- var span = old[i], marker = span.marker;
3684
- var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh);
3685
- if (startsBefore || marker.type == "bookmark" && span.from == startCh && (!isInsert || !span.marker.insertLeft)) {
3686
- var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh);
3687
- (nw || (nw = [])).push({from: span.from,
3688
- to: endsAfter ? null : span.to,
3689
- marker: marker});
3690
- }
3691
- }
3692
- return nw;
3693
- }
3694
-
3695
- function markedSpansAfter(old, endCh, isInsert) {
3696
- if (old) for (var i = 0, nw; i < old.length; ++i) {
3697
- var span = old[i], marker = span.marker;
3698
- var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh);
3699
- if (endsAfter || marker.type == "bookmark" && span.from == endCh && (!isInsert || span.marker.insertLeft)) {
3700
- var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh);
3701
- (nw || (nw = [])).push({from: startsBefore ? null : span.from - endCh,
3702
- to: span.to == null ? null : span.to - endCh,
3703
- marker: marker});
3704
- }
3705
- }
3706
- return nw;
3707
- }
3708
-
3709
- function stretchSpansOverChange(doc, change) {
3710
- var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans;
3711
- var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans;
3712
- if (!oldFirst && !oldLast) return null;
3713
-
3714
- var startCh = change.from.ch, endCh = change.to.ch, isInsert = posEq(change.from, change.to);
3715
- // Get the spans that 'stick out' on both sides
3716
- var first = markedSpansBefore(oldFirst, startCh, isInsert);
3717
- var last = markedSpansAfter(oldLast, endCh, isInsert);
3718
-
3719
- // Next, merge those two ends
3720
- var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0);
3721
- if (first) {
3722
- // Fix up .to properties of first
3723
- for (var i = 0; i < first.length; ++i) {
3724
- var span = first[i];
3725
- if (span.to == null) {
3726
- var found = getMarkedSpanFor(last, span.marker);
3727
- if (!found) span.to = startCh;
3728
- else if (sameLine) span.to = found.to == null ? null : found.to + offset;
3729
- }
3730
- }
3731
- }
3732
- if (last) {
3733
- // Fix up .from in last (or move them into first in case of sameLine)
3734
- for (var i = 0; i < last.length; ++i) {
3735
- var span = last[i];
3736
- if (span.to != null) span.to += offset;
3737
- if (span.from == null) {
3738
- var found = getMarkedSpanFor(first, span.marker);
3739
- if (!found) {
3740
- span.from = offset;
3741
- if (sameLine) (first || (first = [])).push(span);
3742
- }
3743
- } else {
3744
- span.from += offset;
3745
- if (sameLine) (first || (first = [])).push(span);
3746
- }
3747
- }
3748
- }
3749
-
3750
- var newMarkers = [first];
3751
- if (!sameLine) {
3752
- // Fill gap with whole-line-spans
3753
- var gap = change.text.length - 2, gapMarkers;
3754
- if (gap > 0 && first)
3755
- for (var i = 0; i < first.length; ++i)
3756
- if (first[i].to == null)
3757
- (gapMarkers || (gapMarkers = [])).push({from: null, to: null, marker: first[i].marker});
3758
- for (var i = 0; i < gap; ++i)
3759
- newMarkers.push(gapMarkers);
3760
- newMarkers.push(last);
3761
- }
3762
- return newMarkers;
3763
- }
3764
-
3765
- function mergeOldSpans(doc, change) {
3766
- var old = getOldSpans(doc, change);
3767
- var stretched = stretchSpansOverChange(doc, change);
3768
- if (!old) return stretched;
3769
- if (!stretched) return old;
3770
-
3771
- for (var i = 0; i < old.length; ++i) {
3772
- var oldCur = old[i], stretchCur = stretched[i];
3773
- if (oldCur && stretchCur) {
3774
- spans: for (var j = 0; j < stretchCur.length; ++j) {
3775
- var span = stretchCur[j];
3776
- for (var k = 0; k < oldCur.length; ++k)
3777
- if (oldCur[k].marker == span.marker) continue spans;
3778
- oldCur.push(span);
3779
- }
3780
- } else if (stretchCur) {
3781
- old[i] = stretchCur;
3782
- }
3783
- }
3784
- return old;
3785
- }
3786
-
3787
- function removeReadOnlyRanges(doc, from, to) {
3788
- var markers = null;
3789
- doc.iter(from.line, to.line + 1, function(line) {
3790
- if (line.markedSpans) for (var i = 0; i < line.markedSpans.length; ++i) {
3791
- var mark = line.markedSpans[i].marker;
3792
- if (mark.readOnly && (!markers || indexOf(markers, mark) == -1))
3793
- (markers || (markers = [])).push(mark);
3794
- }
3795
- });
3796
- if (!markers) return null;
3797
- var parts = [{from: from, to: to}];
3798
- for (var i = 0; i < markers.length; ++i) {
3799
- var mk = markers[i], m = mk.find();
3800
- for (var j = 0; j < parts.length; ++j) {
3801
- var p = parts[j];
3802
- if (posLess(p.to, m.from) || posLess(m.to, p.from)) continue;
3803
- var newParts = [j, 1];
3804
- if (posLess(p.from, m.from) || !mk.inclusiveLeft && posEq(p.from, m.from))
3805
- newParts.push({from: p.from, to: m.from});
3806
- if (posLess(m.to, p.to) || !mk.inclusiveRight && posEq(p.to, m.to))
3807
- newParts.push({from: m.to, to: p.to});
3808
- parts.splice.apply(parts, newParts);
3809
- j += newParts.length - 1;
3810
- }
3811
- }
3812
- return parts;
3813
- }
3814
-
3815
- function collapsedSpanAt(line, ch) {
3816
- var sps = sawCollapsedSpans && line.markedSpans, found;
3817
- if (sps) for (var sp, i = 0; i < sps.length; ++i) {
3818
- sp = sps[i];
3819
- if (!sp.marker.collapsed) continue;
3820
- if ((sp.from == null || sp.from < ch) &&
3821
- (sp.to == null || sp.to > ch) &&
3822
- (!found || found.width < sp.marker.width))
3823
- found = sp.marker;
3824
- }
3825
- return found;
3826
- }
3827
- function collapsedSpanAtStart(line) { return collapsedSpanAt(line, -1); }
3828
- function collapsedSpanAtEnd(line) { return collapsedSpanAt(line, line.text.length + 1); }
3829
-
3830
- function visualLine(doc, line) {
3831
- var merged;
3832
- while (merged = collapsedSpanAtStart(line))
3833
- line = getLine(doc, merged.find().from.line);
3834
- return line;
3835
- }
3836
-
3837
- function lineIsHidden(doc, line) {
3838
- var sps = sawCollapsedSpans && line.markedSpans;
3839
- if (sps) for (var sp, i = 0; i < sps.length; ++i) {
3840
- sp = sps[i];
3841
- if (!sp.marker.collapsed) continue;
3842
- if (sp.from == null) return true;
3843
- if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp))
3844
- return true;
3845
- }
3846
- }
3847
- function lineIsHiddenInner(doc, line, span) {
3848
- if (span.to == null) {
3849
- var end = span.marker.find().to, endLine = getLine(doc, end.line);
3850
- return lineIsHiddenInner(doc, endLine, getMarkedSpanFor(endLine.markedSpans, span.marker));
3851
- }
3852
- if (span.marker.inclusiveRight && span.to == line.text.length)
3853
- return true;
3854
- for (var sp, i = 0; i < line.markedSpans.length; ++i) {
3855
- sp = line.markedSpans[i];
3856
- if (sp.marker.collapsed && sp.from == span.to &&
3857
- (sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
3858
- lineIsHiddenInner(doc, line, sp)) return true;
3859
- }
3860
- }
3861
-
3862
- function detachMarkedSpans(line) {
3863
- var spans = line.markedSpans;
3864
- if (!spans) return;
3865
- for (var i = 0; i < spans.length; ++i)
3866
- spans[i].marker.detachLine(line);
3867
- line.markedSpans = null;
3868
- }
3869
-
3870
- function attachMarkedSpans(line, spans) {
3871
- if (!spans) return;
3872
- for (var i = 0; i < spans.length; ++i)
3873
- spans[i].marker.attachLine(line);
3874
- line.markedSpans = spans;
3875
- }
3876
-
3877
- // LINE WIDGETS
3878
-
3879
- var LineWidget = CodeMirror.LineWidget = function(cm, node, options) {
3880
- for (var opt in options) if (options.hasOwnProperty(opt))
3881
- this[opt] = options[opt];
3882
- this.cm = cm;
3883
- this.node = node;
3884
- };
3885
- function widgetOperation(f) {
3886
- return function() {
3887
- var withOp = !this.cm.curOp;
3888
- if (withOp) startOperation(this.cm);
3889
- try {var result = f.apply(this, arguments);}
3890
- finally {if (withOp) endOperation(this.cm);}
3891
- return result;
3892
- };
3893
- }
3894
- LineWidget.prototype.clear = widgetOperation(function() {
3895
- var ws = this.line.widgets, no = lineNo(this.line);
3896
- if (no == null || !ws) return;
3897
- for (var i = 0; i < ws.length; ++i) if (ws[i] == this) ws.splice(i--, 1);
3898
- if (!ws.length) this.line.widgets = null;
3899
- updateLineHeight(this.line, Math.max(0, this.line.height - widgetHeight(this)));
3900
- regChange(this.cm, no, no + 1);
3901
- });
3902
- LineWidget.prototype.changed = widgetOperation(function() {
3903
- var oldH = this.height;
3904
- this.height = null;
3905
- var diff = widgetHeight(this) - oldH;
3906
- if (!diff) return;
3907
- updateLineHeight(this.line, this.line.height + diff);
3908
- var no = lineNo(this.line);
3909
- regChange(this.cm, no, no + 1);
3910
- });
3911
-
3912
- function widgetHeight(widget) {
3913
- if (widget.height != null) return widget.height;
3914
- if (!widget.node.parentNode || widget.node.parentNode.nodeType != 1)
3915
- removeChildrenAndAdd(widget.cm.display.measure, elt("div", [widget.node], null, "position: relative"));
3916
- return widget.height = widget.node.offsetHeight;
3917
- }
3918
-
3919
- function addLineWidget(cm, handle, node, options) {
3920
- var widget = new LineWidget(cm, node, options);
3921
- if (widget.noHScroll) cm.display.alignWidgets = true;
3922
- changeLine(cm, handle, function(line) {
3923
- (line.widgets || (line.widgets = [])).push(widget);
3924
- widget.line = line;
3925
- if (!lineIsHidden(cm.doc, line) || widget.showIfHidden) {
3926
- var aboveVisible = heightAtLine(cm, line) < cm.display.scroller.scrollTop;
3927
- updateLineHeight(line, line.height + widgetHeight(widget));
3928
- if (aboveVisible) addToScrollPos(cm, 0, widget.height);
3929
- }
3930
- return true;
3931
- });
3932
- return widget;
3933
- }
3934
-
3935
- // LINE DATA STRUCTURE
3936
-
3937
- // Line objects. These hold state related to a line, including
3938
- // highlighting info (the styles array).
3939
- function makeLine(text, markedSpans, estimateHeight) {
3940
- var line = {text: text};
3941
- attachMarkedSpans(line, markedSpans);
3942
- line.height = estimateHeight ? estimateHeight(line) : 1;
3943
- return line;
3944
- }
3945
-
3946
- function updateLine(line, text, markedSpans, estimateHeight) {
3947
- line.text = text;
3948
- if (line.stateAfter) line.stateAfter = null;
3949
- if (line.styles) line.styles = null;
3950
- if (line.order != null) line.order = null;
3951
- detachMarkedSpans(line);
3952
- attachMarkedSpans(line, markedSpans);
3953
- var estHeight = estimateHeight ? estimateHeight(line) : 1;
3954
- if (estHeight != line.height) updateLineHeight(line, estHeight);
3955
- }
3956
-
3957
- function cleanUpLine(line) {
3958
- line.parent = null;
3959
- detachMarkedSpans(line);
3960
- }
3961
-
3962
- // Run the given mode's parser over a line, update the styles
3963
- // array, which contains alternating fragments of text and CSS
3964
- // classes.
3965
- function runMode(cm, text, mode, state, f) {
3966
- var flattenSpans = mode.flattenSpans;
3967
- if (flattenSpans == null) flattenSpans = cm.options.flattenSpans;
3968
- var curText = "", curStyle = null;
3969
- var stream = new StringStream(text, cm.options.tabSize);
3970
- if (text == "" && mode.blankLine) mode.blankLine(state);
3971
- while (!stream.eol()) {
3972
- var style = mode.token(stream, state);
3973
- if (stream.pos > 5000) {
3974
- flattenSpans = false;
3975
- // Webkit seems to refuse to render text nodes longer than 57444 characters
3976
- stream.pos = Math.min(text.length, stream.start + 50000);
3977
- style = null;
3978
- }
3979
- var substr = stream.current();
3980
- stream.start = stream.pos;
3981
- if (!flattenSpans || curStyle != style) {
3982
- if (curText) f(curText, curStyle);
3983
- curText = substr; curStyle = style;
3984
- } else curText = curText + substr;
3985
- }
3986
- if (curText) f(curText, curStyle);
3987
- }
3988
-
3989
- function highlightLine(cm, line, state) {
3990
- // A styles array always starts with a number identifying the
3991
- // mode/overlays that it is based on (for easy invalidation).
3992
- var st = [cm.state.modeGen];
3993
- // Compute the base array of styles
3994
- runMode(cm, line.text, cm.doc.mode, state, function(txt, style) {st.push(txt, style);});
3995
-
3996
- // Run overlays, adjust style array.
3997
- for (var o = 0; o < cm.state.overlays.length; ++o) {
3998
- var overlay = cm.state.overlays[o], i = 1;
3999
- runMode(cm, line.text, overlay.mode, true, function(txt, style) {
4000
- var start = i, len = txt.length;
4001
- // Ensure there's a token end at the current position, and that i points at it
4002
- while (len) {
4003
- var cur = st[i], len_ = cur.length;
4004
- if (len_ <= len) {
4005
- len -= len_;
4006
- } else {
4007
- st.splice(i, 1, cur.slice(0, len), st[i+1], cur.slice(len));
4008
- len = 0;
4009
- }
4010
- i += 2;
4011
- }
4012
- if (!style) return;
4013
- if (overlay.opaque) {
4014
- st.splice(start, i - start, txt, style);
4015
- i = start + 2;
4016
- } else {
4017
- for (; start < i; start += 2) {
4018
- var cur = st[start+1];
4019
- st[start+1] = cur ? cur + " " + style : style;
4020
- }
4021
- }
4022
- });
4023
- }
4024
-
4025
- return st;
4026
- }
4027
-
4028
- function getLineStyles(cm, line) {
4029
- if (!line.styles || line.styles[0] != cm.state.modeGen)
4030
- line.styles = highlightLine(cm, line, line.stateAfter = getStateBefore(cm, lineNo(line)));
4031
- return line.styles;
4032
- }
4033
-
4034
- // Lightweight form of highlight -- proceed over this line and
4035
- // update state, but don't save a style array.
4036
- function processLine(cm, line, state) {
4037
- var mode = cm.doc.mode;
4038
- var stream = new StringStream(line.text, cm.options.tabSize);
4039
- if (line.text == "" && mode.blankLine) mode.blankLine(state);
4040
- while (!stream.eol() && stream.pos <= 5000) {
4041
- mode.token(stream, state);
4042
- stream.start = stream.pos;
4043
- }
4044
- }
4045
-
4046
- var styleToClassCache = {};
4047
- function styleToClass(style) {
4048
- if (!style) return null;
4049
- return styleToClassCache[style] ||
4050
- (styleToClassCache[style] = "cm-" + style.replace(/ +/g, " cm-"));
4051
- }
4052
-
4053
- function lineContent(cm, realLine, measure) {
4054
- var merged, line = realLine, lineBefore, sawBefore, simple = true;
4055
- while (merged = collapsedSpanAtStart(line)) {
4056
- simple = false;
4057
- line = getLine(cm.doc, merged.find().from.line);
4058
- if (!lineBefore) lineBefore = line;
4059
- }
4060
-
4061
- var builder = {pre: elt("pre"), col: 0, pos: 0, display: !measure,
4062
- measure: null, addedOne: false, cm: cm};
4063
- if (line.textClass) builder.pre.className = line.textClass;
4064
-
4065
- do {
4066
- builder.measure = line == realLine && measure;
4067
- builder.pos = 0;
4068
- builder.addToken = builder.measure ? buildTokenMeasure : buildToken;
4069
- if ((ie || webkit) && cm.getOption("lineWrapping"))
4070
- builder.addToken = buildTokenSplitSpaces(builder.addToken);
4071
- if (measure && sawBefore && line != realLine && !builder.addedOne) {
4072
- measure[0] = builder.pre.appendChild(zeroWidthElement(cm.display.measure));
4073
- builder.addedOne = true;
4074
- }
4075
- var next = insertLineContent(line, builder, getLineStyles(cm, line));
4076
- sawBefore = line == lineBefore;
4077
- if (next) {
4078
- line = getLine(cm.doc, next.to.line);
4079
- simple = false;
4080
- }
4081
- } while (next);
4082
-
4083
- if (measure && !builder.addedOne)
4084
- measure[0] = builder.pre.appendChild(simple ? elt("span", "\u00a0") : zeroWidthElement(cm.display.measure));
4085
- if (!builder.pre.firstChild && !lineIsHidden(cm.doc, realLine))
4086
- builder.pre.appendChild(document.createTextNode("\u00a0"));
4087
-
4088
- var order;
4089
- // Work around problem with the reported dimensions of single-char
4090
- // direction spans on IE (issue #1129). See also the comment in
4091
- // cursorCoords.
4092
- if (measure && ie && (order = getOrder(line))) {
4093
- var l = order.length - 1;
4094
- if (order[l].from == order[l].to) --l;
4095
- var last = order[l], prev = order[l - 1];
4096
- if (last.from + 1 == last.to && prev && last.level < prev.level) {
4097
- var span = measure[builder.pos - 1];
4098
- if (span) span.parentNode.insertBefore(span.measureRight = zeroWidthElement(cm.display.measure),
4099
- span.nextSibling);
4100
- }
4101
- }
4102
-
4103
- signal(cm, "renderLine", cm, realLine, builder.pre);
4104
- return builder.pre;
4105
- }
4106
-
4107
- var tokenSpecialChars = /[\t\u0000-\u0019\u00ad\u200b\u2028\u2029\uFEFF]/g;
4108
- function buildToken(builder, text, style, startStyle, endStyle) {
4109
- if (!text) return;
4110
- if (!tokenSpecialChars.test(text)) {
4111
- builder.col += text.length;
4112
- var content = document.createTextNode(text);
4113
- } else {
4114
- var content = document.createDocumentFragment(), pos = 0;
4115
- while (true) {
4116
- tokenSpecialChars.lastIndex = pos;
4117
- var m = tokenSpecialChars.exec(text);
4118
- var skipped = m ? m.index - pos : text.length - pos;
4119
- if (skipped) {
4120
- content.appendChild(document.createTextNode(text.slice(pos, pos + skipped)));
4121
- builder.col += skipped;
4122
- }
4123
- if (!m) break;
4124
- pos += skipped + 1;
4125
- if (m[0] == "\t") {
4126
- var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize;
4127
- content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab"));
4128
- builder.col += tabWidth;
4129
- } else {
4130
- var token = elt("span", "\u2022", "cm-invalidchar");
4131
- token.title = "\\u" + m[0].charCodeAt(0).toString(16);
4132
- content.appendChild(token);
4133
- builder.col += 1;
4134
- }
4135
- }
4136
- }
4137
- if (style || startStyle || endStyle || builder.measure) {
4138
- var fullStyle = style || "";
4139
- if (startStyle) fullStyle += startStyle;
4140
- if (endStyle) fullStyle += endStyle;
4141
- return builder.pre.appendChild(elt("span", [content], fullStyle));
4142
- }
4143
- builder.pre.appendChild(content);
4144
- }
4145
-
4146
- function buildTokenMeasure(builder, text, style, startStyle, endStyle) {
4147
- var wrapping = builder.cm.options.lineWrapping;
4148
- for (var i = 0; i < text.length; ++i) {
4149
- var ch = text.charAt(i), start = i == 0;
4150
- if (ch >= "\ud800" && ch < "\udbff" && i < text.length - 1) {
4151
- ch = text.slice(i, i + 2);
4152
- ++i;
4153
- } else if (i && wrapping &&
4154
- spanAffectsWrapping.test(text.slice(i - 1, i + 1))) {
4155
- builder.pre.appendChild(elt("wbr"));
4156
- }
4157
- var span = builder.measure[builder.pos] =
4158
- buildToken(builder, ch, style,
4159
- start && startStyle, i == text.length - 1 && endStyle);
4160
- // In IE single-space nodes wrap differently than spaces
4161
- // embedded in larger text nodes, except when set to
4162
- // white-space: normal (issue #1268).
4163
- if (ie && wrapping && ch == " " && i && !/\s/.test(text.charAt(i - 1)) &&
4164
- i < text.length - 1 && !/\s/.test(text.charAt(i + 1)))
4165
- span.style.whiteSpace = "normal";
4166
- builder.pos += ch.length;
4167
- }
4168
- if (text.length) builder.addedOne = true;
4169
- }
4170
-
4171
- function buildTokenSplitSpaces(inner) {
4172
- function split(old) {
4173
- var out = " ";
4174
- for (var i = 0; i < old.length - 2; ++i) out += i % 2 ? " " : "\u00a0";
4175
- out += " ";
4176
- return out;
4177
- }
4178
- return function(builder, text, style, startStyle, endStyle) {
4179
- return inner(builder, text.replace(/ {3,}/, split), style, startStyle, endStyle);
4180
- };
4181
- }
4182
-
4183
- function buildCollapsedSpan(builder, size, widget) {
4184
- if (widget) {
4185
- if (!builder.display) widget = widget.cloneNode(true);
4186
- builder.pre.appendChild(widget);
4187
- if (builder.measure && size) {
4188
- builder.measure[builder.pos] = widget;
4189
- builder.addedOne = true;
4190
- }
4191
- }
4192
- builder.pos += size;
4193
- }
4194
-
4195
- // Outputs a number of spans to make up a line, taking highlighting
4196
- // and marked text into account.
4197
- function insertLineContent(line, builder, styles) {
4198
- var spans = line.markedSpans;
4199
- if (!spans) {
4200
- for (var i = 1; i < styles.length; i+=2)
4201
- builder.addToken(builder, styles[i], styleToClass(styles[i+1]));
4202
- return;
4203
- }
4204
-
4205
- var allText = line.text, len = allText.length;
4206
- var pos = 0, i = 1, text = "", style;
4207
- var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, collapsed;
4208
- for (;;) {
4209
- if (nextChange == pos) { // Update current marker set
4210
- spanStyle = spanEndStyle = spanStartStyle = "";
4211
- collapsed = null; nextChange = Infinity;
4212
- var foundBookmark = null;
4213
- for (var j = 0; j < spans.length; ++j) {
4214
- var sp = spans[j], m = sp.marker;
4215
- if (sp.from <= pos && (sp.to == null || sp.to > pos)) {
4216
- if (sp.to != null && nextChange > sp.to) { nextChange = sp.to; spanEndStyle = ""; }
4217
- if (m.className) spanStyle += " " + m.className;
4218
- if (m.startStyle && sp.from == pos) spanStartStyle += " " + m.startStyle;
4219
- if (m.endStyle && sp.to == nextChange) spanEndStyle += " " + m.endStyle;
4220
- if (m.collapsed && (!collapsed || collapsed.marker.width < m.width))
4221
- collapsed = sp;
4222
- } else if (sp.from > pos && nextChange > sp.from) {
4223
- nextChange = sp.from;
4224
- }
4225
- if (m.type == "bookmark" && sp.from == pos && m.replacedWith)
4226
- foundBookmark = m.replacedWith;
4227
- }
4228
- if (collapsed && (collapsed.from || 0) == pos) {
4229
- buildCollapsedSpan(builder, (collapsed.to == null ? len : collapsed.to) - pos,
4230
- collapsed.from != null && collapsed.marker.replacedWith);
4231
- if (collapsed.to == null) return collapsed.marker.find();
4232
- }
4233
- if (foundBookmark && !collapsed) buildCollapsedSpan(builder, 0, foundBookmark);
4234
- }
4235
- if (pos >= len) break;
4236
-
4237
- var upto = Math.min(len, nextChange);
4238
- while (true) {
4239
- if (text) {
4240
- var end = pos + text.length;
4241
- if (!collapsed) {
4242
- var tokenText = end > upto ? text.slice(0, upto - pos) : text;
4243
- builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle,
4244
- spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "");
4245
- }
4246
- if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;}
4247
- pos = end;
4248
- spanStartStyle = "";
4249
- }
4250
- text = styles[i++]; style = styleToClass(styles[i++]);
4251
- }
4252
- }
4253
- }
4254
-
4255
- // DOCUMENT DATA STRUCTURE
4256
-
4257
- function updateDoc(doc, change, markedSpans, selAfter, estimateHeight) {
4258
- function spansFor(n) {return markedSpans ? markedSpans[n] : null;}
4259
- function update(line, text, spans) {
4260
- updateLine(line, text, spans, estimateHeight);
4261
- signalLater(line, "change", line, change);
4262
- }
4263
-
4264
- var from = change.from, to = change.to, text = change.text;
4265
- var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line);
4266
- var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line;
4267
-
4268
- // First adjust the line structure
4269
- if (from.ch == 0 && to.ch == 0 && lastText == "") {
4270
- // This is a whole-line replace. Treated specially to make
4271
- // sure line objects move the way they are supposed to.
4272
- for (var i = 0, e = text.length - 1, added = []; i < e; ++i)
4273
- added.push(makeLine(text[i], spansFor(i), estimateHeight));
4274
- update(lastLine, lastLine.text, lastSpans);
4275
- if (nlines) doc.remove(from.line, nlines);
4276
- if (added.length) doc.insert(from.line, added);
4277
- } else if (firstLine == lastLine) {
4278
- if (text.length == 1) {
4279
- update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans);
4280
- } else {
4281
- for (var added = [], i = 1, e = text.length - 1; i < e; ++i)
4282
- added.push(makeLine(text[i], spansFor(i), estimateHeight));
4283
- added.push(makeLine(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight));
4284
- update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
4285
- doc.insert(from.line + 1, added);
4286
- }
4287
- } else if (text.length == 1) {
4288
- update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0));
4289
- doc.remove(from.line + 1, nlines);
4290
- } else {
4291
- update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
4292
- update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans);
4293
- for (var i = 1, e = text.length - 1, added = []; i < e; ++i)
4294
- added.push(makeLine(text[i], spansFor(i), estimateHeight));
4295
- if (nlines > 1) doc.remove(from.line + 1, nlines - 1);
4296
- doc.insert(from.line + 1, added);
4297
- }
4298
-
4299
- signalLater(doc, "change", doc, change);
4300
- setSelection(doc, selAfter.anchor, selAfter.head, null, true);
4301
- }
4302
-
4303
- function LeafChunk(lines) {
4304
- this.lines = lines;
4305
- this.parent = null;
4306
- for (var i = 0, e = lines.length, height = 0; i < e; ++i) {
4307
- lines[i].parent = this;
4308
- height += lines[i].height;
4309
- }
4310
- this.height = height;
4311
- }
4312
-
4313
- LeafChunk.prototype = {
4314
- chunkSize: function() { return this.lines.length; },
4315
- removeInner: function(at, n) {
4316
- for (var i = at, e = at + n; i < e; ++i) {
4317
- var line = this.lines[i];
4318
- this.height -= line.height;
4319
- cleanUpLine(line);
4320
- signalLater(line, "delete");
4321
- }
4322
- this.lines.splice(at, n);
4323
- },
4324
- collapse: function(lines) {
4325
- lines.splice.apply(lines, [lines.length, 0].concat(this.lines));
4326
- },
4327
- insertInner: function(at, lines, height) {
4328
- this.height += height;
4329
- this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at));
4330
- for (var i = 0, e = lines.length; i < e; ++i) lines[i].parent = this;
4331
- },
4332
- iterN: function(at, n, op) {
4333
- for (var e = at + n; at < e; ++at)
4334
- if (op(this.lines[at])) return true;
4335
- }
4336
- };
4337
-
4338
- function BranchChunk(children) {
4339
- this.children = children;
4340
- var size = 0, height = 0;
4341
- for (var i = 0, e = children.length; i < e; ++i) {
4342
- var ch = children[i];
4343
- size += ch.chunkSize(); height += ch.height;
4344
- ch.parent = this;
4345
- }
4346
- this.size = size;
4347
- this.height = height;
4348
- this.parent = null;
4349
- }
4350
-
4351
- BranchChunk.prototype = {
4352
- chunkSize: function() { return this.size; },
4353
- removeInner: function(at, n) {
4354
- this.size -= n;
4355
- for (var i = 0; i < this.children.length; ++i) {
4356
- var child = this.children[i], sz = child.chunkSize();
4357
- if (at < sz) {
4358
- var rm = Math.min(n, sz - at), oldHeight = child.height;
4359
- child.removeInner(at, rm);
4360
- this.height -= oldHeight - child.height;
4361
- if (sz == rm) { this.children.splice(i--, 1); child.parent = null; }
4362
- if ((n -= rm) == 0) break;
4363
- at = 0;
4364
- } else at -= sz;
4365
- }
4366
- if (this.size - n < 25) {
4367
- var lines = [];
4368
- this.collapse(lines);
4369
- this.children = [new LeafChunk(lines)];
4370
- this.children[0].parent = this;
4371
- }
4372
- },
4373
- collapse: function(lines) {
4374
- for (var i = 0, e = this.children.length; i < e; ++i) this.children[i].collapse(lines);
4375
- },
4376
- insertInner: function(at, lines, height) {
4377
- this.size += lines.length;
4378
- this.height += height;
4379
- for (var i = 0, e = this.children.length; i < e; ++i) {
4380
- var child = this.children[i], sz = child.chunkSize();
4381
- if (at <= sz) {
4382
- child.insertInner(at, lines, height);
4383
- if (child.lines && child.lines.length > 50) {
4384
- while (child.lines.length > 50) {
4385
- var spilled = child.lines.splice(child.lines.length - 25, 25);
4386
- var newleaf = new LeafChunk(spilled);
4387
- child.height -= newleaf.height;
4388
- this.children.splice(i + 1, 0, newleaf);
4389
- newleaf.parent = this;
4390
- }
4391
- this.maybeSpill();
4392
- }
4393
- break;
4394
- }
4395
- at -= sz;
4396
- }
4397
- },
4398
- maybeSpill: function() {
4399
- if (this.children.length <= 10) return;
4400
- var me = this;
4401
- do {
4402
- var spilled = me.children.splice(me.children.length - 5, 5);
4403
- var sibling = new BranchChunk(spilled);
4404
- if (!me.parent) { // Become the parent node
4405
- var copy = new BranchChunk(me.children);
4406
- copy.parent = me;
4407
- me.children = [copy, sibling];
4408
- me = copy;
4409
- } else {
4410
- me.size -= sibling.size;
4411
- me.height -= sibling.height;
4412
- var myIndex = indexOf(me.parent.children, me);
4413
- me.parent.children.splice(myIndex + 1, 0, sibling);
4414
- }
4415
- sibling.parent = me.parent;
4416
- } while (me.children.length > 10);
4417
- me.parent.maybeSpill();
4418
- },
4419
- iterN: function(at, n, op) {
4420
- for (var i = 0, e = this.children.length; i < e; ++i) {
4421
- var child = this.children[i], sz = child.chunkSize();
4422
- if (at < sz) {
4423
- var used = Math.min(n, sz - at);
4424
- if (child.iterN(at, used, op)) return true;
4425
- if ((n -= used) == 0) break;
4426
- at = 0;
4427
- } else at -= sz;
4428
- }
4429
- }
4430
- };
4431
-
4432
- var nextDocId = 0;
4433
- var Doc = CodeMirror.Doc = function(text, mode, firstLine) {
4434
- if (!(this instanceof Doc)) return new Doc(text, mode, firstLine);
4435
- if (firstLine == null) firstLine = 0;
4436
-
4437
- BranchChunk.call(this, [new LeafChunk([makeLine("", null)])]);
4438
- this.first = firstLine;
4439
- this.scrollTop = this.scrollLeft = 0;
4440
- this.cantEdit = false;
4441
- this.history = makeHistory();
4442
- this.frontier = firstLine;
4443
- var start = Pos(firstLine, 0);
4444
- this.sel = {from: start, to: start, head: start, anchor: start, shift: false, extend: false, goalColumn: null};
4445
- this.id = ++nextDocId;
4446
- this.modeOption = mode;
4447
-
4448
- if (typeof text == "string") text = splitLines(text);
4449
- updateDoc(this, {from: start, to: start, text: text}, null, {head: start, anchor: start});
4450
- };
4451
-
4452
- Doc.prototype = createObj(BranchChunk.prototype, {
4453
- iter: function(from, to, op) {
4454
- if (op) this.iterN(from - this.first, to - from, op);
4455
- else this.iterN(this.first, this.first + this.size, from);
4456
- },
4457
-
4458
- insert: function(at, lines) {
4459
- var height = 0;
4460
- for (var i = 0, e = lines.length; i < e; ++i) height += lines[i].height;
4461
- this.insertInner(at - this.first, lines, height);
4462
- },
4463
- remove: function(at, n) { this.removeInner(at - this.first, n); },
4464
-
4465
- getValue: function(lineSep) {
4466
- var lines = getLines(this, this.first, this.first + this.size);
4467
- if (lineSep === false) return lines;
4468
- return lines.join(lineSep || "\n");
4469
- },
4470
- setValue: function(code) {
4471
- var top = Pos(this.first, 0), last = this.first + this.size - 1;
4472
- makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length),
4473
- text: splitLines(code), origin: "setValue"},
4474
- {head: top, anchor: top}, true);
4475
- },
4476
- replaceRange: function(code, from, to, origin) {
4477
- from = clipPos(this, from);
4478
- to = to ? clipPos(this, to) : from;
4479
- replaceRange(this, code, from, to, origin);
4480
- },
4481
- getRange: function(from, to, lineSep) {
4482
- var lines = getBetween(this, clipPos(this, from), clipPos(this, to));
4483
- if (lineSep === false) return lines;
4484
- return lines.join(lineSep || "\n");
4485
- },
4486
-
4487
- getLine: function(line) {var l = this.getLineHandle(line); return l && l.text;},
4488
- setLine: function(line, text) {
4489
- if (isLine(this, line))
4490
- replaceRange(this, text, Pos(line, 0), clipPos(this, Pos(line)));
4491
- },
4492
- removeLine: function(line) {
4493
- if (isLine(this, line))
4494
- replaceRange(this, "", Pos(line, 0), clipPos(this, Pos(line + 1, 0)));
4495
- },
4496
-
4497
- getLineHandle: function(line) {if (isLine(this, line)) return getLine(this, line);},
4498
- getLineNumber: function(line) {return lineNo(line);},
4499
-
4500
- lineCount: function() {return this.size;},
4501
- firstLine: function() {return this.first;},
4502
- lastLine: function() {return this.first + this.size - 1;},
4503
-
4504
- clipPos: function(pos) {return clipPos(this, pos);},
4505
-
4506
- getCursor: function(start) {
4507
- var sel = this.sel, pos;
4508
- if (start == null || start == "head") pos = sel.head;
4509
- else if (start == "anchor") pos = sel.anchor;
4510
- else if (start == "end" || start === false) pos = sel.to;
4511
- else pos = sel.from;
4512
- return copyPos(pos);
4513
- },
4514
- somethingSelected: function() {return !posEq(this.sel.head, this.sel.anchor);},
4515
-
4516
- setCursor: docOperation(function(line, ch, extend) {
4517
- var pos = clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line);
4518
- if (extend) extendSelection(this, pos);
4519
- else setSelection(this, pos, pos);
4520
- }),
4521
- setSelection: docOperation(function(anchor, head) {
4522
- setSelection(this, clipPos(this, anchor), clipPos(this, head || anchor));
4523
- }),
4524
- extendSelection: docOperation(function(from, to) {
4525
- extendSelection(this, clipPos(this, from), to && clipPos(this, to));
4526
- }),
4527
-
4528
- getSelection: function(lineSep) {return this.getRange(this.sel.from, this.sel.to, lineSep);},
4529
- replaceSelection: function(code, collapse, origin) {
4530
- makeChange(this, {from: this.sel.from, to: this.sel.to, text: splitLines(code), origin: origin}, collapse || "around");
4531
- },
4532
- undo: docOperation(function() {makeChangeFromHistory(this, "undo");}),
4533
- redo: docOperation(function() {makeChangeFromHistory(this, "redo");}),
4534
-
4535
- setExtending: function(val) {this.sel.extend = val;},
4536
-
4537
- historySize: function() {
4538
- var hist = this.history;
4539
- return {undo: hist.done.length, redo: hist.undone.length};
4540
- },
4541
- clearHistory: function() {this.history = makeHistory();},
4542
-
4543
- markClean: function() {
4544
- this.history.dirtyCounter = 0;
4545
- this.history.lastOp = this.history.lastOrigin = null;
4546
- },
4547
- isClean: function () {return this.history.dirtyCounter == 0;},
4548
-
4549
- getHistory: function() {
4550
- return {done: copyHistoryArray(this.history.done),
4551
- undone: copyHistoryArray(this.history.undone)};
4552
- },
4553
- setHistory: function(histData) {
4554
- var hist = this.history = makeHistory();
4555
- hist.done = histData.done.slice(0);
4556
- hist.undone = histData.undone.slice(0);
4557
- },
4558
-
4559
- markText: function(from, to, options) {
4560
- return markText(this, clipPos(this, from), clipPos(this, to), options, "range");
4561
- },
4562
- setBookmark: function(pos, options) {
4563
- var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options),
4564
- insertLeft: options && options.insertLeft};
4565
- pos = clipPos(this, pos);
4566
- return markText(this, pos, pos, realOpts, "bookmark");
4567
- },
4568
- findMarksAt: function(pos) {
4569
- pos = clipPos(this, pos);
4570
- var markers = [], spans = getLine(this, pos.line).markedSpans;
4571
- if (spans) for (var i = 0; i < spans.length; ++i) {
4572
- var span = spans[i];
4573
- if ((span.from == null || span.from <= pos.ch) &&
4574
- (span.to == null || span.to >= pos.ch))
4575
- markers.push(span.marker.parent || span.marker);
4576
- }
4577
- return markers;
4578
- },
4579
- getAllMarks: function() {
4580
- var markers = [];
4581
- this.iter(function(line) {
4582
- var sps = line.markedSpans;
4583
- if (sps) for (var i = 0; i < sps.length; ++i)
4584
- if (sps[i].from != null) markers.push(sps[i].marker);
4585
- });
4586
- return markers;
4587
- },
4588
-
4589
- posFromIndex: function(off) {
4590
- var ch, lineNo = this.first;
4591
- this.iter(function(line) {
4592
- var sz = line.text.length + 1;
4593
- if (sz > off) { ch = off; return true; }
4594
- off -= sz;
4595
- ++lineNo;
4596
- });
4597
- return clipPos(this, Pos(lineNo, ch));
4598
- },
4599
- indexFromPos: function (coords) {
4600
- coords = clipPos(this, coords);
4601
- var index = coords.ch;
4602
- if (coords.line < this.first || coords.ch < 0) return 0;
4603
- this.iter(this.first, coords.line, function (line) {
4604
- index += line.text.length + 1;
4605
- });
4606
- return index;
4607
- },
4608
-
4609
- copy: function(copyHistory) {
4610
- var doc = new Doc(getLines(this, this.first, this.first + this.size), this.modeOption, this.first);
4611
- doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft;
4612
- doc.sel = {from: this.sel.from, to: this.sel.to, head: this.sel.head, anchor: this.sel.anchor,
4613
- shift: this.sel.shift, extend: false, goalColumn: this.sel.goalColumn};
4614
- if (copyHistory) {
4615
- doc.history.undoDepth = this.history.undoDepth;
4616
- doc.setHistory(this.getHistory());
4617
- }
4618
- return doc;
4619
- },
4620
-
4621
- linkedDoc: function(options) {
4622
- if (!options) options = {};
4623
- var from = this.first, to = this.first + this.size;
4624
- if (options.from != null && options.from > from) from = options.from;
4625
- if (options.to != null && options.to < to) to = options.to;
4626
- var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from);
4627
- if (options.sharedHist) copy.history = this.history;
4628
- (this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist});
4629
- copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}];
4630
- return copy;
4631
- },
4632
- unlinkDoc: function(other) {
4633
- if (other instanceof CodeMirror) other = other.doc;
4634
- if (this.linked) for (var i = 0; i < this.linked.length; ++i) {
4635
- var link = this.linked[i];
4636
- if (link.doc != other) continue;
4637
- this.linked.splice(i, 1);
4638
- other.unlinkDoc(this);
4639
- break;
4640
- }
4641
- // If the histories were shared, split them again
4642
- if (other.history == this.history) {
4643
- var splitIds = [other.id];
4644
- linkedDocs(other, function(doc) {splitIds.push(doc.id);}, true);
4645
- other.history = makeHistory();
4646
- other.history.done = copyHistoryArray(this.history.done, splitIds);
4647
- other.history.undone = copyHistoryArray(this.history.undone, splitIds);
4648
- }
4649
- },
4650
- iterLinkedDocs: function(f) {linkedDocs(this, f);},
4651
-
4652
- getMode: function() {return this.mode;},
4653
- getEditor: function() {return this.cm;}
4654
- });
4655
-
4656
- Doc.prototype.eachLine = Doc.prototype.iter;
4657
-
4658
- // The Doc methods that should be available on CodeMirror instances
4659
- var dontDelegate = "iter insert remove copy getEditor".split(" ");
4660
- for (var prop in Doc.prototype) if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0)
4661
- CodeMirror.prototype[prop] = (function(method) {
4662
- return function() {return method.apply(this.doc, arguments);};
4663
- })(Doc.prototype[prop]);
4664
-
4665
- function linkedDocs(doc, f, sharedHistOnly) {
4666
- function propagate(doc, skip, sharedHist) {
4667
- if (doc.linked) for (var i = 0; i < doc.linked.length; ++i) {
4668
- var rel = doc.linked[i];
4669
- if (rel.doc == skip) continue;
4670
- var shared = sharedHist && rel.sharedHist;
4671
- if (sharedHistOnly && !shared) continue;
4672
- f(rel.doc, shared);
4673
- propagate(rel.doc, doc, shared);
4674
- }
4675
- }
4676
- propagate(doc, null, true);
4677
- }
4678
-
4679
- function attachDoc(cm, doc) {
4680
- if (doc.cm) throw new Error("This document is already in use.");
4681
- cm.doc = doc;
4682
- doc.cm = cm;
4683
- estimateLineHeights(cm);
4684
- loadMode(cm);
4685
- if (!cm.options.lineWrapping) computeMaxLength(cm);
4686
- cm.options.mode = doc.modeOption;
4687
- regChange(cm);
4688
- }
4689
-
4690
- // LINE UTILITIES
4691
-
4692
- function getLine(chunk, n) {
4693
- n -= chunk.first;
4694
- while (!chunk.lines) {
4695
- for (var i = 0;; ++i) {
4696
- var child = chunk.children[i], sz = child.chunkSize();
4697
- if (n < sz) { chunk = child; break; }
4698
- n -= sz;
4699
- }
4700
- }
4701
- return chunk.lines[n];
4702
- }
4703
-
4704
- function getBetween(doc, start, end) {
4705
- var out = [], n = start.line;
4706
- doc.iter(start.line, end.line + 1, function(line) {
4707
- var text = line.text;
4708
- if (n == end.line) text = text.slice(0, end.ch);
4709
- if (n == start.line) text = text.slice(start.ch);
4710
- out.push(text);
4711
- ++n;
4712
- });
4713
- return out;
4714
- }
4715
- function getLines(doc, from, to) {
4716
- var out = [];
4717
- doc.iter(from, to, function(line) { out.push(line.text); });
4718
- return out;
4719
- }
4720
-
4721
- function updateLineHeight(line, height) {
4722
- var diff = height - line.height;
4723
- for (var n = line; n; n = n.parent) n.height += diff;
4724
- }
4725
-
4726
- function lineNo(line) {
4727
- if (line.parent == null) return null;
4728
- var cur = line.parent, no = indexOf(cur.lines, line);
4729
- for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {
4730
- for (var i = 0;; ++i) {
4731
- if (chunk.children[i] == cur) break;
4732
- no += chunk.children[i].chunkSize();
4733
- }
4734
- }
4735
- return no + cur.first;
4736
- }
4737
-
4738
- function lineAtHeight(chunk, h) {
4739
- var n = chunk.first;
4740
- outer: do {
4741
- for (var i = 0, e = chunk.children.length; i < e; ++i) {
4742
- var child = chunk.children[i], ch = child.height;
4743
- if (h < ch) { chunk = child; continue outer; }
4744
- h -= ch;
4745
- n += child.chunkSize();
4746
- }
4747
- return n;
4748
- } while (!chunk.lines);
4749
- for (var i = 0, e = chunk.lines.length; i < e; ++i) {
4750
- var line = chunk.lines[i], lh = line.height;
4751
- if (h < lh) break;
4752
- h -= lh;
4753
- }
4754
- return n + i;
4755
- }
4756
-
4757
- function heightAtLine(cm, lineObj) {
4758
- lineObj = visualLine(cm.doc, lineObj);
4759
-
4760
- var h = 0, chunk = lineObj.parent;
4761
- for (var i = 0; i < chunk.lines.length; ++i) {
4762
- var line = chunk.lines[i];
4763
- if (line == lineObj) break;
4764
- else h += line.height;
4765
- }
4766
- for (var p = chunk.parent; p; chunk = p, p = chunk.parent) {
4767
- for (var i = 0; i < p.children.length; ++i) {
4768
- var cur = p.children[i];
4769
- if (cur == chunk) break;
4770
- else h += cur.height;
4771
- }
4772
- }
4773
- return h;
4774
- }
4775
-
4776
- function getOrder(line) {
4777
- var order = line.order;
4778
- if (order == null) order = line.order = bidiOrdering(line.text);
4779
- return order;
4780
- }
4781
-
4782
- // HISTORY
4783
-
4784
- function makeHistory() {
4785
- return {
4786
- // Arrays of history events. Doing something adds an event to
4787
- // done and clears undo. Undoing moves events from done to
4788
- // undone, redoing moves them in the other direction.
4789
- done: [], undone: [], undoDepth: Infinity,
4790
- // Used to track when changes can be merged into a single undo
4791
- // event
4792
- lastTime: 0, lastOp: null, lastOrigin: null,
4793
- // Used by the isClean() method
4794
- dirtyCounter: 0
4795
- };
4796
- }
4797
-
4798
- function attachLocalSpans(doc, change, from, to) {
4799
- var existing = change["spans_" + doc.id], n = 0;
4800
- doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function(line) {
4801
- if (line.markedSpans)
4802
- (existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans;
4803
- ++n;
4804
- });
4805
- }
4806
-
4807
- function historyChangeFromChange(doc, change) {
4808
- var histChange = {from: change.from, to: changeEnd(change), text: getBetween(doc, change.from, change.to)};
4809
- attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);
4810
- linkedDocs(doc, function(doc) {attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);}, true);
4811
- return histChange;
4812
- }
4813
-
4814
- function addToHistory(doc, change, selAfter, opId) {
4815
- var hist = doc.history;
4816
- hist.undone.length = 0;
4817
- var time = +new Date, cur = lst(hist.done);
4818
-
4819
- if (cur &&
4820
- (hist.lastOp == opId ||
4821
- hist.lastOrigin == change.origin && change.origin &&
4822
- ((change.origin.charAt(0) == "+" && hist.lastTime > time - 600) || change.origin.charAt(0) == "*"))) {
4823
- // Merge this change into the last event
4824
- var last = lst(cur.changes);
4825
- if (posEq(change.from, change.to) && posEq(change.from, last.to)) {
4826
- // Optimized case for simple insertion -- don't want to add
4827
- // new changesets for every character typed
4828
- last.to = changeEnd(change);
4829
- } else {
4830
- // Add new sub-event
4831
- cur.changes.push(historyChangeFromChange(doc, change));
4832
- }
4833
- cur.anchorAfter = selAfter.anchor; cur.headAfter = selAfter.head;
4834
- } else {
4835
- // Can not be merged, start a new event.
4836
- cur = {changes: [historyChangeFromChange(doc, change)],
4837
- anchorBefore: doc.sel.anchor, headBefore: doc.sel.head,
4838
- anchorAfter: selAfter.anchor, headAfter: selAfter.head};
4839
- hist.done.push(cur);
4840
- while (hist.done.length > hist.undoDepth)
4841
- hist.done.shift();
4842
- if (hist.dirtyCounter < 0)
4843
- // The user has made a change after undoing past the last clean state.
4844
- // We can never get back to a clean state now until markClean() is called.
4845
- hist.dirtyCounter = NaN;
4846
- else
4847
- hist.dirtyCounter++;
4848
- }
4849
- hist.lastTime = time;
4850
- hist.lastOp = opId;
4851
- hist.lastOrigin = change.origin;
4852
- }
4853
-
4854
- function removeClearedSpans(spans) {
4855
- if (!spans) return null;
4856
- for (var i = 0, out; i < spans.length; ++i) {
4857
- if (spans[i].marker.explicitlyCleared) { if (!out) out = spans.slice(0, i); }
4858
- else if (out) out.push(spans[i]);
4859
- }
4860
- return !out ? spans : out.length ? out : null;
4861
- }
4862
-
4863
- function getOldSpans(doc, change) {
4864
- var found = change["spans_" + doc.id];
4865
- if (!found) return null;
4866
- for (var i = 0, nw = []; i < change.text.length; ++i)
4867
- nw.push(removeClearedSpans(found[i]));
4868
- return nw;
4869
- }
4870
-
4871
- // Used both to provide a JSON-safe object in .getHistory, and, when
4872
- // detaching a document, to split the history in two
4873
- function copyHistoryArray(events, newGroup) {
4874
- for (var i = 0, copy = []; i < events.length; ++i) {
4875
- var event = events[i], changes = event.changes, newChanges = [];
4876
- copy.push({changes: newChanges, anchorBefore: event.anchorBefore, headBefore: event.headBefore,
4877
- anchorAfter: event.anchorAfter, headAfter: event.headAfter});
4878
- for (var j = 0; j < changes.length; ++j) {
4879
- var change = changes[j], m;
4880
- newChanges.push({from: change.from, to: change.to, text: change.text});
4881
- if (newGroup) for (var prop in change) if (m = prop.match(/^spans_(\d+)$/)) {
4882
- if (indexOf(newGroup, Number(m[1])) > -1) {
4883
- lst(newChanges)[prop] = change[prop];
4884
- delete change[prop];
4885
- }
4886
- }
4887
- }
4888
- }
4889
- return copy;
4890
- }
4891
-
4892
- // Rebasing/resetting history to deal with externally-sourced changes
4893
-
4894
- function rebaseHistSel(pos, from, to, diff) {
4895
- if (to < pos.line) {
4896
- pos.line += diff;
4897
- } else if (from < pos.line) {
4898
- pos.line = from;
4899
- pos.ch = 0;
4900
- }
4901
- }
4902
-
4903
- // Tries to rebase an array of history events given a change in the
4904
- // document. If the change touches the same lines as the event, the
4905
- // event, and everything 'behind' it, is discarded. If the change is
4906
- // before the event, the event's positions are updated. Uses a
4907
- // copy-on-write scheme for the positions, to avoid having to
4908
- // reallocate them all on every rebase, but also avoid problems with
4909
- // shared position objects being unsafely updated.
4910
- function rebaseHistArray(array, from, to, diff) {
4911
- for (var i = 0; i < array.length; ++i) {
4912
- var sub = array[i], ok = true;
4913
- for (var j = 0; j < sub.changes.length; ++j) {
4914
- var cur = sub.changes[j];
4915
- if (!sub.copied) { cur.from = copyPos(cur.from); cur.to = copyPos(cur.to); }
4916
- if (to < cur.from.line) {
4917
- cur.from.line += diff;
4918
- cur.to.line += diff;
4919
- } else if (from <= cur.to.line) {
4920
- ok = false;
4921
- break;
4922
- }
4923
- }
4924
- if (!sub.copied) {
4925
- sub.anchorBefore = copyPos(sub.anchorBefore); sub.headBefore = copyPos(sub.headBefore);
4926
- sub.anchorAfter = copyPos(sub.anchorAfter); sub.readAfter = copyPos(sub.headAfter);
4927
- sub.copied = true;
4928
- }
4929
- if (!ok) {
4930
- array.splice(0, i + 1);
4931
- i = 0;
4932
- } else {
4933
- rebaseHistSel(sub.anchorBefore); rebaseHistSel(sub.headBefore);
4934
- rebaseHistSel(sub.anchorAfter); rebaseHistSel(sub.headAfter);
4935
- }
4936
- }
4937
- }
4938
-
4939
- function rebaseHist(hist, change) {
4940
- var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1;
4941
- rebaseHistArray(hist.done, from, to, diff);
4942
- rebaseHistArray(hist.undone, from, to, diff);
4943
- }
4944
-
4945
- // EVENT OPERATORS
4946
-
4947
- function stopMethod() {e_stop(this);}
4948
- // Ensure an event has a stop method.
4949
- function addStop(event) {
4950
- if (!event.stop) event.stop = stopMethod;
4951
- return event;
4952
- }
4953
-
4954
- function e_preventDefault(e) {
4955
- if (e.preventDefault) e.preventDefault();
4956
- else e.returnValue = false;
4957
- }
4958
- function e_stopPropagation(e) {
4959
- if (e.stopPropagation) e.stopPropagation();
4960
- else e.cancelBubble = true;
4961
- }
4962
- function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);}
4963
- CodeMirror.e_stop = e_stop;
4964
- CodeMirror.e_preventDefault = e_preventDefault;
4965
- CodeMirror.e_stopPropagation = e_stopPropagation;
4966
-
4967
- function e_target(e) {return e.target || e.srcElement;}
4968
- function e_button(e) {
4969
- var b = e.which;
4970
- if (b == null) {
4971
- if (e.button & 1) b = 1;
4972
- else if (e.button & 2) b = 3;
4973
- else if (e.button & 4) b = 2;
4974
- }
4975
- if (mac && e.ctrlKey && b == 1) b = 3;
4976
- return b;
4977
- }
4978
-
4979
- // EVENT HANDLING
4980
-
4981
- function on(emitter, type, f) {
4982
- if (emitter.addEventListener)
4983
- emitter.addEventListener(type, f, false);
4984
- else if (emitter.attachEvent)
4985
- emitter.attachEvent("on" + type, f);
4986
- else {
4987
- var map = emitter._handlers || (emitter._handlers = {});
4988
- var arr = map[type] || (map[type] = []);
4989
- arr.push(f);
4990
- }
4991
- }
4992
-
4993
- function off(emitter, type, f) {
4994
- if (emitter.removeEventListener)
4995
- emitter.removeEventListener(type, f, false);
4996
- else if (emitter.detachEvent)
4997
- emitter.detachEvent("on" + type, f);
4998
- else {
4999
- var arr = emitter._handlers && emitter._handlers[type];
5000
- if (!arr) return;
5001
- for (var i = 0; i < arr.length; ++i)
5002
- if (arr[i] == f) { arr.splice(i, 1); break; }
5003
- }
5004
- }
5005
-
5006
- function signal(emitter, type /*, values...*/) {
5007
- var arr = emitter._handlers && emitter._handlers[type];
5008
- if (!arr) return;
5009
- var args = Array.prototype.slice.call(arguments, 2);
5010
- for (var i = 0; i < arr.length; ++i) arr[i].apply(null, args);
5011
- }
5012
-
5013
- var delayedCallbacks, delayedCallbackDepth = 0;
5014
- function signalLater(emitter, type /*, values...*/) {
5015
- var arr = emitter._handlers && emitter._handlers[type];
5016
- if (!arr) return;
5017
- var args = Array.prototype.slice.call(arguments, 2);
5018
- if (!delayedCallbacks) {
5019
- ++delayedCallbackDepth;
5020
- delayedCallbacks = [];
5021
- setTimeout(fireDelayed, 0);
5022
- }
5023
- function bnd(f) {return function(){f.apply(null, args);};};
5024
- for (var i = 0; i < arr.length; ++i)
5025
- delayedCallbacks.push(bnd(arr[i]));
5026
- }
5027
-
5028
- function fireDelayed() {
5029
- --delayedCallbackDepth;
5030
- var delayed = delayedCallbacks;
5031
- delayedCallbacks = null;
5032
- for (var i = 0; i < delayed.length; ++i) delayed[i]();
5033
- }
5034
-
5035
- function hasHandler(emitter, type) {
5036
- var arr = emitter._handlers && emitter._handlers[type];
5037
- return arr && arr.length > 0;
5038
- }
5039
-
5040
- CodeMirror.on = on; CodeMirror.off = off; CodeMirror.signal = signal;
5041
-
5042
- // MISC UTILITIES
5043
-
5044
- // Number of pixels added to scroller and sizer to hide scrollbar
5045
- var scrollerCutOff = 30;
5046
-
5047
- // Returned or thrown by various protocols to signal 'I'm not
5048
- // handling this'.
5049
- var Pass = CodeMirror.Pass = {toString: function(){return "CodeMirror.Pass";}};
5050
-
5051
- function Delayed() {this.id = null;}
5052
- Delayed.prototype = {set: function(ms, f) {clearTimeout(this.id); this.id = setTimeout(f, ms);}};
5053
-
5054
- // Counts the column offset in a string, taking tabs into account.
5055
- // Used mostly to find indentation.
5056
- function countColumn(string, end, tabSize, startIndex, startValue) {
5057
- if (end == null) {
5058
- end = string.search(/[^\s\u00a0]/);
5059
- if (end == -1) end = string.length;
5060
- }
5061
- for (var i = startIndex || 0, n = startValue || 0; i < end; ++i) {
5062
- if (string.charAt(i) == "\t") n += tabSize - (n % tabSize);
5063
- else ++n;
5064
- }
5065
- return n;
5066
- }
5067
- CodeMirror.countColumn = countColumn;
5068
-
5069
- var spaceStrs = [""];
5070
- function spaceStr(n) {
5071
- while (spaceStrs.length <= n)
5072
- spaceStrs.push(lst(spaceStrs) + " ");
5073
- return spaceStrs[n];
5074
- }
5075
-
5076
- function lst(arr) { return arr[arr.length-1]; }
5077
-
5078
- function selectInput(node) {
5079
- if (ios) { // Mobile Safari apparently has a bug where select() is broken.
5080
- node.selectionStart = 0;
5081
- node.selectionEnd = node.value.length;
5082
- } else node.select();
5083
- }
5084
-
5085
- function indexOf(collection, elt) {
5086
- if (collection.indexOf) return collection.indexOf(elt);
5087
- for (var i = 0, e = collection.length; i < e; ++i)
5088
- if (collection[i] == elt) return i;
5089
- return -1;
5090
- }
5091
-
5092
- function createObj(base, props) {
5093
- function Obj() {}
5094
- Obj.prototype = base;
5095
- var inst = new Obj();
5096
- if (props) copyObj(props, inst);
5097
- return inst;
5098
- }
5099
-
5100
- function copyObj(obj, target) {
5101
- if (!target) target = {};
5102
- for (var prop in obj) if (obj.hasOwnProperty(prop)) target[prop] = obj[prop];
5103
- return target;
5104
- }
5105
-
5106
- function emptyArray(size) {
5107
- for (var a = [], i = 0; i < size; ++i) a.push(undefined);
5108
- return a;
5109
- }
5110
-
5111
- function bind(f) {
5112
- var args = Array.prototype.slice.call(arguments, 1);
5113
- return function(){return f.apply(null, args);};
5114
- }
5115
-
5116
- var nonASCIISingleCaseWordChar = /[\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc]/;
5117
- function isWordChar(ch) {
5118
- return /\w/.test(ch) || ch > "\x80" &&
5119
- (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch));
5120
- }
5121
-
5122
- function isEmpty(obj) {
5123
- for (var n in obj) if (obj.hasOwnProperty(n) && obj[n]) return false;
5124
- return true;
5125
- }
5126
-
5127
- var isExtendingChar = /[\u0300-\u036F\u0483-\u0487\u0488-\u0489\u0591-\u05BD\u05BF\u05C1-\u05C2\u05C4-\u05C5\u05C7\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7-\u06E8\u06EA-\u06ED\uA66F\uA670-\uA672\uA674-\uA67D\uA69F\udc00-\udfff]/;
5128
-
5129
- // DOM UTILITIES
5130
-
5131
- function elt(tag, content, className, style) {
5132
- var e = document.createElement(tag);
5133
- if (className) e.className = className;
5134
- if (style) e.style.cssText = style;
5135
- if (typeof content == "string") setTextContent(e, content);
5136
- else if (content) for (var i = 0; i < content.length; ++i) e.appendChild(content[i]);
5137
- return e;
5138
- }
5139
-
5140
- function removeChildren(e) {
5141
- for (var count = e.childNodes.length; count > 0; --count)
5142
- e.removeChild(e.firstChild);
5143
- return e;
5144
- }
5145
-
5146
- function removeChildrenAndAdd(parent, e) {
5147
- return removeChildren(parent).appendChild(e);
5148
- }
5149
-
5150
- function setTextContent(e, str) {
5151
- if (ie_lt9) {
5152
- e.innerHTML = "";
5153
- e.appendChild(document.createTextNode(str));
5154
- } else e.textContent = str;
5155
- }
5156
-
5157
- function getRect(node) {
5158
- return node.getBoundingClientRect();
5159
- }
5160
- CodeMirror.replaceGetRect = function(f) { getRect = f; };
5161
-
5162
- // FEATURE DETECTION
5163
-
5164
- // Detect drag-and-drop
5165
- var dragAndDrop = function() {
5166
- // There is *some* kind of drag-and-drop support in IE6-8, but I
5167
- // couldn't get it to work yet.
5168
- if (ie_lt9) return false;
5169
- var div = elt('div');
5170
- return "draggable" in div || "dragDrop" in div;
5171
- }();
5172
-
5173
- // For a reason I have yet to figure out, some browsers disallow
5174
- // word wrapping between certain characters *only* if a new inline
5175
- // element is started between them. This makes it hard to reliably
5176
- // measure the position of things, since that requires inserting an
5177
- // extra span. This terribly fragile set of regexps matches the
5178
- // character combinations that suffer from this phenomenon on the
5179
- // various browsers.
5180
- var spanAffectsWrapping = /^$/; // Won't match any two-character string
5181
- if (gecko) spanAffectsWrapping = /$'/;
5182
- else if (safari && !/Version\/([6-9]|\d\d)\b/.test(navigator.userAgent)) spanAffectsWrapping = /\-[^ \-?]|\?[^ !'\"\),.\-\/:;\?\]\}]/;
5183
- else if (webkit) spanAffectsWrapping = /[~!#%&*)=+}\]|\"\.>,:;][({[<]|-[^\-?\.]|\?[\w~`@#$%\^&*(_=+{[|><]/;
5184
-
5185
- var knownScrollbarWidth;
5186
- function scrollbarWidth(measure) {
5187
- if (knownScrollbarWidth != null) return knownScrollbarWidth;
5188
- var test = elt("div", null, null, "width: 50px; height: 50px; overflow-x: scroll");
5189
- removeChildrenAndAdd(measure, test);
5190
- if (test.offsetWidth)
5191
- knownScrollbarWidth = test.offsetHeight - test.clientHeight;
5192
- return knownScrollbarWidth || 0;
5193
- }
5194
-
5195
- var zwspSupported;
5196
- function zeroWidthElement(measure) {
5197
- if (zwspSupported == null) {
5198
- var test = elt("span", "\u200b");
5199
- removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")]));
5200
- if (measure.firstChild.offsetHeight != 0)
5201
- zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !ie_lt8;
5202
- }
5203
- if (zwspSupported) return elt("span", "\u200b");
5204
- else return elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px");
5205
- }
5206
-
5207
- // See if "".split is the broken IE version, if so, provide an
5208
- // alternative way to split lines.
5209
- var splitLines = "\n\nb".split(/\n/).length != 3 ? function(string) {
5210
- var pos = 0, result = [], l = string.length;
5211
- while (pos <= l) {
5212
- var nl = string.indexOf("\n", pos);
5213
- if (nl == -1) nl = string.length;
5214
- var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl);
5215
- var rt = line.indexOf("\r");
5216
- if (rt != -1) {
5217
- result.push(line.slice(0, rt));
5218
- pos += rt + 1;
5219
- } else {
5220
- result.push(line);
5221
- pos = nl + 1;
5222
- }
5223
- }
5224
- return result;
5225
- } : function(string){return string.split(/\r\n?|\n/);};
5226
- CodeMirror.splitLines = splitLines;
5227
-
5228
- var hasSelection = window.getSelection ? function(te) {
5229
- try { return te.selectionStart != te.selectionEnd; }
5230
- catch(e) { return false; }
5231
- } : function(te) {
5232
- try {var range = te.ownerDocument.selection.createRange();}
5233
- catch(e) {}
5234
- if (!range || range.parentElement() != te) return false;
5235
- return range.compareEndPoints("StartToEnd", range) != 0;
5236
- };
5237
-
5238
- var hasCopyEvent = (function() {
5239
- var e = elt("div");
5240
- if ("oncopy" in e) return true;
5241
- e.setAttribute("oncopy", "return;");
5242
- return typeof e.oncopy == 'function';
5243
- })();
5244
-
5245
- // KEY NAMING
5246
-
5247
- var keyNames = {3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
5248
- 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
5249
- 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
5250
- 46: "Delete", 59: ";", 91: "Mod", 92: "Mod", 93: "Mod", 109: "-", 107: "=", 127: "Delete",
5251
- 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\",
5252
- 221: "]", 222: "'", 63276: "PageUp", 63277: "PageDown", 63275: "End", 63273: "Home",
5253
- 63234: "Left", 63232: "Up", 63235: "Right", 63233: "Down", 63302: "Insert", 63272: "Delete"};
5254
- CodeMirror.keyNames = keyNames;
5255
- (function() {
5256
- // Number keys
5257
- for (var i = 0; i < 10; i++) keyNames[i + 48] = String(i);
5258
- // Alphabetic keys
5259
- for (var i = 65; i <= 90; i++) keyNames[i] = String.fromCharCode(i);
5260
- // Function keys
5261
- for (var i = 1; i <= 12; i++) keyNames[i + 111] = keyNames[i + 63235] = "F" + i;
5262
- })();
5263
-
5264
- // BIDI HELPERS
5265
-
5266
- function iterateBidiSections(order, from, to, f) {
5267
- if (!order) return f(from, to, "ltr");
5268
- for (var i = 0; i < order.length; ++i) {
5269
- var part = order[i];
5270
- if (part.from < to && part.to > from || from == to && part.to == from)
5271
- f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr");
5272
- }
5273
- }
5274
-
5275
- function bidiLeft(part) { return part.level % 2 ? part.to : part.from; }
5276
- function bidiRight(part) { return part.level % 2 ? part.from : part.to; }
5277
-
5278
- function lineLeft(line) { var order = getOrder(line); return order ? bidiLeft(order[0]) : 0; }
5279
- function lineRight(line) {
5280
- var order = getOrder(line);
5281
- if (!order) return line.text.length;
5282
- return bidiRight(lst(order));
5283
- }
5284
-
5285
- function lineStart(cm, lineN) {
5286
- var line = getLine(cm.doc, lineN);
5287
- var visual = visualLine(cm.doc, line);
5288
- if (visual != line) lineN = lineNo(visual);
5289
- var order = getOrder(visual);
5290
- var ch = !order ? 0 : order[0].level % 2 ? lineRight(visual) : lineLeft(visual);
5291
- return Pos(lineN, ch);
5292
- }
5293
- function lineEnd(cm, lineN) {
5294
- var merged, line;
5295
- while (merged = collapsedSpanAtEnd(line = getLine(cm.doc, lineN)))
5296
- lineN = merged.find().to.line;
5297
- var order = getOrder(line);
5298
- var ch = !order ? line.text.length : order[0].level % 2 ? lineLeft(line) : lineRight(line);
5299
- return Pos(lineN, ch);
5300
- }
5301
-
5302
- // This is somewhat involved. It is needed in order to move
5303
- // 'visually' through bi-directional text -- i.e., pressing left
5304
- // should make the cursor go left, even when in RTL text. The
5305
- // tricky part is the 'jumps', where RTL and LTR text touch each
5306
- // other. This often requires the cursor offset to move more than
5307
- // one unit, in order to visually move one unit.
5308
- function moveVisually(line, start, dir, byUnit) {
5309
- var bidi = getOrder(line);
5310
- if (!bidi) return moveLogically(line, start, dir, byUnit);
5311
- var moveOneUnit = byUnit ? function(pos, dir) {
5312
- do pos += dir;
5313
- while (pos > 0 && isExtendingChar.test(line.text.charAt(pos)));
5314
- return pos;
5315
- } : function(pos, dir) { return pos + dir; };
5316
- var linedir = bidi[0].level;
5317
- for (var i = 0; i < bidi.length; ++i) {
5318
- var part = bidi[i], sticky = part.level % 2 == linedir;
5319
- if ((part.from < start && part.to > start) ||
5320
- (sticky && (part.from == start || part.to == start))) break;
5321
- }
5322
- var target = moveOneUnit(start, part.level % 2 ? -dir : dir);
5323
-
5324
- while (target != null) {
5325
- if (part.level % 2 == linedir) {
5326
- if (target < part.from || target > part.to) {
5327
- part = bidi[i += dir];
5328
- target = part && (dir > 0 == part.level % 2 ? moveOneUnit(part.to, -1) : moveOneUnit(part.from, 1));
5329
- } else break;
5330
- } else {
5331
- if (target == bidiLeft(part)) {
5332
- part = bidi[--i];
5333
- target = part && bidiRight(part);
5334
- } else if (target == bidiRight(part)) {
5335
- part = bidi[++i];
5336
- target = part && bidiLeft(part);
5337
- } else break;
5338
- }
5339
- }
5340
-
5341
- return target < 0 || target > line.text.length ? null : target;
5342
- }
5343
-
5344
- function moveLogically(line, start, dir, byUnit) {
5345
- var target = start + dir;
5346
- if (byUnit) while (target > 0 && isExtendingChar.test(line.text.charAt(target))) target += dir;
5347
- return target < 0 || target > line.text.length ? null : target;
5348
- }
5349
-
5350
- // Bidirectional ordering algorithm
5351
- // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm
5352
- // that this (partially) implements.
5353
-
5354
- // One-char codes used for character types:
5355
- // L (L): Left-to-Right
5356
- // R (R): Right-to-Left
5357
- // r (AL): Right-to-Left Arabic
5358
- // 1 (EN): European Number
5359
- // + (ES): European Number Separator
5360
- // % (ET): European Number Terminator
5361
- // n (AN): Arabic Number
5362
- // , (CS): Common Number Separator
5363
- // m (NSM): Non-Spacing Mark
5364
- // b (BN): Boundary Neutral
5365
- // s (B): Paragraph Separator
5366
- // t (S): Segment Separator
5367
- // w (WS): Whitespace
5368
- // N (ON): Other Neutrals
5369
-
5370
- // Returns null if characters are ordered as they appear
5371
- // (left-to-right), or an array of sections ({from, to, level}
5372
- // objects) in the order in which they occur visually.
5373
- var bidiOrdering = (function() {
5374
- // Character types for codepoints 0 to 0xff
5375
- var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLL";
5376
- // Character types for codepoints 0x600 to 0x6ff
5377
- var arabicTypes = "rrrrrrrrrrrr,rNNmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmrrrrrrrnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmNmmmmrrrrrrrrrrrrrrrrrr";
5378
- function charType(code) {
5379
- if (code <= 0xff) return lowTypes.charAt(code);
5380
- else if (0x590 <= code && code <= 0x5f4) return "R";
5381
- else if (0x600 <= code && code <= 0x6ff) return arabicTypes.charAt(code - 0x600);
5382
- else if (0x700 <= code && code <= 0x8ac) return "r";
5383
- else return "L";
5384
- }
5385
-
5386
- var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;
5387
- var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/;
5388
- // Browsers seem to always treat the boundaries of block elements as being L.
5389
- var outerType = "L";
5390
-
5391
- return function(str) {
5392
- if (!bidiRE.test(str)) return false;
5393
- var len = str.length, types = [];
5394
- for (var i = 0, type; i < len; ++i)
5395
- types.push(type = charType(str.charCodeAt(i)));
5396
-
5397
- // W1. Examine each non-spacing mark (NSM) in the level run, and
5398
- // change the type of the NSM to the type of the previous
5399
- // character. If the NSM is at the start of the level run, it will
5400
- // get the type of sor.
5401
- for (var i = 0, prev = outerType; i < len; ++i) {
5402
- var type = types[i];
5403
- if (type == "m") types[i] = prev;
5404
- else prev = type;
5405
- }
5406
-
5407
- // W2. Search backwards from each instance of a European number
5408
- // until the first strong type (R, L, AL, or sor) is found. If an
5409
- // AL is found, change the type of the European number to Arabic
5410
- // number.
5411
- // W3. Change all ALs to R.
5412
- for (var i = 0, cur = outerType; i < len; ++i) {
5413
- var type = types[i];
5414
- if (type == "1" && cur == "r") types[i] = "n";
5415
- else if (isStrong.test(type)) { cur = type; if (type == "r") types[i] = "R"; }
5416
- }
5417
-
5418
- // W4. A single European separator between two European numbers
5419
- // changes to a European number. A single common separator between
5420
- // two numbers of the same type changes to that type.
5421
- for (var i = 1, prev = types[0]; i < len - 1; ++i) {
5422
- var type = types[i];
5423
- if (type == "+" && prev == "1" && types[i+1] == "1") types[i] = "1";
5424
- else if (type == "," && prev == types[i+1] &&
5425
- (prev == "1" || prev == "n")) types[i] = prev;
5426
- prev = type;
5427
- }
5428
-
5429
- // W5. A sequence of European terminators adjacent to European
5430
- // numbers changes to all European numbers.
5431
- // W6. Otherwise, separators and terminators change to Other
5432
- // Neutral.
5433
- for (var i = 0; i < len; ++i) {
5434
- var type = types[i];
5435
- if (type == ",") types[i] = "N";
5436
- else if (type == "%") {
5437
- for (var end = i + 1; end < len && types[end] == "%"; ++end) {}
5438
- var replace = (i && types[i-1] == "!") || (end < len - 1 && types[end] == "1") ? "1" : "N";
5439
- for (var j = i; j < end; ++j) types[j] = replace;
5440
- i = end - 1;
5441
- }
5442
- }
5443
-
5444
- // W7. Search backwards from each instance of a European number
5445
- // until the first strong type (R, L, or sor) is found. If an L is
5446
- // found, then change the type of the European number to L.
5447
- for (var i = 0, cur = outerType; i < len; ++i) {
5448
- var type = types[i];
5449
- if (cur == "L" && type == "1") types[i] = "L";
5450
- else if (isStrong.test(type)) cur = type;
5451
- }
5452
-
5453
- // N1. A sequence of neutrals takes the direction of the
5454
- // surrounding strong text if the text on both sides has the same
5455
- // direction. European and Arabic numbers act as if they were R in
5456
- // terms of their influence on neutrals. Start-of-level-run (sor)
5457
- // and end-of-level-run (eor) are used at level run boundaries.
5458
- // N2. Any remaining neutrals take the embedding direction.
5459
- for (var i = 0; i < len; ++i) {
5460
- if (isNeutral.test(types[i])) {
5461
- for (var end = i + 1; end < len && isNeutral.test(types[end]); ++end) {}
5462
- var before = (i ? types[i-1] : outerType) == "L";
5463
- var after = (end < len - 1 ? types[end] : outerType) == "L";
5464
- var replace = before || after ? "L" : "R";
5465
- for (var j = i; j < end; ++j) types[j] = replace;
5466
- i = end - 1;
5467
- }
5468
- }
5469
-
5470
- // Here we depart from the documented algorithm, in order to avoid
5471
- // building up an actual levels array. Since there are only three
5472
- // levels (0, 1, 2) in an implementation that doesn't take
5473
- // explicit embedding into account, we can build up the order on
5474
- // the fly, without following the level-based algorithm.
5475
- var order = [], m;
5476
- for (var i = 0; i < len;) {
5477
- if (countsAsLeft.test(types[i])) {
5478
- var start = i;
5479
- for (++i; i < len && countsAsLeft.test(types[i]); ++i) {}
5480
- order.push({from: start, to: i, level: 0});
5481
- } else {
5482
- var pos = i, at = order.length;
5483
- for (++i; i < len && types[i] != "L"; ++i) {}
5484
- for (var j = pos; j < i;) {
5485
- if (countsAsNum.test(types[j])) {
5486
- if (pos < j) order.splice(at, 0, {from: pos, to: j, level: 1});
5487
- var nstart = j;
5488
- for (++j; j < i && countsAsNum.test(types[j]); ++j) {}
5489
- order.splice(at, 0, {from: nstart, to: j, level: 2});
5490
- pos = j;
5491
- } else ++j;
5492
- }
5493
- if (pos < i) order.splice(at, 0, {from: pos, to: i, level: 1});
5494
- }
5495
- }
5496
- if (order[0].level == 1 && (m = str.match(/^\s+/))) {
5497
- order[0].from = m[0].length;
5498
- order.unshift({from: 0, to: m[0].length, level: 0});
5499
- }
5500
- if (lst(order).level == 1 && (m = str.match(/\s+$/))) {
5501
- lst(order).to -= m[0].length;
5502
- order.push({from: len - m[0].length, to: len, level: 0});
5503
- }
5504
- if (order[0].level != lst(order).level)
5505
- order.push({from: len, to: len, level: order[0].level});
5506
-
5507
- return order;
5508
- };
5509
- })();
5510
-
5511
- // THE END
5512
-
5513
- CodeMirror.version = "3.11";
5514
-
5515
- return CodeMirror;
5516
- })();
1
+ // CodeMirror version 3.11
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
+ var captureMiddleClick = gecko || (ie && !ie_lt9);
36
+
37
+ // Optimize some code when these features are not used
38
+ var sawReadOnlySpans = false, sawCollapsedSpans = false;
39
+
40
+ // CONSTRUCTOR
41
+
42
+ function CodeMirror(place, options) {
43
+ if (!(this instanceof CodeMirror)) return new CodeMirror(place, options);
44
+
45
+ this.options = options = options || {};
46
+ // Determine effective options based on given values and defaults.
47
+ for (var opt in defaults) if (!options.hasOwnProperty(opt) && defaults.hasOwnProperty(opt))
48
+ options[opt] = defaults[opt];
49
+ setGuttersForLineNumbers(options);
50
+
51
+ var docStart = typeof options.value == "string" ? 0 : options.value.first;
52
+ var display = this.display = makeDisplay(place, docStart);
53
+ display.wrapper.CodeMirror = this;
54
+ updateGutters(this);
55
+ if (options.autofocus && !mobile) focusInput(this);
56
+
57
+ this.state = {keyMaps: [],
58
+ overlays: [],
59
+ modeGen: 0,
60
+ overwrite: false, focused: false,
61
+ suppressEdits: false, pasteIncoming: false,
62
+ draggingText: false,
63
+ highlight: new Delayed()};
64
+
65
+ themeChanged(this);
66
+ if (options.lineWrapping)
67
+ this.display.wrapper.className += " CodeMirror-wrap";
68
+
69
+ var doc = options.value;
70
+ if (typeof doc == "string") doc = new Doc(options.value, options.mode);
71
+ operation(this, attachDoc)(this, doc);
72
+
73
+ // Override magic textarea content restore that IE sometimes does
74
+ // on our hidden textarea on reload
75
+ if (ie) setTimeout(bind(resetInput, this, true), 20);
76
+
77
+ registerEventHandlers(this);
78
+ // IE throws unspecified error in certain cases, when
79
+ // trying to access activeElement before onload
80
+ var hasFocus; try { hasFocus = (document.activeElement == display.input); } catch(e) { }
81
+ if (hasFocus || (options.autofocus && !mobile)) setTimeout(bind(onFocus, this), 20);
82
+ else onBlur(this);
83
+
84
+ operation(this, function() {
85
+ for (var opt in optionHandlers)
86
+ if (optionHandlers.propertyIsEnumerable(opt))
87
+ optionHandlers[opt](this, options[opt], Init);
88
+ for (var i = 0; i < initHooks.length; ++i) initHooks[i](this);
89
+ })();
90
+ }
91
+
92
+ // DISPLAY CONSTRUCTOR
93
+
94
+ function makeDisplay(place, docStart) {
95
+ var d = {};
96
+
97
+ var input = d.input = elt("textarea", null, null, "position: absolute; padding: 0; width: 1px; height: 1em; outline: none;");
98
+ if (webkit) input.style.width = "1000px";
99
+ else input.setAttribute("wrap", "off");
100
+ // if border: 0; -- iOS fails to open keyboard (issue #1287)
101
+ if (ios) input.style.border = "1px solid black";
102
+ input.setAttribute("autocorrect", "off"); input.setAttribute("autocapitalize", "off");
103
+
104
+ // Wraps and hides input textarea
105
+ d.inputDiv = elt("div", [input], null, "overflow: hidden; position: relative; width: 3px; height: 0px;");
106
+ // The actual fake scrollbars.
107
+ d.scrollbarH = elt("div", [elt("div", null, null, "height: 1px")], "CodeMirror-hscrollbar");
108
+ d.scrollbarV = elt("div", [elt("div", null, null, "width: 1px")], "CodeMirror-vscrollbar");
109
+ d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler");
110
+ // DIVs containing the selection and the actual code
111
+ d.lineDiv = elt("div");
112
+ d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1");
113
+ // Blinky cursor, and element used to ensure cursor fits at the end of a line
114
+ d.cursor = elt("div", "\u00a0", "CodeMirror-cursor");
115
+ // Secondary cursor, shown when on a 'jump' in bi-directional text
116
+ d.otherCursor = elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor");
117
+ // Used to measure text size
118
+ d.measure = elt("div", null, "CodeMirror-measure");
119
+ // Wraps everything that needs to exist inside the vertically-padded coordinate system
120
+ d.lineSpace = elt("div", [d.measure, d.selectionDiv, d.lineDiv, d.cursor, d.otherCursor],
121
+ null, "position: relative; outline: none");
122
+ // Moved around its parent to cover visible view
123
+ d.mover = elt("div", [elt("div", [d.lineSpace], "CodeMirror-lines")], null, "position: relative");
124
+ // Set to the height of the text, causes scrolling
125
+ d.sizer = elt("div", [d.mover], "CodeMirror-sizer");
126
+ // D is needed because behavior of elts with overflow: auto and padding is inconsistent across browsers
127
+ d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerCutOff + "px; width: 1px;");
128
+ // Will contain the gutters, if any
129
+ d.gutters = elt("div", null, "CodeMirror-gutters");
130
+ d.lineGutter = null;
131
+ // Helper element to properly size the gutter backgrounds
132
+ var scrollerInner = elt("div", [d.sizer, d.heightForcer, d.gutters], null, "position: relative; min-height: 100%");
133
+ // Provides scrolling
134
+ d.scroller = elt("div", [scrollerInner], "CodeMirror-scroll");
135
+ d.scroller.setAttribute("tabIndex", "-1");
136
+ // The element in which the editor lives.
137
+ d.wrapper = elt("div", [d.inputDiv, d.scrollbarH, d.scrollbarV,
138
+ d.scrollbarFiller, d.scroller], "CodeMirror");
139
+ // Work around IE7 z-index bug
140
+ if (ie_lt8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; }
141
+ if (place.appendChild) place.appendChild(d.wrapper); else place(d.wrapper);
142
+
143
+ // Needed to hide big blue blinking cursor on Mobile Safari
144
+ if (ios) input.style.width = "0px";
145
+ if (!webkit) d.scroller.draggable = true;
146
+ // Needed to handle Tab key in KHTML
147
+ if (khtml) { d.inputDiv.style.height = "1px"; d.inputDiv.style.position = "absolute"; }
148
+ // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
149
+ else if (ie_lt8) d.scrollbarH.style.minWidth = d.scrollbarV.style.minWidth = "18px";
150
+
151
+ // Current visible range (may be bigger than the view window).
152
+ d.viewOffset = d.lastSizeC = 0;
153
+ d.showingFrom = d.showingTo = docStart;
154
+
155
+ // Used to only resize the line number gutter when necessary (when
156
+ // the amount of lines crosses a boundary that makes its width change)
157
+ d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null;
158
+ // See readInput and resetInput
159
+ d.prevInput = "";
160
+ // Set to true when a non-horizontal-scrolling widget is added. As
161
+ // an optimization, widget aligning is skipped when d is false.
162
+ d.alignWidgets = false;
163
+ // Flag that indicates whether we currently expect input to appear
164
+ // (after some event like 'keypress' or 'input') and are polling
165
+ // intensively.
166
+ d.pollingFast = false;
167
+ // Self-resetting timeout for the poller
168
+ d.poll = new Delayed();
169
+ // True when a drag from the editor is active
170
+ d.draggingText = false;
171
+
172
+ d.cachedCharWidth = d.cachedTextHeight = null;
173
+ d.measureLineCache = [];
174
+ d.measureLineCachePos = 0;
175
+
176
+ // Tracks when resetInput has punted to just putting a short
177
+ // string instead of the (large) selection.
178
+ d.inaccurateSelection = false;
179
+
180
+ // Tracks the maximum line length so that the horizontal scrollbar
181
+ // can be kept static when scrolling.
182
+ d.maxLine = null;
183
+ d.maxLineLength = 0;
184
+ d.maxLineChanged = false;
185
+
186
+ // Used for measuring wheel scrolling granularity
187
+ d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null;
188
+
189
+ return d;
190
+ }
191
+
192
+ // STATE UPDATES
193
+
194
+ // Used to get the editor into a consistent state again when options change.
195
+
196
+ function loadMode(cm) {
197
+ cm.doc.mode = CodeMirror.getMode(cm.options, cm.doc.modeOption);
198
+ cm.doc.iter(function(line) {
199
+ if (line.stateAfter) line.stateAfter = null;
200
+ if (line.styles) line.styles = null;
201
+ });
202
+ cm.doc.frontier = cm.doc.first;
203
+ startWorker(cm, 100);
204
+ cm.state.modeGen++;
205
+ if (cm.curOp) regChange(cm);
206
+ }
207
+
208
+ function wrappingChanged(cm) {
209
+ if (cm.options.lineWrapping) {
210
+ cm.display.wrapper.className += " CodeMirror-wrap";
211
+ cm.display.sizer.style.minWidth = "";
212
+ } else {
213
+ cm.display.wrapper.className = cm.display.wrapper.className.replace(" CodeMirror-wrap", "");
214
+ computeMaxLength(cm);
215
+ }
216
+ estimateLineHeights(cm);
217
+ regChange(cm);
218
+ clearCaches(cm);
219
+ setTimeout(function(){updateScrollbars(cm.display, cm.doc.height);}, 100);
220
+ }
221
+
222
+ function estimateHeight(cm) {
223
+ var th = textHeight(cm.display), wrapping = cm.options.lineWrapping;
224
+ var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3);
225
+ return function(line) {
226
+ if (lineIsHidden(cm.doc, line))
227
+ return 0;
228
+ else if (wrapping)
229
+ return (Math.ceil(line.text.length / perLine) || 1) * th;
230
+ else
231
+ return th;
232
+ };
233
+ }
234
+
235
+ function estimateLineHeights(cm) {
236
+ var doc = cm.doc, est = estimateHeight(cm);
237
+ doc.iter(function(line) {
238
+ var estHeight = est(line);
239
+ if (estHeight != line.height) updateLineHeight(line, estHeight);
240
+ });
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
+ regChange(cm);
258
+ }
259
+
260
+ function updateGutters(cm) {
261
+ var gutters = cm.display.gutters, specs = cm.options.gutters;
262
+ removeChildren(gutters);
263
+ for (var i = 0; i < specs.length; ++i) {
264
+ var gutterClass = specs[i];
265
+ var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + gutterClass));
266
+ if (gutterClass == "CodeMirror-linenumbers") {
267
+ cm.display.lineGutter = gElt;
268
+ gElt.style.width = (cm.display.lineNumWidth || 1) + "px";
269
+ }
270
+ }
271
+ gutters.style.display = i ? "" : "none";
272
+ }
273
+
274
+ function lineLength(doc, line) {
275
+ if (line.height == 0) return 0;
276
+ var len = line.text.length, merged, cur = line;
277
+ while (merged = collapsedSpanAtStart(cur)) {
278
+ var found = merged.find();
279
+ cur = getLine(doc, found.from.line);
280
+ len += found.from.ch - found.to.ch;
281
+ }
282
+ cur = line;
283
+ while (merged = collapsedSpanAtEnd(cur)) {
284
+ var found = merged.find();
285
+ len -= cur.text.length - found.from.ch;
286
+ cur = getLine(doc, found.to.line);
287
+ len += cur.text.length - found.to.ch;
288
+ }
289
+ return len;
290
+ }
291
+
292
+ function computeMaxLength(cm) {
293
+ var d = cm.display, doc = cm.doc;
294
+ d.maxLine = getLine(doc, doc.first);
295
+ d.maxLineLength = lineLength(doc, d.maxLine);
296
+ d.maxLineChanged = true;
297
+ doc.iter(function(line) {
298
+ var len = lineLength(doc, line);
299
+ if (len > d.maxLineLength) {
300
+ d.maxLineLength = len;
301
+ d.maxLine = line;
302
+ }
303
+ });
304
+ }
305
+
306
+ // Make sure the gutters options contains the element
307
+ // "CodeMirror-linenumbers" when the lineNumbers option is true.
308
+ function setGuttersForLineNumbers(options) {
309
+ var found = false;
310
+ for (var i = 0; i < options.gutters.length; ++i) {
311
+ if (options.gutters[i] == "CodeMirror-linenumbers") {
312
+ if (options.lineNumbers) found = true;
313
+ else options.gutters.splice(i--, 1);
314
+ }
315
+ }
316
+ if (!found && options.lineNumbers)
317
+ options.gutters.push("CodeMirror-linenumbers");
318
+ }
319
+
320
+ // SCROLLBARS
321
+
322
+ // Re-synchronize the fake scrollbars with the actual size of the
323
+ // content. Optionally force a scrollTop.
324
+ function updateScrollbars(d /* display */, docHeight) {
325
+ var totalHeight = docHeight + paddingVert(d);
326
+ d.sizer.style.minHeight = d.heightForcer.style.top = totalHeight + "px";
327
+ var scrollHeight = Math.max(totalHeight, d.scroller.scrollHeight);
328
+ var needsH = d.scroller.scrollWidth > d.scroller.clientWidth;
329
+ var needsV = scrollHeight > d.scroller.clientHeight;
330
+ if (needsV) {
331
+ d.scrollbarV.style.display = "block";
332
+ d.scrollbarV.style.bottom = needsH ? scrollbarWidth(d.measure) + "px" : "0";
333
+ d.scrollbarV.firstChild.style.height =
334
+ (scrollHeight - d.scroller.clientHeight + d.scrollbarV.clientHeight) + "px";
335
+ } else d.scrollbarV.style.display = "";
336
+ if (needsH) {
337
+ d.scrollbarH.style.display = "block";
338
+ d.scrollbarH.style.right = needsV ? scrollbarWidth(d.measure) + "px" : "0";
339
+ d.scrollbarH.firstChild.style.width =
340
+ (d.scroller.scrollWidth - d.scroller.clientWidth + d.scrollbarH.clientWidth) + "px";
341
+ } else d.scrollbarH.style.display = "";
342
+ if (needsH && needsV) {
343
+ d.scrollbarFiller.style.display = "block";
344
+ d.scrollbarFiller.style.height = d.scrollbarFiller.style.width = scrollbarWidth(d.measure) + "px";
345
+ } else d.scrollbarFiller.style.display = "";
346
+
347
+ if (mac_geLion && scrollbarWidth(d.measure) === 0)
348
+ d.scrollbarV.style.minWidth = d.scrollbarH.style.minHeight = mac_geMountainLion ? "18px" : "12px";
349
+ }
350
+
351
+ function visibleLines(display, doc, viewPort) {
352
+ var top = display.scroller.scrollTop, height = display.wrapper.clientHeight;
353
+ if (typeof viewPort == "number") top = viewPort;
354
+ else if (viewPort) {top = viewPort.top; height = viewPort.bottom - viewPort.top;}
355
+ top = Math.floor(top - paddingTop(display));
356
+ var bottom = Math.ceil(top + height);
357
+ return {from: lineAtHeight(doc, top), to: lineAtHeight(doc, bottom)};
358
+ }
359
+
360
+ // LINE NUMBERS
361
+
362
+ function alignHorizontally(cm) {
363
+ var display = cm.display;
364
+ if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) return;
365
+ var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft;
366
+ var gutterW = display.gutters.offsetWidth, l = comp + "px";
367
+ for (var n = display.lineDiv.firstChild; n; n = n.nextSibling) if (n.alignable) {
368
+ for (var i = 0, a = n.alignable; i < a.length; ++i) a[i].style.left = l;
369
+ }
370
+ if (cm.options.fixedGutter)
371
+ display.gutters.style.left = (comp + gutterW) + "px";
372
+ }
373
+
374
+ function maybeUpdateLineNumberWidth(cm) {
375
+ if (!cm.options.lineNumbers) return false;
376
+ var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display;
377
+ if (last.length != display.lineNumChars) {
378
+ var test = display.measure.appendChild(elt("div", [elt("div", last)],
379
+ "CodeMirror-linenumber CodeMirror-gutter-elt"));
380
+ var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW;
381
+ display.lineGutter.style.width = "";
382
+ display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding);
383
+ display.lineNumWidth = display.lineNumInnerWidth + padding;
384
+ display.lineNumChars = display.lineNumInnerWidth ? last.length : -1;
385
+ display.lineGutter.style.width = display.lineNumWidth + "px";
386
+ return true;
387
+ }
388
+ return false;
389
+ }
390
+
391
+ function lineNumberFor(options, i) {
392
+ return String(options.lineNumberFormatter(i + options.firstLineNumber));
393
+ }
394
+ function compensateForHScroll(display) {
395
+ return getRect(display.scroller).left - getRect(display.sizer).left;
396
+ }
397
+
398
+ // DISPLAY DRAWING
399
+
400
+ function updateDisplay(cm, changes, viewPort) {
401
+ var oldFrom = cm.display.showingFrom, oldTo = cm.display.showingTo;
402
+ var updated = updateDisplayInner(cm, changes, viewPort);
403
+ if (updated) {
404
+ signalLater(cm, "update", cm);
405
+ if (cm.display.showingFrom != oldFrom || cm.display.showingTo != oldTo)
406
+ signalLater(cm, "viewportChange", cm, cm.display.showingFrom, cm.display.showingTo);
407
+ }
408
+ updateSelection(cm);
409
+ updateScrollbars(cm.display, cm.doc.height);
410
+
411
+ return updated;
412
+ }
413
+
414
+ // Uses a set of changes plus the current scroll position to
415
+ // determine which DOM updates have to be made, and makes the
416
+ // updates.
417
+ function updateDisplayInner(cm, changes, viewPort) {
418
+ var display = cm.display, doc = cm.doc;
419
+ if (!display.wrapper.clientWidth) {
420
+ display.showingFrom = display.showingTo = doc.first;
421
+ display.viewOffset = 0;
422
+ return;
423
+ }
424
+
425
+ // Compute the new visible window
426
+ // If scrollTop is specified, use that to determine which lines
427
+ // to render instead of the current scrollbar position.
428
+ var visible = visibleLines(display, doc, viewPort);
429
+ // Bail out if the visible area is already rendered and nothing changed.
430
+ if (changes.length == 0 &&
431
+ visible.from > display.showingFrom && visible.to < display.showingTo)
432
+ return;
433
+
434
+ if (maybeUpdateLineNumberWidth(cm))
435
+ changes = [{from: doc.first, to: doc.first + doc.size}];
436
+ var gutterW = display.sizer.style.marginLeft = display.gutters.offsetWidth + "px";
437
+ display.scrollbarH.style.left = cm.options.fixedGutter ? gutterW : "0";
438
+
439
+ // Used to determine which lines need their line numbers updated
440
+ var positionsChangedFrom = Infinity;
441
+ if (cm.options.lineNumbers)
442
+ for (var i = 0; i < changes.length; ++i)
443
+ if (changes[i].diff) { positionsChangedFrom = changes[i].from; break; }
444
+
445
+ var end = doc.first + doc.size;
446
+ var from = Math.max(visible.from - cm.options.viewportMargin, doc.first);
447
+ var to = Math.min(end, visible.to + cm.options.viewportMargin);
448
+ if (display.showingFrom < from && from - display.showingFrom < 20) from = Math.max(doc.first, display.showingFrom);
449
+ if (display.showingTo > to && display.showingTo - to < 20) to = Math.min(end, display.showingTo);
450
+ if (sawCollapsedSpans) {
451
+ from = lineNo(visualLine(doc, getLine(doc, from)));
452
+ while (to < end && lineIsHidden(doc, getLine(doc, to))) ++to;
453
+ }
454
+
455
+ // Create a range of theoretically intact lines, and punch holes
456
+ // in that using the change info.
457
+ var intact = [{from: Math.max(display.showingFrom, doc.first),
458
+ to: Math.min(display.showingTo, end)}];
459
+ if (intact[0].from >= intact[0].to) intact = [];
460
+ else intact = computeIntact(intact, changes);
461
+ // When merged lines are present, we might have to reduce the
462
+ // intact ranges because changes in continued fragments of the
463
+ // intact lines do require the lines to be redrawn.
464
+ if (sawCollapsedSpans)
465
+ for (var i = 0; i < intact.length; ++i) {
466
+ var range = intact[i], merged;
467
+ while (merged = collapsedSpanAtEnd(getLine(doc, range.to - 1))) {
468
+ var newTo = merged.find().from.line;
469
+ if (newTo > range.from) range.to = newTo;
470
+ else { intact.splice(i--, 1); break; }
471
+ }
472
+ }
473
+
474
+ // Clip off the parts that won't be visible
475
+ var intactLines = 0;
476
+ for (var i = 0; i < intact.length; ++i) {
477
+ var range = intact[i];
478
+ if (range.from < from) range.from = from;
479
+ if (range.to > to) range.to = to;
480
+ if (range.from >= range.to) intact.splice(i--, 1);
481
+ else intactLines += range.to - range.from;
482
+ }
483
+ if (intactLines == to - from && from == display.showingFrom && to == display.showingTo) {
484
+ updateViewOffset(cm);
485
+ return;
486
+ }
487
+ intact.sort(function(a, b) {return a.from - b.from;});
488
+
489
+ var focused = document.activeElement;
490
+ if (intactLines < (to - from) * .7) display.lineDiv.style.display = "none";
491
+ patchDisplay(cm, from, to, intact, positionsChangedFrom);
492
+ display.lineDiv.style.display = "";
493
+ if (document.activeElement != focused && focused.offsetHeight) focused.focus();
494
+
495
+ var different = from != display.showingFrom || to != display.showingTo ||
496
+ display.lastSizeC != display.wrapper.clientHeight;
497
+ // This is just a bogus formula that detects when the editor is
498
+ // resized or the font size changes.
499
+ if (different) display.lastSizeC = display.wrapper.clientHeight;
500
+ display.showingFrom = from; display.showingTo = to;
501
+ startWorker(cm, 100);
502
+
503
+ var prevBottom = display.lineDiv.offsetTop;
504
+ for (var node = display.lineDiv.firstChild, height; node; node = node.nextSibling) if (node.lineObj) {
505
+ if (ie_lt8) {
506
+ var bot = node.offsetTop + node.offsetHeight;
507
+ height = bot - prevBottom;
508
+ prevBottom = bot;
509
+ } else {
510
+ var box = getRect(node);
511
+ height = box.bottom - box.top;
512
+ }
513
+ var diff = node.lineObj.height - height;
514
+ if (height < 2) height = textHeight(display);
515
+ if (diff > .001 || diff < -.001) {
516
+ updateLineHeight(node.lineObj, height);
517
+ var widgets = node.lineObj.widgets;
518
+ if (widgets) for (var i = 0; i < widgets.length; ++i)
519
+ widgets[i].height = widgets[i].node.offsetHeight;
520
+ }
521
+ }
522
+ updateViewOffset(cm);
523
+
524
+ if (visibleLines(display, doc, viewPort).to > to)
525
+ updateDisplayInner(cm, [], viewPort);
526
+ return true;
527
+ }
528
+
529
+ function updateViewOffset(cm) {
530
+ var off = cm.display.viewOffset = heightAtLine(cm, getLine(cm.doc, cm.display.showingFrom));
531
+ // Position the mover div to align with the current virtual scroll position
532
+ cm.display.mover.style.top = off + "px";
533
+ }
534
+
535
+ function computeIntact(intact, changes) {
536
+ for (var i = 0, l = changes.length || 0; i < l; ++i) {
537
+ var change = changes[i], intact2 = [], diff = change.diff || 0;
538
+ for (var j = 0, l2 = intact.length; j < l2; ++j) {
539
+ var range = intact[j];
540
+ if (change.to <= range.from && change.diff) {
541
+ intact2.push({from: range.from + diff, to: range.to + diff});
542
+ } else if (change.to <= range.from || change.from >= range.to) {
543
+ intact2.push(range);
544
+ } else {
545
+ if (change.from > range.from)
546
+ intact2.push({from: range.from, to: change.from});
547
+ if (change.to < range.to)
548
+ intact2.push({from: change.to + diff, to: range.to + diff});
549
+ }
550
+ }
551
+ intact = intact2;
552
+ }
553
+ return intact;
554
+ }
555
+
556
+ function getDimensions(cm) {
557
+ var d = cm.display, left = {}, width = {};
558
+ for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) {
559
+ left[cm.options.gutters[i]] = n.offsetLeft;
560
+ width[cm.options.gutters[i]] = n.offsetWidth;
561
+ }
562
+ return {fixedPos: compensateForHScroll(d),
563
+ gutterTotalWidth: d.gutters.offsetWidth,
564
+ gutterLeft: left,
565
+ gutterWidth: width,
566
+ wrapperWidth: d.wrapper.clientWidth};
567
+ }
568
+
569
+ function patchDisplay(cm, from, to, intact, updateNumbersFrom) {
570
+ var dims = getDimensions(cm);
571
+ var display = cm.display, lineNumbers = cm.options.lineNumbers;
572
+ if (!intact.length && (!webkit || !cm.display.currentWheelTarget))
573
+ removeChildren(display.lineDiv);
574
+ var container = display.lineDiv, cur = container.firstChild;
575
+
576
+ function rm(node) {
577
+ var next = node.nextSibling;
578
+ if (webkit && mac && cm.display.currentWheelTarget == node) {
579
+ node.style.display = "none";
580
+ node.lineObj = null;
581
+ } else {
582
+ node.parentNode.removeChild(node);
583
+ }
584
+ return next;
585
+ }
586
+
587
+ var nextIntact = intact.shift(), lineN = from;
588
+ cm.doc.iter(from, to, function(line) {
589
+ if (nextIntact && nextIntact.to == lineN) nextIntact = intact.shift();
590
+ if (lineIsHidden(cm.doc, line)) {
591
+ if (line.height != 0) updateLineHeight(line, 0);
592
+ if (line.widgets && cur.previousSibling) for (var i = 0; i < line.widgets.length; ++i)
593
+ if (line.widgets[i].showIfHidden) {
594
+ var prev = cur.previousSibling;
595
+ if (/pre/i.test(prev.nodeName)) {
596
+ var wrap = elt("div", null, null, "position: relative");
597
+ prev.parentNode.replaceChild(wrap, prev);
598
+ wrap.appendChild(prev);
599
+ prev = wrap;
600
+ }
601
+ var wnode = prev.appendChild(elt("div", [line.widgets[i].node], "CodeMirror-linewidget"));
602
+ positionLineWidget(line.widgets[i], wnode, prev, dims);
603
+ }
604
+ } else if (nextIntact && nextIntact.from <= lineN && nextIntact.to > lineN) {
605
+ // This line is intact. Skip to the actual node. Update its
606
+ // line number if needed.
607
+ while (cur.lineObj != line) cur = rm(cur);
608
+ if (lineNumbers && updateNumbersFrom <= lineN && cur.lineNumber)
609
+ setTextContent(cur.lineNumber, lineNumberFor(cm.options, lineN));
610
+ cur = cur.nextSibling;
611
+ } else {
612
+ // For lines with widgets, make an attempt to find and reuse
613
+ // the existing element, so that widgets aren't needlessly
614
+ // removed and re-inserted into the dom
615
+ if (line.widgets) for (var j = 0, search = cur, reuse; search && j < 20; ++j, search = search.nextSibling)
616
+ if (search.lineObj == line && /div/i.test(search.nodeName)) { reuse = search; break; }
617
+ // This line needs to be generated.
618
+ var lineNode = buildLineElement(cm, line, lineN, dims, reuse);
619
+ if (lineNode != reuse) {
620
+ container.insertBefore(lineNode, cur);
621
+ } else {
622
+ while (cur != reuse) cur = rm(cur);
623
+ cur = cur.nextSibling;
624
+ }
625
+
626
+ lineNode.lineObj = line;
627
+ }
628
+ ++lineN;
629
+ });
630
+ while (cur) cur = rm(cur);
631
+ }
632
+
633
+ function buildLineElement(cm, line, lineNo, dims, reuse) {
634
+ var lineElement = lineContent(cm, line);
635
+ var markers = line.gutterMarkers, display = cm.display, wrap;
636
+
637
+ if (!cm.options.lineNumbers && !markers && !line.bgClass && !line.wrapClass && !line.widgets)
638
+ return lineElement;
639
+
640
+ // Lines with gutter elements, widgets or a background class need
641
+ // to be wrapped again, and have the extra elements added to the
642
+ // wrapper div
643
+
644
+ if (reuse) {
645
+ reuse.alignable = null;
646
+ var isOk = true, widgetsSeen = 0;
647
+ for (var n = reuse.firstChild, next; n; n = next) {
648
+ next = n.nextSibling;
649
+ if (!/\bCodeMirror-linewidget\b/.test(n.className)) {
650
+ reuse.removeChild(n);
651
+ } else {
652
+ for (var i = 0, first = true; i < line.widgets.length; ++i) {
653
+ var widget = line.widgets[i], isFirst = false;
654
+ if (!widget.above) { isFirst = first; first = false; }
655
+ if (widget.node == n.firstChild) {
656
+ positionLineWidget(widget, n, reuse, dims);
657
+ ++widgetsSeen;
658
+ if (isFirst) reuse.insertBefore(lineElement, n);
659
+ break;
660
+ }
661
+ }
662
+ if (i == line.widgets.length) { isOk = false; break; }
663
+ }
664
+ }
665
+ if (isOk && widgetsSeen == line.widgets.length) {
666
+ wrap = reuse;
667
+ reuse.className = line.wrapClass || "";
668
+ }
669
+ }
670
+ if (!wrap) {
671
+ wrap = elt("div", null, line.wrapClass, "position: relative");
672
+ wrap.appendChild(lineElement);
673
+ }
674
+ // Kludge to make sure the styled element lies behind the selection (by z-index)
675
+ if (line.bgClass)
676
+ wrap.insertBefore(elt("div", null, line.bgClass + " CodeMirror-linebackground"), wrap.firstChild);
677
+ if (cm.options.lineNumbers || markers) {
678
+ var gutterWrap = wrap.insertBefore(elt("div", null, null, "position: absolute; left: " +
679
+ (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px"),
680
+ wrap.firstChild);
681
+ if (cm.options.fixedGutter) (wrap.alignable || (wrap.alignable = [])).push(gutterWrap);
682
+ if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"]))
683
+ wrap.lineNumber = gutterWrap.appendChild(
684
+ elt("div", lineNumberFor(cm.options, lineNo),
685
+ "CodeMirror-linenumber CodeMirror-gutter-elt",
686
+ "left: " + dims.gutterLeft["CodeMirror-linenumbers"] + "px; width: "
687
+ + display.lineNumInnerWidth + "px"));
688
+ if (markers)
689
+ for (var k = 0; k < cm.options.gutters.length; ++k) {
690
+ var id = cm.options.gutters[k], found = markers.hasOwnProperty(id) && markers[id];
691
+ if (found)
692
+ gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt", "left: " +
693
+ dims.gutterLeft[id] + "px; width: " + dims.gutterWidth[id] + "px"));
694
+ }
695
+ }
696
+ if (ie_lt8) wrap.style.zIndex = 2;
697
+ if (line.widgets && wrap != reuse) for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
698
+ var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget");
699
+ positionLineWidget(widget, node, wrap, dims);
700
+ if (widget.above)
701
+ wrap.insertBefore(node, cm.options.lineNumbers && line.height != 0 ? gutterWrap : lineElement);
702
+ else
703
+ wrap.appendChild(node);
704
+ signalLater(widget, "redraw");
705
+ }
706
+ return wrap;
707
+ }
708
+
709
+ function positionLineWidget(widget, node, wrap, dims) {
710
+ if (widget.noHScroll) {
711
+ (wrap.alignable || (wrap.alignable = [])).push(node);
712
+ var width = dims.wrapperWidth;
713
+ node.style.left = dims.fixedPos + "px";
714
+ if (!widget.coverGutter) {
715
+ width -= dims.gutterTotalWidth;
716
+ node.style.paddingLeft = dims.gutterTotalWidth + "px";
717
+ }
718
+ node.style.width = width + "px";
719
+ }
720
+ if (widget.coverGutter) {
721
+ node.style.zIndex = 5;
722
+ node.style.position = "relative";
723
+ if (!widget.noHScroll) node.style.marginLeft = -dims.gutterTotalWidth + "px";
724
+ }
725
+ }
726
+
727
+ // SELECTION / CURSOR
728
+
729
+ function updateSelection(cm) {
730
+ var display = cm.display;
731
+ var collapsed = posEq(cm.doc.sel.from, cm.doc.sel.to);
732
+ if (collapsed || cm.options.showCursorWhenSelecting)
733
+ updateSelectionCursor(cm);
734
+ else
735
+ display.cursor.style.display = display.otherCursor.style.display = "none";
736
+ if (!collapsed)
737
+ updateSelectionRange(cm);
738
+ else
739
+ display.selectionDiv.style.display = "none";
740
+
741
+ // Move the hidden textarea near the cursor to prevent scrolling artifacts
742
+ var headPos = cursorCoords(cm, cm.doc.sel.head, "div");
743
+ var wrapOff = getRect(display.wrapper), lineOff = getRect(display.lineDiv);
744
+ display.inputDiv.style.top = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
745
+ headPos.top + lineOff.top - wrapOff.top)) + "px";
746
+ display.inputDiv.style.left = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
747
+ headPos.left + lineOff.left - wrapOff.left)) + "px";
748
+ }
749
+
750
+ // No selection, plain cursor
751
+ function updateSelectionCursor(cm) {
752
+ var display = cm.display, pos = cursorCoords(cm, cm.doc.sel.head, "div");
753
+ display.cursor.style.left = pos.left + "px";
754
+ display.cursor.style.top = pos.top + "px";
755
+ display.cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px";
756
+ display.cursor.style.display = "";
757
+
758
+ if (pos.other) {
759
+ display.otherCursor.style.display = "";
760
+ display.otherCursor.style.left = pos.other.left + "px";
761
+ display.otherCursor.style.top = pos.other.top + "px";
762
+ display.otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px";
763
+ } else { display.otherCursor.style.display = "none"; }
764
+ }
765
+
766
+ // Highlight selection
767
+ function updateSelectionRange(cm) {
768
+ var display = cm.display, doc = cm.doc, sel = cm.doc.sel;
769
+ var fragment = document.createDocumentFragment();
770
+ var clientWidth = display.lineSpace.offsetWidth, pl = paddingLeft(cm.display);
771
+
772
+ function add(left, top, width, bottom) {
773
+ if (top < 0) top = 0;
774
+ fragment.appendChild(elt("div", null, "CodeMirror-selected", "position: absolute; left: " + left +
775
+ "px; top: " + top + "px; width: " + (width == null ? clientWidth - left : width) +
776
+ "px; height: " + (bottom - top) + "px"));
777
+ }
778
+
779
+ function drawForLine(line, fromArg, toArg, retTop) {
780
+ var lineObj = getLine(doc, line);
781
+ var lineLen = lineObj.text.length, rVal = retTop ? Infinity : -Infinity;
782
+ function coords(ch) {
783
+ return charCoords(cm, Pos(line, ch), "div", lineObj);
784
+ }
785
+
786
+ iterateBidiSections(getOrder(lineObj), fromArg || 0, toArg == null ? lineLen : toArg, function(from, to, dir) {
787
+ var leftPos = coords(dir == "rtl" ? to - 1 : from);
788
+ var rightPos = coords(dir == "rtl" ? from : to - 1);
789
+ var left = leftPos.left, right = rightPos.right;
790
+ if (rightPos.top - leftPos.top > 3) { // Different lines, draw top part
791
+ add(left, leftPos.top, null, leftPos.bottom);
792
+ left = pl;
793
+ if (leftPos.bottom < rightPos.top) add(left, leftPos.bottom, null, rightPos.top);
794
+ }
795
+ if (toArg == null && to == lineLen) right = clientWidth;
796
+ if (fromArg == null && from == 0) left = pl;
797
+ rVal = retTop ? Math.min(rightPos.top, rVal) : Math.max(rightPos.bottom, rVal);
798
+ if (left < pl + 1) left = pl;
799
+ add(left, rightPos.top, right - left, rightPos.bottom);
800
+ });
801
+ return rVal;
802
+ }
803
+
804
+ if (sel.from.line == sel.to.line) {
805
+ drawForLine(sel.from.line, sel.from.ch, sel.to.ch);
806
+ } else {
807
+ var fromObj = getLine(doc, sel.from.line);
808
+ var cur = fromObj, merged, path = [sel.from.line, sel.from.ch], singleLine;
809
+ while (merged = collapsedSpanAtEnd(cur)) {
810
+ var found = merged.find();
811
+ path.push(found.from.ch, found.to.line, found.to.ch);
812
+ if (found.to.line == sel.to.line) {
813
+ path.push(sel.to.ch);
814
+ singleLine = true;
815
+ break;
816
+ }
817
+ cur = getLine(doc, found.to.line);
818
+ }
819
+
820
+ // This is a single, merged line
821
+ if (singleLine) {
822
+ for (var i = 0; i < path.length; i += 3)
823
+ drawForLine(path[i], path[i+1], path[i+2]);
824
+ } else {
825
+ var middleTop, middleBot, toObj = getLine(doc, sel.to.line);
826
+ if (sel.from.ch)
827
+ // Draw the first line of selection.
828
+ middleTop = drawForLine(sel.from.line, sel.from.ch, null, false);
829
+ else
830
+ // Simply include it in the middle block.
831
+ middleTop = heightAtLine(cm, fromObj) - display.viewOffset;
832
+
833
+ if (!sel.to.ch)
834
+ middleBot = heightAtLine(cm, toObj) - display.viewOffset;
835
+ else
836
+ middleBot = drawForLine(sel.to.line, collapsedSpanAtStart(toObj) ? null : 0, sel.to.ch, true);
837
+
838
+ if (middleTop < middleBot) add(pl, middleTop, null, middleBot);
839
+ }
840
+ }
841
+
842
+ removeChildrenAndAdd(display.selectionDiv, fragment);
843
+ display.selectionDiv.style.display = "";
844
+ }
845
+
846
+ // Cursor-blinking
847
+ function restartBlink(cm) {
848
+ var display = cm.display;
849
+ clearInterval(display.blinker);
850
+ var on = true;
851
+ display.cursor.style.visibility = display.otherCursor.style.visibility = "";
852
+ display.blinker = setInterval(function() {
853
+ if (!display.cursor.offsetHeight) return;
854
+ display.cursor.style.visibility = display.otherCursor.style.visibility = (on = !on) ? "" : "hidden";
855
+ }, cm.options.cursorBlinkRate);
856
+ }
857
+
858
+ // HIGHLIGHT WORKER
859
+
860
+ function startWorker(cm, time) {
861
+ if (cm.doc.mode.startState && cm.doc.frontier < cm.display.showingTo)
862
+ cm.state.highlight.set(time, bind(highlightWorker, cm));
863
+ }
864
+
865
+ function highlightWorker(cm) {
866
+ var doc = cm.doc;
867
+ if (doc.frontier < doc.first) doc.frontier = doc.first;
868
+ if (doc.frontier >= cm.display.showingTo) return;
869
+ var end = +new Date + cm.options.workTime;
870
+ var state = copyState(doc.mode, getStateBefore(cm, doc.frontier));
871
+ var changed = [], prevChange;
872
+ doc.iter(doc.frontier, Math.min(doc.first + doc.size, cm.display.showingTo + 500), function(line) {
873
+ if (doc.frontier >= cm.display.showingFrom) { // Visible
874
+ var oldStyles = line.styles;
875
+ line.styles = highlightLine(cm, line, state);
876
+ var ischange = !oldStyles || oldStyles.length != line.styles.length;
877
+ for (var i = 0; !ischange && i < oldStyles.length; ++i) ischange = oldStyles[i] != line.styles[i];
878
+ if (ischange) {
879
+ if (prevChange && prevChange.end == doc.frontier) prevChange.end++;
880
+ else changed.push(prevChange = {start: doc.frontier, end: doc.frontier + 1});
881
+ }
882
+ line.stateAfter = copyState(doc.mode, state);
883
+ } else {
884
+ processLine(cm, line, state);
885
+ line.stateAfter = doc.frontier % 5 == 0 ? copyState(doc.mode, state) : null;
886
+ }
887
+ ++doc.frontier;
888
+ if (+new Date > end) {
889
+ startWorker(cm, cm.options.workDelay);
890
+ return true;
891
+ }
892
+ });
893
+ if (changed.length)
894
+ operation(cm, function() {
895
+ for (var i = 0; i < changed.length; ++i)
896
+ regChange(this, changed[i].start, changed[i].end);
897
+ })();
898
+ }
899
+
900
+ // Finds the line to start with when starting a parse. Tries to
901
+ // find a line with a stateAfter, so that it can start with a
902
+ // valid state. If that fails, it returns the line with the
903
+ // smallest indentation, which tends to need the least context to
904
+ // parse correctly.
905
+ function findStartLine(cm, n) {
906
+ var minindent, minline, doc = cm.doc;
907
+ for (var search = n, lim = n - 100; search > lim; --search) {
908
+ if (search <= doc.first) return doc.first;
909
+ var line = getLine(doc, search - 1);
910
+ if (line.stateAfter) return search;
911
+ var indented = countColumn(line.text, null, cm.options.tabSize);
912
+ if (minline == null || minindent > indented) {
913
+ minline = search - 1;
914
+ minindent = indented;
915
+ }
916
+ }
917
+ return minline;
918
+ }
919
+
920
+ function getStateBefore(cm, n) {
921
+ var doc = cm.doc, display = cm.display;
922
+ if (!doc.mode.startState) return true;
923
+ var pos = findStartLine(cm, n), state = pos > doc.first && getLine(doc, pos-1).stateAfter;
924
+ if (!state) state = startState(doc.mode);
925
+ else state = copyState(doc.mode, state);
926
+ doc.iter(pos, n, function(line) {
927
+ processLine(cm, line, state);
928
+ var save = pos == n - 1 || pos % 5 == 0 || pos >= display.showingFrom && pos < display.showingTo;
929
+ line.stateAfter = save ? copyState(doc.mode, state) : null;
930
+ ++pos;
931
+ });
932
+ return state;
933
+ }
934
+
935
+ // POSITION MEASUREMENT
936
+
937
+ function paddingTop(display) {return display.lineSpace.offsetTop;}
938
+ function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight;}
939
+ function paddingLeft(display) {
940
+ var e = removeChildrenAndAdd(display.measure, elt("pre", null, null, "text-align: left")).appendChild(elt("span", "x"));
941
+ return e.offsetLeft;
942
+ }
943
+
944
+ function measureChar(cm, line, ch, data) {
945
+ var dir = -1;
946
+ data = data || measureLine(cm, line);
947
+
948
+ for (var pos = ch;; pos += dir) {
949
+ var r = data[pos];
950
+ if (r) break;
951
+ if (dir < 0 && pos == 0) dir = 1;
952
+ }
953
+ return {left: pos < ch ? r.right : r.left,
954
+ right: pos > ch ? r.left : r.right,
955
+ top: r.top, bottom: r.bottom};
956
+ }
957
+
958
+ function findCachedMeasurement(cm, line) {
959
+ var cache = cm.display.measureLineCache;
960
+ for (var i = 0; i < cache.length; ++i) {
961
+ var memo = cache[i];
962
+ if (memo.text == line.text && memo.markedSpans == line.markedSpans &&
963
+ cm.display.scroller.clientWidth == memo.width &&
964
+ memo.classes == line.textClass + "|" + line.bgClass + "|" + line.wrapClass)
965
+ return memo.measure;
966
+ }
967
+ }
968
+
969
+ function measureLine(cm, line) {
970
+ // First look in the cache
971
+ var measure = findCachedMeasurement(cm, line);
972
+ if (!measure) {
973
+ // Failing that, recompute and store result in cache
974
+ measure = measureLineInner(cm, line);
975
+ var cache = cm.display.measureLineCache;
976
+ var memo = {text: line.text, width: cm.display.scroller.clientWidth,
977
+ markedSpans: line.markedSpans, measure: measure,
978
+ classes: line.textClass + "|" + line.bgClass + "|" + line.wrapClass};
979
+ if (cache.length == 16) cache[++cm.display.measureLineCachePos % 16] = memo;
980
+ else cache.push(memo);
981
+ }
982
+ return measure;
983
+ }
984
+
985
+ function measureLineInner(cm, line) {
986
+ var display = cm.display, measure = emptyArray(line.text.length);
987
+ var pre = lineContent(cm, line, measure);
988
+
989
+ // IE does not cache element positions of inline elements between
990
+ // calls to getBoundingClientRect. This makes the loop below,
991
+ // which gathers the positions of all the characters on the line,
992
+ // do an amount of layout work quadratic to the number of
993
+ // characters. When line wrapping is off, we try to improve things
994
+ // by first subdividing the line into a bunch of inline blocks, so
995
+ // that IE can reuse most of the layout information from caches
996
+ // for those blocks. This does interfere with line wrapping, so it
997
+ // doesn't work when wrapping is on, but in that case the
998
+ // situation is slightly better, since IE does cache line-wrapping
999
+ // information and only recomputes per-line.
1000
+ if (ie && !ie_lt8 && !cm.options.lineWrapping && pre.childNodes.length > 100) {
1001
+ var fragment = document.createDocumentFragment();
1002
+ var chunk = 10, n = pre.childNodes.length;
1003
+ for (var i = 0, chunks = Math.ceil(n / chunk); i < chunks; ++i) {
1004
+ var wrap = elt("div", null, null, "display: inline-block");
1005
+ for (var j = 0; j < chunk && n; ++j) {
1006
+ wrap.appendChild(pre.firstChild);
1007
+ --n;
1008
+ }
1009
+ fragment.appendChild(wrap);
1010
+ }
1011
+ pre.appendChild(fragment);
1012
+ }
1013
+
1014
+ removeChildrenAndAdd(display.measure, pre);
1015
+
1016
+ var outer = getRect(display.lineDiv);
1017
+ var vranges = [], data = emptyArray(line.text.length), maxBot = pre.offsetHeight;
1018
+ // Work around an IE7/8 bug where it will sometimes have randomly
1019
+ // replaced our pre with a clone at this point.
1020
+ if (ie_lt9 && display.measure.first != pre)
1021
+ removeChildrenAndAdd(display.measure, pre);
1022
+
1023
+ for (var i = 0, cur; i < measure.length; ++i) if (cur = measure[i]) {
1024
+ var size = getRect(cur);
1025
+ var top = Math.max(0, size.top - outer.top), bot = Math.min(size.bottom - outer.top, maxBot);
1026
+ for (var j = 0; j < vranges.length; j += 2) {
1027
+ var rtop = vranges[j], rbot = vranges[j+1];
1028
+ if (rtop > bot || rbot < top) continue;
1029
+ if (rtop <= top && rbot >= bot ||
1030
+ top <= rtop && bot >= rbot ||
1031
+ Math.min(bot, rbot) - Math.max(top, rtop) >= (bot - top) >> 1) {
1032
+ vranges[j] = Math.min(top, rtop);
1033
+ vranges[j+1] = Math.max(bot, rbot);
1034
+ break;
1035
+ }
1036
+ }
1037
+ if (j == vranges.length) vranges.push(top, bot);
1038
+ var right = size.right;
1039
+ if (cur.measureRight) right = getRect(cur.measureRight).left;
1040
+ data[i] = {left: size.left - outer.left, right: right - outer.left, top: j};
1041
+ }
1042
+ for (var i = 0, cur; i < data.length; ++i) if (cur = data[i]) {
1043
+ var vr = cur.top;
1044
+ cur.top = vranges[vr]; cur.bottom = vranges[vr+1];
1045
+ }
1046
+
1047
+ return data;
1048
+ }
1049
+
1050
+ function measureLineWidth(cm, line) {
1051
+ var hasBadSpan = false;
1052
+ if (line.markedSpans) for (var i = 0; i < line.markedSpans; ++i) {
1053
+ var sp = line.markedSpans[i];
1054
+ if (sp.collapsed && (sp.to == null || sp.to == line.text.length)) hasBadSpan = true;
1055
+ }
1056
+ var cached = !hasBadSpan && findCachedMeasurement(cm, line);
1057
+ if (cached) return measureChar(cm, line, line.text.length, cached).right;
1058
+
1059
+ var pre = lineContent(cm, line);
1060
+ var end = pre.appendChild(zeroWidthElement(cm.display.measure));
1061
+ removeChildrenAndAdd(cm.display.measure, pre);
1062
+ return getRect(end).right - getRect(cm.display.lineDiv).left;
1063
+ }
1064
+
1065
+ function clearCaches(cm) {
1066
+ cm.display.measureLineCache.length = cm.display.measureLineCachePos = 0;
1067
+ cm.display.cachedCharWidth = cm.display.cachedTextHeight = null;
1068
+ cm.display.maxLineChanged = true;
1069
+ cm.display.lineNumChars = null;
1070
+ }
1071
+
1072
+ // Context is one of "line", "div" (display.lineDiv), "local"/null (editor), or "page"
1073
+ function intoCoordSystem(cm, lineObj, rect, context) {
1074
+ if (lineObj.widgets) for (var i = 0; i < lineObj.widgets.length; ++i) if (lineObj.widgets[i].above) {
1075
+ var size = widgetHeight(lineObj.widgets[i]);
1076
+ rect.top += size; rect.bottom += size;
1077
+ }
1078
+ if (context == "line") return rect;
1079
+ if (!context) context = "local";
1080
+ var yOff = heightAtLine(cm, lineObj);
1081
+ if (context != "local") yOff -= cm.display.viewOffset;
1082
+ if (context == "page") {
1083
+ var lOff = getRect(cm.display.lineSpace);
1084
+ yOff += lOff.top + (window.pageYOffset || (document.documentElement || document.body).scrollTop);
1085
+ var xOff = lOff.left + (window.pageXOffset || (document.documentElement || document.body).scrollLeft);
1086
+ rect.left += xOff; rect.right += xOff;
1087
+ }
1088
+ rect.top += yOff; rect.bottom += yOff;
1089
+ return rect;
1090
+ }
1091
+
1092
+ // Context may be "window", "page", "div", or "local"/null
1093
+ // Result is in local coords
1094
+ function fromCoordSystem(cm, coords, context) {
1095
+ if (context == "div") return coords;
1096
+ var left = coords.left, top = coords.top;
1097
+ if (context == "page") {
1098
+ left -= window.pageXOffset || (document.documentElement || document.body).scrollLeft;
1099
+ top -= window.pageYOffset || (document.documentElement || document.body).scrollTop;
1100
+ }
1101
+ var lineSpaceBox = getRect(cm.display.lineSpace);
1102
+ left -= lineSpaceBox.left;
1103
+ top -= lineSpaceBox.top;
1104
+ if (context == "local" || !context) {
1105
+ var editorBox = getRect(cm.display.wrapper);
1106
+ left -= editorBox.left;
1107
+ top -= editorBox.top;
1108
+ }
1109
+ return {left: left, top: top};
1110
+ }
1111
+
1112
+ function charCoords(cm, pos, context, lineObj) {
1113
+ if (!lineObj) lineObj = getLine(cm.doc, pos.line);
1114
+ return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch), context);
1115
+ }
1116
+
1117
+ function cursorCoords(cm, pos, context, lineObj, measurement) {
1118
+ lineObj = lineObj || getLine(cm.doc, pos.line);
1119
+ if (!measurement) measurement = measureLine(cm, lineObj);
1120
+ function get(ch, right) {
1121
+ var m = measureChar(cm, lineObj, ch, measurement);
1122
+ if (right) m.left = m.right; else m.right = m.left;
1123
+ return intoCoordSystem(cm, lineObj, m, context);
1124
+ }
1125
+ var order = getOrder(lineObj), ch = pos.ch;
1126
+ if (!order) return get(ch);
1127
+ var main, other, linedir = order[0].level;
1128
+ for (var i = 0; i < order.length; ++i) {
1129
+ var part = order[i], rtl = part.level % 2, nb, here;
1130
+ if (part.from < ch && part.to > ch) return get(ch, rtl);
1131
+ var left = rtl ? part.to : part.from, right = rtl ? part.from : part.to;
1132
+ if (left == ch) {
1133
+ // IE returns bogus offsets and widths for edges where the
1134
+ // direction flips, but only for the side with the lower
1135
+ // level. So we try to use the side with the higher level.
1136
+ if (i && part.level < (nb = order[i-1]).level) here = get(nb.level % 2 ? nb.from : nb.to - 1, true);
1137
+ else here = get(rtl && part.from != part.to ? ch - 1 : ch);
1138
+ if (rtl == linedir) main = here; else other = here;
1139
+ } else if (right == ch) {
1140
+ var nb = i < order.length - 1 && order[i+1];
1141
+ if (!rtl && nb && nb.from == nb.to) continue;
1142
+ if (nb && part.level < nb.level) here = get(nb.level % 2 ? nb.to - 1 : nb.from);
1143
+ else here = get(rtl ? ch : ch - 1, true);
1144
+ if (rtl == linedir) main = here; else other = here;
1145
+ }
1146
+ }
1147
+ if (linedir && !ch) other = get(order[0].to - 1);
1148
+ if (!main) return other;
1149
+ if (other) main.other = other;
1150
+ return main;
1151
+ }
1152
+
1153
+ function PosMaybeOutside(line, ch, outside) {
1154
+ var pos = new Pos(line, ch);
1155
+ if (outside) pos.outside = true;
1156
+ return pos;
1157
+ }
1158
+
1159
+ // Coords must be lineSpace-local
1160
+ function coordsChar(cm, x, y) {
1161
+ var doc = cm.doc;
1162
+ y += cm.display.viewOffset;
1163
+ if (y < 0) return PosMaybeOutside(doc.first, 0, true);
1164
+ var lineNo = lineAtHeight(doc, y), last = doc.first + doc.size - 1;
1165
+ if (lineNo > last)
1166
+ return PosMaybeOutside(doc.first + doc.size - 1, getLine(doc, last).text.length, true);
1167
+ if (x < 0) x = 0;
1168
+
1169
+ for (;;) {
1170
+ var lineObj = getLine(doc, lineNo);
1171
+ var found = coordsCharInner(cm, lineObj, lineNo, x, y);
1172
+ var merged = collapsedSpanAtEnd(lineObj);
1173
+ var mergedPos = merged && merged.find();
1174
+ if (merged && found.ch >= mergedPos.from.ch)
1175
+ lineNo = mergedPos.to.line;
1176
+ else
1177
+ return found;
1178
+ }
1179
+ }
1180
+
1181
+ function coordsCharInner(cm, lineObj, lineNo, x, y) {
1182
+ var innerOff = y - heightAtLine(cm, lineObj);
1183
+ var wrongLine = false, adjust = 2 * cm.display.wrapper.clientWidth;
1184
+ var measurement = measureLine(cm, lineObj);
1185
+
1186
+ function getX(ch) {
1187
+ var sp = cursorCoords(cm, Pos(lineNo, ch), "line",
1188
+ lineObj, measurement);
1189
+ wrongLine = true;
1190
+ if (innerOff > sp.bottom) return sp.left - adjust;
1191
+ else if (innerOff < sp.top) return sp.left + adjust;
1192
+ else wrongLine = false;
1193
+ return sp.left;
1194
+ }
1195
+
1196
+ var bidi = getOrder(lineObj), dist = lineObj.text.length;
1197
+ var from = lineLeft(lineObj), to = lineRight(lineObj);
1198
+ var fromX = getX(from), fromOutside = wrongLine, toX = getX(to), toOutside = wrongLine;
1199
+
1200
+ if (x > toX) return PosMaybeOutside(lineNo, to, toOutside);
1201
+ // Do a binary search between these bounds.
1202
+ for (;;) {
1203
+ if (bidi ? to == from || to == moveVisually(lineObj, from, 1) : to - from <= 1) {
1204
+ var after = x - fromX < toX - x, ch = after ? from : to;
1205
+ while (isExtendingChar.test(lineObj.text.charAt(ch))) ++ch;
1206
+ var pos = PosMaybeOutside(lineNo, ch, after ? fromOutside : toOutside);
1207
+ pos.after = after;
1208
+ return pos;
1209
+ }
1210
+ var step = Math.ceil(dist / 2), middle = from + step;
1211
+ if (bidi) {
1212
+ middle = from;
1213
+ for (var i = 0; i < step; ++i) middle = moveVisually(lineObj, middle, 1);
1214
+ }
1215
+ var middleX = getX(middle);
1216
+ if (middleX > x) {to = middle; toX = middleX; if (toOutside = wrongLine) toX += 1000; dist -= step;}
1217
+ else {from = middle; fromX = middleX; fromOutside = wrongLine; dist = step;}
1218
+ }
1219
+ }
1220
+
1221
+ var measureText;
1222
+ function textHeight(display) {
1223
+ if (display.cachedTextHeight != null) return display.cachedTextHeight;
1224
+ if (measureText == null) {
1225
+ measureText = elt("pre");
1226
+ // Measure a bunch of lines, for browsers that compute
1227
+ // fractional heights.
1228
+ for (var i = 0; i < 49; ++i) {
1229
+ measureText.appendChild(document.createTextNode("x"));
1230
+ measureText.appendChild(elt("br"));
1231
+ }
1232
+ measureText.appendChild(document.createTextNode("x"));
1233
+ }
1234
+ removeChildrenAndAdd(display.measure, measureText);
1235
+ var height = measureText.offsetHeight / 50;
1236
+ if (height > 3) display.cachedTextHeight = height;
1237
+ removeChildren(display.measure);
1238
+ return height || 1;
1239
+ }
1240
+
1241
+ function charWidth(display) {
1242
+ if (display.cachedCharWidth != null) return display.cachedCharWidth;
1243
+ var anchor = elt("span", "x");
1244
+ var pre = elt("pre", [anchor]);
1245
+ removeChildrenAndAdd(display.measure, pre);
1246
+ var width = anchor.offsetWidth;
1247
+ if (width > 2) display.cachedCharWidth = width;
1248
+ return width || 10;
1249
+ }
1250
+
1251
+ // OPERATIONS
1252
+
1253
+ // Operations are used to wrap changes in such a way that each
1254
+ // change won't have to update the cursor and display (which would
1255
+ // be awkward, slow, and error-prone), but instead updates are
1256
+ // batched and then all combined and executed at once.
1257
+
1258
+ var nextOpId = 0;
1259
+ function startOperation(cm) {
1260
+ cm.curOp = {
1261
+ // An array of ranges of lines that have to be updated. See
1262
+ // updateDisplay.
1263
+ changes: [],
1264
+ updateInput: null,
1265
+ userSelChange: null,
1266
+ textChanged: null,
1267
+ selectionChanged: false,
1268
+ updateMaxLine: false,
1269
+ updateScrollPos: false,
1270
+ id: ++nextOpId
1271
+ };
1272
+ if (!delayedCallbackDepth++) delayedCallbacks = [];
1273
+ }
1274
+
1275
+ function endOperation(cm) {
1276
+ var op = cm.curOp, doc = cm.doc, display = cm.display;
1277
+ cm.curOp = null;
1278
+
1279
+ if (op.updateMaxLine) computeMaxLength(cm);
1280
+ if (display.maxLineChanged && !cm.options.lineWrapping) {
1281
+ var width = measureLineWidth(cm, display.maxLine);
1282
+ display.sizer.style.minWidth = Math.max(0, width + 3 + scrollerCutOff) + "px";
1283
+ display.maxLineChanged = false;
1284
+ var maxScrollLeft = Math.max(0, display.sizer.offsetLeft + display.sizer.offsetWidth - display.scroller.clientWidth);
1285
+ if (maxScrollLeft < doc.scrollLeft && !op.updateScrollPos)
1286
+ setScrollLeft(cm, Math.min(display.scroller.scrollLeft, maxScrollLeft), true);
1287
+ }
1288
+ var newScrollPos, updated;
1289
+ if (op.updateScrollPos) {
1290
+ newScrollPos = op.updateScrollPos;
1291
+ } else if (op.selectionChanged && display.scroller.clientHeight) { // don't rescroll if not visible
1292
+ var coords = cursorCoords(cm, doc.sel.head);
1293
+ newScrollPos = calculateScrollPos(cm, coords.left, coords.top, coords.left, coords.bottom);
1294
+ }
1295
+ if (op.changes.length || newScrollPos && newScrollPos.scrollTop != null) {
1296
+ updated = updateDisplay(cm, op.changes, newScrollPos && newScrollPos.scrollTop);
1297
+ if (cm.display.scroller.offsetHeight) cm.doc.scrollTop = cm.display.scroller.scrollTop;
1298
+ }
1299
+ if (!updated && op.selectionChanged) updateSelection(cm);
1300
+ if (op.updateScrollPos) {
1301
+ display.scroller.scrollTop = display.scrollbarV.scrollTop = doc.scrollTop = newScrollPos.scrollTop;
1302
+ display.scroller.scrollLeft = display.scrollbarH.scrollLeft = doc.scrollLeft = newScrollPos.scrollLeft;
1303
+ alignHorizontally(cm);
1304
+ } else if (newScrollPos) {
1305
+ scrollCursorIntoView(cm);
1306
+ }
1307
+ if (op.selectionChanged) restartBlink(cm);
1308
+
1309
+ if (cm.state.focused && op.updateInput)
1310
+ resetInput(cm, op.userSelChange);
1311
+
1312
+ var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers;
1313
+ if (hidden) for (var i = 0; i < hidden.length; ++i)
1314
+ if (!hidden[i].lines.length) signal(hidden[i], "hide");
1315
+ if (unhidden) for (var i = 0; i < unhidden.length; ++i)
1316
+ if (unhidden[i].lines.length) signal(unhidden[i], "unhide");
1317
+
1318
+ var delayed;
1319
+ if (!--delayedCallbackDepth) {
1320
+ delayed = delayedCallbacks;
1321
+ delayedCallbacks = null;
1322
+ }
1323
+ if (op.textChanged)
1324
+ signal(cm, "change", cm, op.textChanged);
1325
+ if (op.selectionChanged) signal(cm, "cursorActivity", cm);
1326
+ if (delayed) for (var i = 0; i < delayed.length; ++i) delayed[i]();
1327
+ }
1328
+
1329
+ // Wraps a function in an operation. Returns the wrapped function.
1330
+ function operation(cm1, f) {
1331
+ return function() {
1332
+ var cm = cm1 || this, withOp = !cm.curOp;
1333
+ if (withOp) startOperation(cm);
1334
+ try { var result = f.apply(cm, arguments); }
1335
+ finally { if (withOp) endOperation(cm); }
1336
+ return result;
1337
+ };
1338
+ }
1339
+ function docOperation(f) {
1340
+ return function() {
1341
+ var withOp = this.cm && !this.cm.curOp, result;
1342
+ if (withOp) startOperation(this.cm);
1343
+ try { result = f.apply(this, arguments); }
1344
+ finally { if (withOp) endOperation(this.cm); }
1345
+ return result;
1346
+ };
1347
+ }
1348
+ function runInOp(cm, f) {
1349
+ var withOp = !cm.curOp, result;
1350
+ if (withOp) startOperation(cm);
1351
+ try { result = f(); }
1352
+ finally { if (withOp) endOperation(cm); }
1353
+ return result;
1354
+ }
1355
+
1356
+ function regChange(cm, from, to, lendiff) {
1357
+ if (from == null) from = cm.doc.first;
1358
+ if (to == null) to = cm.doc.first + cm.doc.size;
1359
+ cm.curOp.changes.push({from: from, to: to, diff: lendiff});
1360
+ }
1361
+
1362
+ // INPUT HANDLING
1363
+
1364
+ function slowPoll(cm) {
1365
+ if (cm.display.pollingFast) return;
1366
+ cm.display.poll.set(cm.options.pollInterval, function() {
1367
+ readInput(cm);
1368
+ if (cm.state.focused) slowPoll(cm);
1369
+ });
1370
+ }
1371
+
1372
+ function fastPoll(cm) {
1373
+ var missed = false;
1374
+ cm.display.pollingFast = true;
1375
+ function p() {
1376
+ var changed = readInput(cm);
1377
+ if (!changed && !missed) {missed = true; cm.display.poll.set(60, p);}
1378
+ else {cm.display.pollingFast = false; slowPoll(cm);}
1379
+ }
1380
+ cm.display.poll.set(20, p);
1381
+ }
1382
+
1383
+ // prevInput is a hack to work with IME. If we reset the textarea
1384
+ // on every change, that breaks IME. So we look for changes
1385
+ // compared to the previous content instead. (Modern browsers have
1386
+ // events that indicate IME taking place, but these are not widely
1387
+ // supported or compatible enough yet to rely on.)
1388
+ function readInput(cm) {
1389
+ var input = cm.display.input, prevInput = cm.display.prevInput, doc = cm.doc, sel = doc.sel;
1390
+ if (!cm.state.focused || hasSelection(input) || isReadOnly(cm)) return false;
1391
+ var text = input.value;
1392
+ if (text == prevInput && posEq(sel.from, sel.to)) return false;
1393
+ // IE enjoys randomly deselecting our input's text when
1394
+ // re-focusing. If the selection is gone but the cursor is at the
1395
+ // start of the input, that's probably what happened.
1396
+ if (ie && text && input.selectionStart === 0) {
1397
+ resetInput(cm, true);
1398
+ return false;
1399
+ }
1400
+ var withOp = !cm.curOp;
1401
+ if (withOp) startOperation(cm);
1402
+ sel.shift = false;
1403
+ var same = 0, l = Math.min(prevInput.length, text.length);
1404
+ while (same < l && prevInput[same] == text[same]) ++same;
1405
+ var from = sel.from, to = sel.to;
1406
+ if (same < prevInput.length)
1407
+ from = Pos(from.line, from.ch - (prevInput.length - same));
1408
+ else if (cm.state.overwrite && posEq(from, to) && !cm.state.pasteIncoming)
1409
+ to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + (text.length - same)));
1410
+ var updateInput = cm.curOp.updateInput;
1411
+ makeChange(cm.doc, {from: from, to: to, text: splitLines(text.slice(same)),
1412
+ origin: cm.state.pasteIncoming ? "paste" : "+input"}, "end");
1413
+
1414
+ cm.curOp.updateInput = updateInput;
1415
+ if (text.length > 1000 || text.indexOf("\n") > -1) input.value = cm.display.prevInput = "";
1416
+ else cm.display.prevInput = text;
1417
+ if (withOp) endOperation(cm);
1418
+ cm.state.pasteIncoming = false;
1419
+ return true;
1420
+ }
1421
+
1422
+ function resetInput(cm, user) {
1423
+ var minimal, selected, doc = cm.doc;
1424
+ if (!posEq(doc.sel.from, doc.sel.to)) {
1425
+ cm.display.prevInput = "";
1426
+ minimal = hasCopyEvent &&
1427
+ (doc.sel.to.line - doc.sel.from.line > 100 || (selected = cm.getSelection()).length > 1000);
1428
+ if (minimal) cm.display.input.value = "-";
1429
+ else cm.display.input.value = selected || cm.getSelection();
1430
+ if (cm.state.focused) selectInput(cm.display.input);
1431
+ } else if (user) cm.display.prevInput = cm.display.input.value = "";
1432
+ cm.display.inaccurateSelection = minimal;
1433
+ }
1434
+
1435
+ function focusInput(cm) {
1436
+ if (cm.options.readOnly != "nocursor" && (!mobile || document.activeElement != cm.display.input))
1437
+ cm.display.input.focus();
1438
+ }
1439
+
1440
+ function isReadOnly(cm) {
1441
+ return cm.options.readOnly || cm.doc.cantEdit;
1442
+ }
1443
+
1444
+ // EVENT HANDLERS
1445
+
1446
+ function registerEventHandlers(cm) {
1447
+ var d = cm.display;
1448
+ on(d.scroller, "mousedown", operation(cm, onMouseDown));
1449
+ on(d.scroller, "dblclick", operation(cm, e_preventDefault));
1450
+ on(d.lineSpace, "selectstart", function(e) {
1451
+ if (!eventInWidget(d, e)) e_preventDefault(e);
1452
+ });
1453
+ // Gecko browsers fire contextmenu *after* opening the menu, at
1454
+ // which point we can't mess with it anymore. Context menu is
1455
+ // handled in onMouseDown for Gecko.
1456
+ if (!captureMiddleClick) on(d.scroller, "contextmenu", function(e) {onContextMenu(cm, e);});
1457
+
1458
+ on(d.scroller, "scroll", function() {
1459
+ if (d.scroller.clientHeight) {
1460
+ setScrollTop(cm, d.scroller.scrollTop);
1461
+ setScrollLeft(cm, d.scroller.scrollLeft, true);
1462
+ signal(cm, "scroll", cm);
1463
+ }
1464
+ });
1465
+ on(d.scrollbarV, "scroll", function() {
1466
+ if (d.scroller.clientHeight) setScrollTop(cm, d.scrollbarV.scrollTop);
1467
+ });
1468
+ on(d.scrollbarH, "scroll", function() {
1469
+ if (d.scroller.clientHeight) setScrollLeft(cm, d.scrollbarH.scrollLeft);
1470
+ });
1471
+
1472
+ on(d.scroller, "mousewheel", function(e){onScrollWheel(cm, e);});
1473
+ on(d.scroller, "DOMMouseScroll", function(e){onScrollWheel(cm, e);});
1474
+
1475
+ function reFocus() { if (cm.state.focused) setTimeout(bind(focusInput, cm), 0); }
1476
+ on(d.scrollbarH, "mousedown", reFocus);
1477
+ on(d.scrollbarV, "mousedown", reFocus);
1478
+ // Prevent wrapper from ever scrolling
1479
+ on(d.wrapper, "scroll", function() { d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; });
1480
+
1481
+ function onResize() {
1482
+ // Might be a text scaling operation, clear size caches.
1483
+ d.cachedCharWidth = d.cachedTextHeight = null;
1484
+ clearCaches(cm);
1485
+ runInOp(cm, bind(regChange, cm));
1486
+ }
1487
+ on(window, "resize", onResize);
1488
+ // Above handler holds on to the editor and its data structures.
1489
+ // Here we poll to unregister it when the editor is no longer in
1490
+ // the document, so that it can be garbage-collected.
1491
+ function unregister() {
1492
+ for (var p = d.wrapper.parentNode; p && p != document.body; p = p.parentNode) {}
1493
+ if (p) setTimeout(unregister, 5000);
1494
+ else off(window, "resize", onResize);
1495
+ }
1496
+ setTimeout(unregister, 5000);
1497
+
1498
+ on(d.input, "keyup", operation(cm, function(e) {
1499
+ if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
1500
+ if (e.keyCode == 16) cm.doc.sel.shift = false;
1501
+ }));
1502
+ on(d.input, "input", bind(fastPoll, cm));
1503
+ on(d.input, "keydown", operation(cm, onKeyDown));
1504
+ on(d.input, "keypress", operation(cm, onKeyPress));
1505
+ on(d.input, "focus", bind(onFocus, cm));
1506
+ on(d.input, "blur", bind(onBlur, cm));
1507
+
1508
+ function drag_(e) {
1509
+ if (cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e))) return;
1510
+ e_stop(e);
1511
+ }
1512
+ if (cm.options.dragDrop) {
1513
+ on(d.scroller, "dragstart", function(e){onDragStart(cm, e);});
1514
+ on(d.scroller, "dragenter", drag_);
1515
+ on(d.scroller, "dragover", drag_);
1516
+ on(d.scroller, "drop", operation(cm, onDrop));
1517
+ }
1518
+ on(d.scroller, "paste", function(e){
1519
+ if (eventInWidget(d, e)) return;
1520
+ focusInput(cm);
1521
+ fastPoll(cm);
1522
+ });
1523
+ on(d.input, "paste", function() {
1524
+ cm.state.pasteIncoming = true;
1525
+ fastPoll(cm);
1526
+ });
1527
+
1528
+ function prepareCopy() {
1529
+ if (d.inaccurateSelection) {
1530
+ d.prevInput = "";
1531
+ d.inaccurateSelection = false;
1532
+ d.input.value = cm.getSelection();
1533
+ selectInput(d.input);
1534
+ }
1535
+ }
1536
+ on(d.input, "cut", prepareCopy);
1537
+ on(d.input, "copy", prepareCopy);
1538
+
1539
+ // Needed to handle Tab key in KHTML
1540
+ if (khtml) on(d.sizer, "mouseup", function() {
1541
+ if (document.activeElement == d.input) d.input.blur();
1542
+ focusInput(cm);
1543
+ });
1544
+ }
1545
+
1546
+ function eventInWidget(display, e) {
1547
+ for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {
1548
+ if (!n) return true;
1549
+ if (/\bCodeMirror-(?:line)?widget\b/.test(n.className) ||
1550
+ n.parentNode == display.sizer && n != display.mover) return true;
1551
+ }
1552
+ }
1553
+
1554
+ function posFromMouse(cm, e, liberal) {
1555
+ var display = cm.display;
1556
+ if (!liberal) {
1557
+ var target = e_target(e);
1558
+ if (target == display.scrollbarH || target == display.scrollbarH.firstChild ||
1559
+ target == display.scrollbarV || target == display.scrollbarV.firstChild ||
1560
+ target == display.scrollbarFiller) return null;
1561
+ }
1562
+ var x, y, space = getRect(display.lineSpace);
1563
+ // Fails unpredictably on IE[67] when mouse is dragged around quickly.
1564
+ try { x = e.clientX; y = e.clientY; } catch (e) { return null; }
1565
+ return coordsChar(cm, x - space.left, y - space.top);
1566
+ }
1567
+
1568
+ var lastClick, lastDoubleClick;
1569
+ function onMouseDown(e) {
1570
+ var cm = this, display = cm.display, doc = cm.doc, sel = doc.sel;
1571
+ sel.shift = e.shiftKey;
1572
+
1573
+ if (eventInWidget(display, e)) {
1574
+ if (!webkit) {
1575
+ display.scroller.draggable = false;
1576
+ setTimeout(function(){display.scroller.draggable = true;}, 100);
1577
+ }
1578
+ return;
1579
+ }
1580
+ if (clickInGutter(cm, e)) return;
1581
+ var start = posFromMouse(cm, e);
1582
+
1583
+ switch (e_button(e)) {
1584
+ case 3:
1585
+ if (captureMiddleClick) onContextMenu.call(cm, cm, e);
1586
+ return;
1587
+ case 2:
1588
+ if (start) extendSelection(cm.doc, start);
1589
+ setTimeout(bind(focusInput, cm), 20);
1590
+ e_preventDefault(e);
1591
+ return;
1592
+ }
1593
+ // For button 1, if it was clicked inside the editor
1594
+ // (posFromMouse returning non-null), we have to adjust the
1595
+ // selection.
1596
+ if (!start) {if (e_target(e) == display.scroller) e_preventDefault(e); return;}
1597
+
1598
+ if (!cm.state.focused) onFocus(cm);
1599
+
1600
+ var now = +new Date, type = "single";
1601
+ if (lastDoubleClick && lastDoubleClick.time > now - 400 && posEq(lastDoubleClick.pos, start)) {
1602
+ type = "triple";
1603
+ e_preventDefault(e);
1604
+ setTimeout(bind(focusInput, cm), 20);
1605
+ selectLine(cm, start.line);
1606
+ } else if (lastClick && lastClick.time > now - 400 && posEq(lastClick.pos, start)) {
1607
+ type = "double";
1608
+ lastDoubleClick = {time: now, pos: start};
1609
+ e_preventDefault(e);
1610
+ var word = findWordAt(getLine(doc, start.line).text, start);
1611
+ extendSelection(cm.doc, word.from, word.to);
1612
+ } else { lastClick = {time: now, pos: start}; }
1613
+
1614
+ var last = start;
1615
+ if (cm.options.dragDrop && dragAndDrop && !isReadOnly(cm) && !posEq(sel.from, sel.to) &&
1616
+ !posLess(start, sel.from) && !posLess(sel.to, start) && type == "single") {
1617
+ var dragEnd = operation(cm, function(e2) {
1618
+ if (webkit) display.scroller.draggable = false;
1619
+ cm.state.draggingText = false;
1620
+ off(document, "mouseup", dragEnd);
1621
+ off(display.scroller, "drop", dragEnd);
1622
+ if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) {
1623
+ e_preventDefault(e2);
1624
+ extendSelection(cm.doc, start);
1625
+ focusInput(cm);
1626
+ }
1627
+ });
1628
+ // Let the drag handler handle this.
1629
+ if (webkit) display.scroller.draggable = true;
1630
+ cm.state.draggingText = dragEnd;
1631
+ // IE's approach to draggable
1632
+ if (display.scroller.dragDrop) display.scroller.dragDrop();
1633
+ on(document, "mouseup", dragEnd);
1634
+ on(display.scroller, "drop", dragEnd);
1635
+ return;
1636
+ }
1637
+ e_preventDefault(e);
1638
+ if (type == "single") extendSelection(cm.doc, clipPos(doc, start));
1639
+
1640
+ var startstart = sel.from, startend = sel.to;
1641
+
1642
+ function doSelect(cur) {
1643
+ if (type == "single") {
1644
+ extendSelection(cm.doc, clipPos(doc, start), cur);
1645
+ return;
1646
+ }
1647
+
1648
+ startstart = clipPos(doc, startstart);
1649
+ startend = clipPos(doc, startend);
1650
+ if (type == "double") {
1651
+ var word = findWordAt(getLine(doc, cur.line).text, cur);
1652
+ if (posLess(cur, startstart)) extendSelection(cm.doc, word.from, startend);
1653
+ else extendSelection(cm.doc, startstart, word.to);
1654
+ } else if (type == "triple") {
1655
+ if (posLess(cur, startstart)) extendSelection(cm.doc, startend, clipPos(doc, Pos(cur.line, 0)));
1656
+ else extendSelection(cm.doc, startstart, clipPos(doc, Pos(cur.line + 1, 0)));
1657
+ }
1658
+ }
1659
+
1660
+ var editorSize = getRect(display.wrapper);
1661
+ // Used to ensure timeout re-tries don't fire when another extend
1662
+ // happened in the meantime (clearTimeout isn't reliable -- at
1663
+ // least on Chrome, the timeouts still happen even when cleared,
1664
+ // if the clear happens after their scheduled firing time).
1665
+ var counter = 0;
1666
+
1667
+ function extend(e) {
1668
+ var curCount = ++counter;
1669
+ var cur = posFromMouse(cm, e, true);
1670
+ if (!cur) return;
1671
+ if (!posEq(cur, last)) {
1672
+ if (!cm.state.focused) onFocus(cm);
1673
+ last = cur;
1674
+ doSelect(cur);
1675
+ var visible = visibleLines(display, doc);
1676
+ if (cur.line >= visible.to || cur.line < visible.from)
1677
+ setTimeout(operation(cm, function(){if (counter == curCount) extend(e);}), 150);
1678
+ } else {
1679
+ var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0;
1680
+ if (outside) setTimeout(operation(cm, function() {
1681
+ if (counter != curCount) return;
1682
+ display.scroller.scrollTop += outside;
1683
+ extend(e);
1684
+ }), 50);
1685
+ }
1686
+ }
1687
+
1688
+ function done(e) {
1689
+ counter = Infinity;
1690
+ var cur = posFromMouse(cm, e);
1691
+ if (cur) doSelect(cur);
1692
+ e_preventDefault(e);
1693
+ focusInput(cm);
1694
+ off(document, "mousemove", move);
1695
+ off(document, "mouseup", up);
1696
+ }
1697
+
1698
+ var move = operation(cm, function(e) {
1699
+ if (!ie && !e_button(e)) done(e);
1700
+ else extend(e);
1701
+ });
1702
+ var up = operation(cm, done);
1703
+ on(document, "mousemove", move);
1704
+ on(document, "mouseup", up);
1705
+ }
1706
+
1707
+ function onDrop(e) {
1708
+ var cm = this;
1709
+ if (eventInWidget(cm.display, e) || (cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e))))
1710
+ return;
1711
+ e_preventDefault(e);
1712
+ var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files;
1713
+ if (!pos || isReadOnly(cm)) return;
1714
+ if (files && files.length && window.FileReader && window.File) {
1715
+ var n = files.length, text = Array(n), read = 0;
1716
+ var loadFile = function(file, i) {
1717
+ var reader = new FileReader;
1718
+ reader.onload = function() {
1719
+ text[i] = reader.result;
1720
+ if (++read == n) {
1721
+ pos = clipPos(cm.doc, pos);
1722
+ makeChange(cm.doc, {from: pos, to: pos, text: splitLines(text.join("\n")), origin: "paste"}, "around");
1723
+ }
1724
+ };
1725
+ reader.readAsText(file);
1726
+ };
1727
+ for (var i = 0; i < n; ++i) loadFile(files[i], i);
1728
+ } else {
1729
+ // Don't do a replace if the drop happened inside of the selected text.
1730
+ if (cm.state.draggingText && !(posLess(pos, cm.doc.sel.from) || posLess(cm.doc.sel.to, pos))) {
1731
+ cm.state.draggingText(e);
1732
+ // Ensure the editor is re-focused
1733
+ setTimeout(bind(focusInput, cm), 20);
1734
+ return;
1735
+ }
1736
+ try {
1737
+ var text = e.dataTransfer.getData("Text");
1738
+ if (text) {
1739
+ var curFrom = cm.doc.sel.from, curTo = cm.doc.sel.to;
1740
+ setSelection(cm.doc, pos, pos);
1741
+ if (cm.state.draggingText) replaceRange(cm.doc, "", curFrom, curTo, "paste");
1742
+ cm.replaceSelection(text, null, "paste");
1743
+ focusInput(cm);
1744
+ onFocus(cm);
1745
+ }
1746
+ }
1747
+ catch(e){}
1748
+ }
1749
+ }
1750
+
1751
+ function clickInGutter(cm, e) {
1752
+ var display = cm.display;
1753
+ try { var mX = e.clientX, mY = e.clientY; }
1754
+ catch(e) { return false; }
1755
+
1756
+ if (mX >= Math.floor(getRect(display.gutters).right)) return false;
1757
+ e_preventDefault(e);
1758
+ if (!hasHandler(cm, "gutterClick")) return true;
1759
+
1760
+ var lineBox = getRect(display.lineDiv);
1761
+ if (mY > lineBox.bottom) return true;
1762
+ mY -= lineBox.top - display.viewOffset;
1763
+
1764
+ for (var i = 0; i < cm.options.gutters.length; ++i) {
1765
+ var g = display.gutters.childNodes[i];
1766
+ if (g && getRect(g).right >= mX) {
1767
+ var line = lineAtHeight(cm.doc, mY);
1768
+ var gutter = cm.options.gutters[i];
1769
+ signalLater(cm, "gutterClick", cm, line, gutter, e);
1770
+ break;
1771
+ }
1772
+ }
1773
+ return true;
1774
+ }
1775
+
1776
+ function onDragStart(cm, e) {
1777
+ if (eventInWidget(cm.display, e)) return;
1778
+
1779
+ var txt = cm.getSelection();
1780
+ e.dataTransfer.setData("Text", txt);
1781
+
1782
+ // Use dummy image instead of default browsers image.
1783
+ // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
1784
+ if (e.dataTransfer.setDragImage) {
1785
+ var img = elt("img", null, null, "position: fixed; left: 0; top: 0;");
1786
+ if (opera) {
1787
+ img.width = img.height = 1;
1788
+ cm.display.wrapper.appendChild(img);
1789
+ // Force a relayout, or Opera won't use our image for some obscure reason
1790
+ img._top = img.offsetTop;
1791
+ }
1792
+ if (safari) {
1793
+ if (cm.display.dragImg) {
1794
+ img = cm.display.dragImg;
1795
+ } else {
1796
+ cm.display.dragImg = img;
1797
+ img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
1798
+ cm.display.wrapper.appendChild(img);
1799
+ }
1800
+ }
1801
+ e.dataTransfer.setDragImage(img, 0, 0);
1802
+ if (opera) img.parentNode.removeChild(img);
1803
+ }
1804
+ }
1805
+
1806
+ function setScrollTop(cm, val) {
1807
+ if (Math.abs(cm.doc.scrollTop - val) < 2) return;
1808
+ cm.doc.scrollTop = val;
1809
+ if (!gecko) updateDisplay(cm, [], val);
1810
+ if (cm.display.scroller.scrollTop != val) cm.display.scroller.scrollTop = val;
1811
+ if (cm.display.scrollbarV.scrollTop != val) cm.display.scrollbarV.scrollTop = val;
1812
+ if (gecko) updateDisplay(cm, []);
1813
+ }
1814
+ function setScrollLeft(cm, val, isScroller) {
1815
+ if (isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) return;
1816
+ val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth);
1817
+ cm.doc.scrollLeft = val;
1818
+ alignHorizontally(cm);
1819
+ if (cm.display.scroller.scrollLeft != val) cm.display.scroller.scrollLeft = val;
1820
+ if (cm.display.scrollbarH.scrollLeft != val) cm.display.scrollbarH.scrollLeft = val;
1821
+ }
1822
+
1823
+ // Since the delta values reported on mouse wheel events are
1824
+ // unstandardized between browsers and even browser versions, and
1825
+ // generally horribly unpredictable, this code starts by measuring
1826
+ // the scroll effect that the first few mouse wheel events have,
1827
+ // and, from that, detects the way it can convert deltas to pixel
1828
+ // offsets afterwards.
1829
+ //
1830
+ // The reason we want to know the amount a wheel event will scroll
1831
+ // is that it gives us a chance to update the display before the
1832
+ // actual scrolling happens, reducing flickering.
1833
+
1834
+ var wheelSamples = 0, wheelPixelsPerUnit = null;
1835
+ // Fill in a browser-detected starting value on browsers where we
1836
+ // know one. These don't have to be accurate -- the result of them
1837
+ // being wrong would just be a slight flicker on the first wheel
1838
+ // scroll (if it is large enough).
1839
+ if (ie) wheelPixelsPerUnit = -.53;
1840
+ else if (gecko) wheelPixelsPerUnit = 15;
1841
+ else if (chrome) wheelPixelsPerUnit = -.7;
1842
+ else if (safari) wheelPixelsPerUnit = -1/3;
1843
+
1844
+ function onScrollWheel(cm, e) {
1845
+ var dx = e.wheelDeltaX, dy = e.wheelDeltaY;
1846
+ if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) dx = e.detail;
1847
+ if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) dy = e.detail;
1848
+ else if (dy == null) dy = e.wheelDelta;
1849
+
1850
+ // Webkit browsers on OS X abort momentum scrolls when the target
1851
+ // of the scroll event is removed from the scrollable element.
1852
+ // This hack (see related code in patchDisplay) makes sure the
1853
+ // element is kept around.
1854
+ if (dy && mac && webkit) {
1855
+ for (var cur = e.target; cur != scroll; cur = cur.parentNode) {
1856
+ if (cur.lineObj) {
1857
+ cm.display.currentWheelTarget = cur;
1858
+ break;
1859
+ }
1860
+ }
1861
+ }
1862
+
1863
+ var display = cm.display, scroll = display.scroller;
1864
+ // On some browsers, horizontal scrolling will cause redraws to
1865
+ // happen before the gutter has been realigned, causing it to
1866
+ // wriggle around in a most unseemly way. When we have an
1867
+ // estimated pixels/delta value, we just handle horizontal
1868
+ // scrolling entirely here. It'll be slightly off from native, but
1869
+ // better than glitching out.
1870
+ if (dx && !gecko && !opera && wheelPixelsPerUnit != null) {
1871
+ if (dy)
1872
+ setScrollTop(cm, Math.max(0, Math.min(scroll.scrollTop + dy * wheelPixelsPerUnit, scroll.scrollHeight - scroll.clientHeight)));
1873
+ setScrollLeft(cm, Math.max(0, Math.min(scroll.scrollLeft + dx * wheelPixelsPerUnit, scroll.scrollWidth - scroll.clientWidth)));
1874
+ e_preventDefault(e);
1875
+ display.wheelStartX = null; // Abort measurement, if in progress
1876
+ return;
1877
+ }
1878
+
1879
+ if (dy && wheelPixelsPerUnit != null) {
1880
+ var pixels = dy * wheelPixelsPerUnit;
1881
+ var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight;
1882
+ if (pixels < 0) top = Math.max(0, top + pixels - 50);
1883
+ else bot = Math.min(cm.doc.height, bot + pixels + 50);
1884
+ updateDisplay(cm, [], {top: top, bottom: bot});
1885
+ }
1886
+
1887
+ if (wheelSamples < 20) {
1888
+ if (display.wheelStartX == null) {
1889
+ display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop;
1890
+ display.wheelDX = dx; display.wheelDY = dy;
1891
+ setTimeout(function() {
1892
+ if (display.wheelStartX == null) return;
1893
+ var movedX = scroll.scrollLeft - display.wheelStartX;
1894
+ var movedY = scroll.scrollTop - display.wheelStartY;
1895
+ var sample = (movedY && display.wheelDY && movedY / display.wheelDY) ||
1896
+ (movedX && display.wheelDX && movedX / display.wheelDX);
1897
+ display.wheelStartX = display.wheelStartY = null;
1898
+ if (!sample) return;
1899
+ wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1);
1900
+ ++wheelSamples;
1901
+ }, 200);
1902
+ } else {
1903
+ display.wheelDX += dx; display.wheelDY += dy;
1904
+ }
1905
+ }
1906
+ }
1907
+
1908
+ function doHandleBinding(cm, bound, dropShift) {
1909
+ if (typeof bound == "string") {
1910
+ bound = commands[bound];
1911
+ if (!bound) return false;
1912
+ }
1913
+ // Ensure previous input has been read, so that the handler sees a
1914
+ // consistent view of the document
1915
+ if (cm.display.pollingFast && readInput(cm)) cm.display.pollingFast = false;
1916
+ var doc = cm.doc, prevShift = doc.sel.shift, done = false;
1917
+ try {
1918
+ if (isReadOnly(cm)) cm.state.suppressEdits = true;
1919
+ if (dropShift) doc.sel.shift = false;
1920
+ done = bound(cm) != Pass;
1921
+ } finally {
1922
+ doc.sel.shift = prevShift;
1923
+ cm.state.suppressEdits = false;
1924
+ }
1925
+ return done;
1926
+ }
1927
+
1928
+ function allKeyMaps(cm) {
1929
+ var maps = cm.state.keyMaps.slice(0);
1930
+ if (cm.options.extraKeys) maps.push(cm.options.extraKeys);
1931
+ maps.push(cm.options.keyMap);
1932
+ return maps;
1933
+ }
1934
+
1935
+ var maybeTransition;
1936
+ function handleKeyBinding(cm, e) {
1937
+ // Handle auto keymap transitions
1938
+ var startMap = getKeyMap(cm.options.keyMap), next = startMap.auto;
1939
+ clearTimeout(maybeTransition);
1940
+ if (next && !isModifierKey(e)) maybeTransition = setTimeout(function() {
1941
+ if (getKeyMap(cm.options.keyMap) == startMap)
1942
+ cm.options.keyMap = (next.call ? next.call(null, cm) : next);
1943
+ }, 50);
1944
+
1945
+ var name = keyName(e, true), handled = false;
1946
+ if (!name) return false;
1947
+ var keymaps = allKeyMaps(cm);
1948
+
1949
+ if (e.shiftKey) {
1950
+ // First try to resolve full name (including 'Shift-'). Failing
1951
+ // that, see if there is a cursor-motion command (starting with
1952
+ // 'go') bound to the keyname without 'Shift-'.
1953
+ handled = lookupKey("Shift-" + name, keymaps, function(b) {return doHandleBinding(cm, b, true);})
1954
+ || lookupKey(name, keymaps, function(b) {
1955
+ if (typeof b == "string" && /^go[A-Z]/.test(b)) return doHandleBinding(cm, b);
1956
+ });
1957
+ } else {
1958
+ handled = lookupKey(name, keymaps, function(b) { return doHandleBinding(cm, b); });
1959
+ }
1960
+ if (handled == "stop") handled = false;
1961
+
1962
+ if (handled) {
1963
+ e_preventDefault(e);
1964
+ restartBlink(cm);
1965
+ if (ie_lt9) { e.oldKeyCode = e.keyCode; e.keyCode = 0; }
1966
+ }
1967
+ return handled;
1968
+ }
1969
+
1970
+ function handleCharBinding(cm, e, ch) {
1971
+ var handled = lookupKey("'" + ch + "'", allKeyMaps(cm),
1972
+ function(b) { return doHandleBinding(cm, b, true); });
1973
+ if (handled) {
1974
+ e_preventDefault(e);
1975
+ restartBlink(cm);
1976
+ }
1977
+ return handled;
1978
+ }
1979
+
1980
+ var lastStoppedKey = null;
1981
+ function onKeyDown(e) {
1982
+ var cm = this;
1983
+ if (!cm.state.focused) onFocus(cm);
1984
+ if (ie && e.keyCode == 27) { e.returnValue = false; }
1985
+ if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
1986
+ var code = e.keyCode;
1987
+ // IE does strange things with escape.
1988
+ cm.doc.sel.shift = code == 16 || e.shiftKey;
1989
+ // First give onKeyEvent option a chance to handle this.
1990
+ var handled = handleKeyBinding(cm, e);
1991
+ if (opera) {
1992
+ lastStoppedKey = handled ? code : null;
1993
+ // Opera has no cut event... we try to at least catch the key combo
1994
+ if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey))
1995
+ cm.replaceSelection("");
1996
+ }
1997
+ }
1998
+
1999
+ function onKeyPress(e) {
2000
+ var cm = this;
2001
+ if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
2002
+ var keyCode = e.keyCode, charCode = e.charCode;
2003
+ if (opera && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;}
2004
+ if (((opera && (!e.which || e.which < 10)) || khtml) && handleKeyBinding(cm, e)) return;
2005
+ var ch = String.fromCharCode(charCode == null ? keyCode : charCode);
2006
+ if (this.options.electricChars && this.doc.mode.electricChars &&
2007
+ this.options.smartIndent && !isReadOnly(this) &&
2008
+ this.doc.mode.electricChars.indexOf(ch) > -1)
2009
+ setTimeout(operation(cm, function() {indentLine(cm, cm.doc.sel.to.line, "smart");}), 75);
2010
+ if (handleCharBinding(cm, e, ch)) return;
2011
+ fastPoll(cm);
2012
+ }
2013
+
2014
+ function onFocus(cm) {
2015
+ if (cm.options.readOnly == "nocursor") return;
2016
+ if (!cm.state.focused) {
2017
+ signal(cm, "focus", cm);
2018
+ cm.state.focused = true;
2019
+ if (cm.display.wrapper.className.search(/\bCodeMirror-focused\b/) == -1)
2020
+ cm.display.wrapper.className += " CodeMirror-focused";
2021
+ resetInput(cm, true);
2022
+ }
2023
+ slowPoll(cm);
2024
+ restartBlink(cm);
2025
+ }
2026
+ function onBlur(cm) {
2027
+ if (cm.state.focused) {
2028
+ signal(cm, "blur", cm);
2029
+ cm.state.focused = false;
2030
+ cm.display.wrapper.className = cm.display.wrapper.className.replace(" CodeMirror-focused", "");
2031
+ }
2032
+ clearInterval(cm.display.blinker);
2033
+ setTimeout(function() {if (!cm.state.focused) cm.doc.sel.shift = false;}, 150);
2034
+ }
2035
+
2036
+ var detectingSelectAll;
2037
+ function onContextMenu(cm, e) {
2038
+ var display = cm.display, sel = cm.doc.sel;
2039
+ if (eventInWidget(display, e)) return;
2040
+
2041
+ var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop;
2042
+ if (!pos || opera) return; // Opera is difficult.
2043
+ if (posEq(sel.from, sel.to) || posLess(pos, sel.from) || !posLess(pos, sel.to))
2044
+ operation(cm, setSelection)(cm.doc, pos, pos);
2045
+
2046
+ var oldCSS = display.input.style.cssText;
2047
+ display.inputDiv.style.position = "absolute";
2048
+ display.input.style.cssText = "position: fixed; width: 30px; height: 30px; top: " + (e.clientY - 5) +
2049
+ "px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: white; outline: none;" +
2050
+ "border-width: 0; outline: none; overflow: hidden; opacity: .05; -ms-opacity: .05; filter: alpha(opacity=5);";
2051
+ focusInput(cm);
2052
+ resetInput(cm, true);
2053
+ // Adds "Select all" to context menu in FF
2054
+ if (posEq(sel.from, sel.to)) display.input.value = display.prevInput = " ";
2055
+
2056
+ function rehide() {
2057
+ display.inputDiv.style.position = "relative";
2058
+ display.input.style.cssText = oldCSS;
2059
+ if (ie_lt9) display.scrollbarV.scrollTop = display.scroller.scrollTop = scrollPos;
2060
+ slowPoll(cm);
2061
+
2062
+ // Try to detect the user choosing select-all
2063
+ if (display.input.selectionStart != null && (!ie || ie_lt9)) {
2064
+ clearTimeout(detectingSelectAll);
2065
+ var extval = display.input.value = " " + (posEq(sel.from, sel.to) ? "" : display.input.value), i = 0;
2066
+ display.prevInput = " ";
2067
+ display.input.selectionStart = 1; display.input.selectionEnd = extval.length;
2068
+ var poll = function(){
2069
+ if (display.prevInput == " " && display.input.selectionStart == 0)
2070
+ operation(cm, commands.selectAll)(cm);
2071
+ else if (i++ < 10) detectingSelectAll = setTimeout(poll, 500);
2072
+ else resetInput(cm);
2073
+ };
2074
+ detectingSelectAll = setTimeout(poll, 200);
2075
+ }
2076
+ }
2077
+
2078
+ if (captureMiddleClick) {
2079
+ e_stop(e);
2080
+ var mouseup = function() {
2081
+ off(window, "mouseup", mouseup);
2082
+ setTimeout(rehide, 20);
2083
+ };
2084
+ on(window, "mouseup", mouseup);
2085
+ } else {
2086
+ setTimeout(rehide, 50);
2087
+ }
2088
+ }
2089
+
2090
+ // UPDATING
2091
+
2092
+ function changeEnd(change) {
2093
+ return Pos(change.from.line + change.text.length - 1,
2094
+ lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0));
2095
+ }
2096
+
2097
+ // Make sure a position will be valid after the given change.
2098
+ function clipPostChange(doc, change, pos) {
2099
+ if (!posLess(change.from, pos)) return clipPos(doc, pos);
2100
+ var diff = (change.text.length - 1) - (change.to.line - change.from.line);
2101
+ if (pos.line > change.to.line + diff) {
2102
+ var preLine = pos.line - diff, lastLine = doc.first + doc.size - 1;
2103
+ if (preLine > lastLine) return Pos(lastLine, getLine(doc, lastLine).text.length);
2104
+ return clipToLen(pos, getLine(doc, preLine).text.length);
2105
+ }
2106
+ if (pos.line == change.to.line + diff)
2107
+ return clipToLen(pos, lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0) +
2108
+ getLine(doc, change.to.line).text.length - change.to.ch);
2109
+ var inside = pos.line - change.from.line;
2110
+ return clipToLen(pos, change.text[inside].length + (inside ? 0 : change.from.ch));
2111
+ }
2112
+
2113
+ // Hint can be null|"end"|"start"|"around"|{anchor,head}
2114
+ function computeSelAfterChange(doc, change, hint) {
2115
+ if (hint && typeof hint == "object") // Assumed to be {anchor, head} object
2116
+ return {anchor: clipPostChange(doc, change, hint.anchor),
2117
+ head: clipPostChange(doc, change, hint.head)};
2118
+
2119
+ if (hint == "start") return {anchor: change.from, head: change.from};
2120
+
2121
+ var end = changeEnd(change);
2122
+ if (hint == "around") return {anchor: change.from, head: end};
2123
+ if (hint == "end") return {anchor: end, head: end};
2124
+
2125
+ // hint is null, leave the selection alone as much as possible
2126
+ var adjustPos = function(pos) {
2127
+ if (posLess(pos, change.from)) return pos;
2128
+ if (!posLess(change.to, pos)) return end;
2129
+
2130
+ var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch;
2131
+ if (pos.line == change.to.line) ch += end.ch - change.to.ch;
2132
+ return Pos(line, ch);
2133
+ };
2134
+ return {anchor: adjustPos(doc.sel.anchor), head: adjustPos(doc.sel.head)};
2135
+ }
2136
+
2137
+ function filterChange(doc, change) {
2138
+ var obj = {
2139
+ canceled: false,
2140
+ from: change.from,
2141
+ to: change.to,
2142
+ text: change.text,
2143
+ origin: change.origin,
2144
+ update: function(from, to, text, origin) {
2145
+ if (from) this.from = clipPos(doc, from);
2146
+ if (to) this.to = clipPos(doc, to);
2147
+ if (text) this.text = text;
2148
+ if (origin !== undefined) this.origin = origin;
2149
+ },
2150
+ cancel: function() { this.canceled = true; }
2151
+ };
2152
+ signal(doc, "beforeChange", doc, obj);
2153
+ if (doc.cm) signal(doc.cm, "beforeChange", doc.cm, obj);
2154
+
2155
+ if (obj.canceled) return null;
2156
+ return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin};
2157
+ }
2158
+
2159
+ // Replace the range from from to to by the strings in replacement.
2160
+ // change is a {from, to, text [, origin]} object
2161
+ function makeChange(doc, change, selUpdate, ignoreReadOnly) {
2162
+ if (doc.cm) {
2163
+ if (!doc.cm.curOp) return operation(doc.cm, makeChange)(doc, change, selUpdate, ignoreReadOnly);
2164
+ if (doc.cm.state.suppressEdits) return;
2165
+ }
2166
+
2167
+ if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) {
2168
+ change = filterChange(doc, change);
2169
+ if (!change) return;
2170
+ }
2171
+
2172
+ // Possibly split or suppress the update based on the presence
2173
+ // of read-only spans in its range.
2174
+ var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to);
2175
+ if (split) {
2176
+ for (var i = split.length - 1; i >= 1; --i)
2177
+ makeChangeNoReadonly(doc, {from: split[i].from, to: split[i].to, text: [""]});
2178
+ if (split.length)
2179
+ makeChangeNoReadonly(doc, {from: split[0].from, to: split[0].to, text: change.text}, selUpdate);
2180
+ } else {
2181
+ makeChangeNoReadonly(doc, change, selUpdate);
2182
+ }
2183
+ }
2184
+
2185
+ function makeChangeNoReadonly(doc, change, selUpdate) {
2186
+ var selAfter = computeSelAfterChange(doc, change, selUpdate);
2187
+ addToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN);
2188
+
2189
+ makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change));
2190
+ var rebased = [];
2191
+
2192
+ linkedDocs(doc, function(doc, sharedHist) {
2193
+ if (!sharedHist && indexOf(rebased, doc.history) == -1) {
2194
+ rebaseHist(doc.history, change);
2195
+ rebased.push(doc.history);
2196
+ }
2197
+ makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change));
2198
+ });
2199
+ }
2200
+
2201
+ function makeChangeFromHistory(doc, type) {
2202
+ if (doc.cm && doc.cm.state.suppressEdits) return;
2203
+
2204
+ var hist = doc.history;
2205
+ var event = (type == "undo" ? hist.done : hist.undone).pop();
2206
+ if (!event) return;
2207
+ hist.dirtyCounter += type == "undo" ? -1 : 1;
2208
+
2209
+ var anti = {changes: [], anchorBefore: event.anchorAfter, headBefore: event.headAfter,
2210
+ anchorAfter: event.anchorBefore, headAfter: event.headBefore};
2211
+ (type == "undo" ? hist.undone : hist.done).push(anti);
2212
+
2213
+ for (var i = event.changes.length - 1; i >= 0; --i) {
2214
+ var change = event.changes[i];
2215
+ change.origin = type;
2216
+ anti.changes.push(historyChangeFromChange(doc, change));
2217
+
2218
+ var after = i ? computeSelAfterChange(doc, change, null)
2219
+ : {anchor: event.anchorBefore, head: event.headBefore};
2220
+ makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change));
2221
+ var rebased = [];
2222
+
2223
+ linkedDocs(doc, function(doc, sharedHist) {
2224
+ if (!sharedHist && indexOf(rebased, doc.history) == -1) {
2225
+ rebaseHist(doc.history, change);
2226
+ rebased.push(doc.history);
2227
+ }
2228
+ makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change));
2229
+ });
2230
+ }
2231
+ }
2232
+
2233
+ function shiftDoc(doc, distance) {
2234
+ function shiftPos(pos) {return Pos(pos.line + distance, pos.ch);}
2235
+ doc.first += distance;
2236
+ if (doc.cm) regChange(doc.cm, doc.first, doc.first, distance);
2237
+ doc.sel.head = shiftPos(doc.sel.head); doc.sel.anchor = shiftPos(doc.sel.anchor);
2238
+ doc.sel.from = shiftPos(doc.sel.from); doc.sel.to = shiftPos(doc.sel.to);
2239
+ }
2240
+
2241
+ function makeChangeSingleDoc(doc, change, selAfter, spans) {
2242
+ if (doc.cm && !doc.cm.curOp)
2243
+ return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans);
2244
+
2245
+ if (change.to.line < doc.first) {
2246
+ shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line));
2247
+ return;
2248
+ }
2249
+ if (change.from.line > doc.lastLine()) return;
2250
+
2251
+ // Clip the change to the size of this doc
2252
+ if (change.from.line < doc.first) {
2253
+ var shift = change.text.length - 1 - (doc.first - change.from.line);
2254
+ shiftDoc(doc, shift);
2255
+ change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch),
2256
+ text: [lst(change.text)], origin: change.origin};
2257
+ }
2258
+ var last = doc.lastLine();
2259
+ if (change.to.line > last) {
2260
+ change = {from: change.from, to: Pos(last, getLine(doc, last).text.length),
2261
+ text: [change.text[0]], origin: change.origin};
2262
+ }
2263
+
2264
+ change.removed = getBetween(doc, change.from, change.to);
2265
+
2266
+ if (!selAfter) selAfter = computeSelAfterChange(doc, change, null);
2267
+ if (doc.cm) makeChangeSingleDocInEditor(doc.cm, change, spans, selAfter);
2268
+ else updateDoc(doc, change, spans, selAfter);
2269
+ }
2270
+
2271
+ function makeChangeSingleDocInEditor(cm, change, spans, selAfter) {
2272
+ var doc = cm.doc, display = cm.display, from = change.from, to = change.to;
2273
+
2274
+ var recomputeMaxLength = false, checkWidthStart = from.line;
2275
+ if (!cm.options.lineWrapping) {
2276
+ checkWidthStart = lineNo(visualLine(doc, getLine(doc, from.line)));
2277
+ doc.iter(checkWidthStart, to.line + 1, function(line) {
2278
+ if (line == display.maxLine) {
2279
+ recomputeMaxLength = true;
2280
+ return true;
2281
+ }
2282
+ });
2283
+ }
2284
+
2285
+ updateDoc(doc, change, spans, selAfter, estimateHeight(cm));
2286
+
2287
+ if (!cm.options.lineWrapping) {
2288
+ doc.iter(checkWidthStart, from.line + change.text.length, function(line) {
2289
+ var len = lineLength(doc, line);
2290
+ if (len > display.maxLineLength) {
2291
+ display.maxLine = line;
2292
+ display.maxLineLength = len;
2293
+ display.maxLineChanged = true;
2294
+ recomputeMaxLength = false;
2295
+ }
2296
+ });
2297
+ if (recomputeMaxLength) cm.curOp.updateMaxLine = true;
2298
+ }
2299
+
2300
+ // Adjust frontier, schedule worker
2301
+ doc.frontier = Math.min(doc.frontier, from.line);
2302
+ startWorker(cm, 400);
2303
+
2304
+ var lendiff = change.text.length - (to.line - from.line) - 1;
2305
+ // Remember that these lines changed, for updating the display
2306
+ regChange(cm, from.line, to.line + 1, lendiff);
2307
+
2308
+ if (hasHandler(cm, "change")) {
2309
+ var changeObj = {from: from, to: to,
2310
+ text: change.text,
2311
+ removed: change.removed,
2312
+ origin: change.origin};
2313
+ if (cm.curOp.textChanged) {
2314
+ for (var cur = cm.curOp.textChanged; cur.next; cur = cur.next) {}
2315
+ cur.next = changeObj;
2316
+ } else cm.curOp.textChanged = changeObj;
2317
+ }
2318
+ }
2319
+
2320
+ function replaceRange(doc, code, from, to, origin) {
2321
+ if (!to) to = from;
2322
+ if (posLess(to, from)) { var tmp = to; to = from; from = tmp; }
2323
+ if (typeof code == "string") code = splitLines(code);
2324
+ makeChange(doc, {from: from, to: to, text: code, origin: origin}, null);
2325
+ }
2326
+
2327
+ // POSITION OBJECT
2328
+
2329
+ function Pos(line, ch) {
2330
+ if (!(this instanceof Pos)) return new Pos(line, ch);
2331
+ this.line = line; this.ch = ch;
2332
+ }
2333
+ CodeMirror.Pos = Pos;
2334
+
2335
+ function posEq(a, b) {return a.line == b.line && a.ch == b.ch;}
2336
+ function posLess(a, b) {return a.line < b.line || (a.line == b.line && a.ch < b.ch);}
2337
+ function copyPos(x) {return Pos(x.line, x.ch);}
2338
+
2339
+ // SELECTION
2340
+
2341
+ function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1));}
2342
+ function clipPos(doc, pos) {
2343
+ if (pos.line < doc.first) return Pos(doc.first, 0);
2344
+ var last = doc.first + doc.size - 1;
2345
+ if (pos.line > last) return Pos(last, getLine(doc, last).text.length);
2346
+ return clipToLen(pos, getLine(doc, pos.line).text.length);
2347
+ }
2348
+ function clipToLen(pos, linelen) {
2349
+ var ch = pos.ch;
2350
+ if (ch == null || ch > linelen) return Pos(pos.line, linelen);
2351
+ else if (ch < 0) return Pos(pos.line, 0);
2352
+ else return pos;
2353
+ }
2354
+ function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size;}
2355
+
2356
+ // If shift is held, this will move the selection anchor. Otherwise,
2357
+ // it'll set the whole selection.
2358
+ function extendSelection(doc, pos, other, bias) {
2359
+ if (doc.sel.shift || doc.sel.extend) {
2360
+ var anchor = doc.sel.anchor;
2361
+ if (other) {
2362
+ var posBefore = posLess(pos, anchor);
2363
+ if (posBefore != posLess(other, anchor)) {
2364
+ anchor = pos;
2365
+ pos = other;
2366
+ } else if (posBefore != posLess(pos, other)) {
2367
+ pos = other;
2368
+ }
2369
+ }
2370
+ setSelection(doc, anchor, pos, bias);
2371
+ } else {
2372
+ setSelection(doc, pos, other || pos, bias);
2373
+ }
2374
+ if (doc.cm) doc.cm.curOp.userSelChange = true;
2375
+ }
2376
+
2377
+ function filterSelectionChange(doc, anchor, head) {
2378
+ var obj = {anchor: anchor, head: head};
2379
+ signal(doc, "beforeSelectionChange", doc, obj);
2380
+ if (doc.cm) signal(doc.cm, "beforeSelectionChange", doc.cm, obj);
2381
+ obj.anchor = clipPos(doc, obj.anchor); obj.head = clipPos(doc, obj.head);
2382
+ return obj;
2383
+ }
2384
+
2385
+ // Update the selection. Last two args are only used by
2386
+ // updateDoc, since they have to be expressed in the line
2387
+ // numbers before the update.
2388
+ function setSelection(doc, anchor, head, bias, checkAtomic) {
2389
+ if (!checkAtomic && hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange")) {
2390
+ var filtered = filterSelectionChange(doc, anchor, head);
2391
+ head = filtered.head;
2392
+ anchor = filtered.anchor;
2393
+ }
2394
+
2395
+ var sel = doc.sel;
2396
+ sel.goalColumn = null;
2397
+ // Skip over atomic spans.
2398
+ if (checkAtomic || !posEq(anchor, sel.anchor))
2399
+ anchor = skipAtomic(doc, anchor, bias, checkAtomic != "push");
2400
+ if (checkAtomic || !posEq(head, sel.head))
2401
+ head = skipAtomic(doc, head, bias, checkAtomic != "push");
2402
+
2403
+ if (posEq(sel.anchor, anchor) && posEq(sel.head, head)) return;
2404
+
2405
+ sel.anchor = anchor; sel.head = head;
2406
+ var inv = posLess(head, anchor);
2407
+ sel.from = inv ? head : anchor;
2408
+ sel.to = inv ? anchor : head;
2409
+
2410
+ if (doc.cm)
2411
+ doc.cm.curOp.updateInput = doc.cm.curOp.selectionChanged = true;
2412
+
2413
+ signalLater(doc, "cursorActivity", doc);
2414
+ }
2415
+
2416
+ function reCheckSelection(cm) {
2417
+ setSelection(cm.doc, cm.doc.sel.from, cm.doc.sel.to, null, "push");
2418
+ }
2419
+
2420
+ function skipAtomic(doc, pos, bias, mayClear) {
2421
+ var flipped = false, curPos = pos;
2422
+ var dir = bias || 1;
2423
+ doc.cantEdit = false;
2424
+ search: for (;;) {
2425
+ var line = getLine(doc, curPos.line);
2426
+ if (line.markedSpans) {
2427
+ for (var i = 0; i < line.markedSpans.length; ++i) {
2428
+ var sp = line.markedSpans[i], m = sp.marker;
2429
+ if ((sp.from == null || (m.inclusiveLeft ? sp.from <= curPos.ch : sp.from < curPos.ch)) &&
2430
+ (sp.to == null || (m.inclusiveRight ? sp.to >= curPos.ch : sp.to > curPos.ch))) {
2431
+ if (mayClear) {
2432
+ signal(m, "beforeCursorEnter");
2433
+ if (m.explicitlyCleared) {
2434
+ if (!line.markedSpans) break;
2435
+ else {--i; continue;}
2436
+ }
2437
+ }
2438
+ if (!m.atomic) continue;
2439
+ var newPos = m.find()[dir < 0 ? "from" : "to"];
2440
+ if (posEq(newPos, curPos)) {
2441
+ newPos.ch += dir;
2442
+ if (newPos.ch < 0) {
2443
+ if (newPos.line > doc.first) newPos = clipPos(doc, Pos(newPos.line - 1));
2444
+ else newPos = null;
2445
+ } else if (newPos.ch > line.text.length) {
2446
+ if (newPos.line < doc.first + doc.size - 1) newPos = Pos(newPos.line + 1, 0);
2447
+ else newPos = null;
2448
+ }
2449
+ if (!newPos) {
2450
+ if (flipped) {
2451
+ // Driven in a corner -- no valid cursor position found at all
2452
+ // -- try again *with* clearing, if we didn't already
2453
+ if (!mayClear) return skipAtomic(doc, pos, bias, true);
2454
+ // Otherwise, turn off editing until further notice, and return the start of the doc
2455
+ doc.cantEdit = true;
2456
+ return Pos(doc.first, 0);
2457
+ }
2458
+ flipped = true; newPos = pos; dir = -dir;
2459
+ }
2460
+ }
2461
+ curPos = newPos;
2462
+ continue search;
2463
+ }
2464
+ }
2465
+ }
2466
+ return curPos;
2467
+ }
2468
+ }
2469
+
2470
+ // SCROLLING
2471
+
2472
+ function scrollCursorIntoView(cm) {
2473
+ var coords = scrollPosIntoView(cm, cm.doc.sel.head);
2474
+ if (!cm.state.focused) return;
2475
+ var display = cm.display, box = getRect(display.sizer), doScroll = null, pTop = paddingTop(cm.display);
2476
+ if (coords.top + pTop + box.top < 0) doScroll = true;
2477
+ else if (coords.bottom + pTop + box.top > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false;
2478
+ if (doScroll != null && !phantom) {
2479
+ var hidden = display.cursor.style.display == "none";
2480
+ if (hidden) {
2481
+ display.cursor.style.display = "";
2482
+ display.cursor.style.left = coords.left + "px";
2483
+ display.cursor.style.top = (coords.top - display.viewOffset) + "px";
2484
+ }
2485
+ display.cursor.scrollIntoView(doScroll);
2486
+ if (hidden) display.cursor.style.display = "none";
2487
+ }
2488
+ }
2489
+
2490
+ function scrollPosIntoView(cm, pos, margin) {
2491
+ if (margin == null) margin = 0;
2492
+ for (;;) {
2493
+ var changed = false, coords = cursorCoords(cm, pos);
2494
+ var scrollPos = calculateScrollPos(cm, coords.left, coords.top - margin, coords.left, coords.bottom + margin);
2495
+ var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft;
2496
+ if (scrollPos.scrollTop != null) {
2497
+ setScrollTop(cm, scrollPos.scrollTop);
2498
+ if (Math.abs(cm.doc.scrollTop - startTop) > 1) changed = true;
2499
+ }
2500
+ if (scrollPos.scrollLeft != null) {
2501
+ setScrollLeft(cm, scrollPos.scrollLeft);
2502
+ if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) changed = true;
2503
+ }
2504
+ if (!changed) return coords;
2505
+ }
2506
+ }
2507
+
2508
+ function scrollIntoView(cm, x1, y1, x2, y2) {
2509
+ var scrollPos = calculateScrollPos(cm, x1, y1, x2, y2);
2510
+ if (scrollPos.scrollTop != null) setScrollTop(cm, scrollPos.scrollTop);
2511
+ if (scrollPos.scrollLeft != null) setScrollLeft(cm, scrollPos.scrollLeft);
2512
+ }
2513
+
2514
+ function calculateScrollPos(cm, x1, y1, x2, y2) {
2515
+ var display = cm.display, pt = paddingTop(display);
2516
+ y1 += pt; y2 += pt;
2517
+ var screen = display.scroller.clientHeight - scrollerCutOff, screentop = display.scroller.scrollTop, result = {};
2518
+ var docBottom = cm.doc.height + paddingVert(display);
2519
+ var atTop = y1 < pt + 10, atBottom = y2 + pt > docBottom - 10;
2520
+ if (y1 < screentop) result.scrollTop = atTop ? 0 : Math.max(0, y1);
2521
+ else if (y2 > screentop + screen) result.scrollTop = (atBottom ? docBottom : y2) - screen;
2522
+
2523
+ var screenw = display.scroller.clientWidth - scrollerCutOff, screenleft = display.scroller.scrollLeft;
2524
+ x1 += display.gutters.offsetWidth; x2 += display.gutters.offsetWidth;
2525
+ var gutterw = display.gutters.offsetWidth;
2526
+ var atLeft = x1 < gutterw + 10;
2527
+ if (x1 < screenleft + gutterw || atLeft) {
2528
+ if (atLeft) x1 = 0;
2529
+ result.scrollLeft = Math.max(0, x1 - 10 - gutterw);
2530
+ } else if (x2 > screenw + screenleft - 3) {
2531
+ result.scrollLeft = x2 + 10 - screenw;
2532
+ }
2533
+ return result;
2534
+ }
2535
+
2536
+ function updateScrollPos(cm, left, top) {
2537
+ cm.curOp.updateScrollPos = {scrollLeft: left, scrollTop: top};
2538
+ }
2539
+
2540
+ function addToScrollPos(cm, left, top) {
2541
+ var pos = cm.curOp.updateScrollPos || (cm.curOp.updateScrollPos = {scrollLeft: cm.doc.scrollLeft, scrollTop: cm.doc.scrollTop});
2542
+ var scroll = cm.display.scroller;
2543
+ pos.scrollTop = Math.max(0, Math.min(scroll.scrollHeight - scroll.clientHeight, pos.scrollTop + top));
2544
+ pos.scrollLeft = Math.max(0, Math.min(scroll.scrollWidth - scroll.clientWidth, pos.scrollLeft + left));
2545
+ }
2546
+
2547
+ // API UTILITIES
2548
+
2549
+ function indentLine(cm, n, how, aggressive) {
2550
+ var doc = cm.doc;
2551
+ if (!how) how = "add";
2552
+ if (how == "smart") {
2553
+ if (!cm.doc.mode.indent) how = "prev";
2554
+ else var state = getStateBefore(cm, n);
2555
+ }
2556
+
2557
+ var tabSize = cm.options.tabSize;
2558
+ var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize);
2559
+ var curSpaceString = line.text.match(/^\s*/)[0], indentation;
2560
+ if (how == "smart") {
2561
+ indentation = cm.doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text);
2562
+ if (indentation == Pass) {
2563
+ if (!aggressive) return;
2564
+ how = "prev";
2565
+ }
2566
+ }
2567
+ if (how == "prev") {
2568
+ if (n > doc.first) indentation = countColumn(getLine(doc, n-1).text, null, tabSize);
2569
+ else indentation = 0;
2570
+ } else if (how == "add") {
2571
+ indentation = curSpace + cm.options.indentUnit;
2572
+ } else if (how == "subtract") {
2573
+ indentation = curSpace - cm.options.indentUnit;
2574
+ }
2575
+ indentation = Math.max(0, indentation);
2576
+
2577
+ var indentString = "", pos = 0;
2578
+ if (cm.options.indentWithTabs)
2579
+ for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";}
2580
+ if (pos < indentation) indentString += spaceStr(indentation - pos);
2581
+
2582
+ if (indentString != curSpaceString)
2583
+ replaceRange(cm.doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input");
2584
+ line.stateAfter = null;
2585
+ }
2586
+
2587
+ function changeLine(cm, handle, op) {
2588
+ var no = handle, line = handle, doc = cm.doc;
2589
+ if (typeof handle == "number") line = getLine(doc, clipLine(doc, handle));
2590
+ else no = lineNo(handle);
2591
+ if (no == null) return null;
2592
+ if (op(line, no)) regChange(cm, no, no + 1);
2593
+ else return null;
2594
+ return line;
2595
+ }
2596
+
2597
+ function findPosH(doc, pos, dir, unit, visually) {
2598
+ var line = pos.line, ch = pos.ch;
2599
+ var lineObj = getLine(doc, line);
2600
+ var possible = true;
2601
+ function findNextLine() {
2602
+ var l = line + dir;
2603
+ if (l < doc.first || l >= doc.first + doc.size) return (possible = false);
2604
+ line = l;
2605
+ return lineObj = getLine(doc, l);
2606
+ }
2607
+ function moveOnce(boundToLine) {
2608
+ var next = (visually ? moveVisually : moveLogically)(lineObj, ch, dir, true);
2609
+ if (next == null) {
2610
+ if (!boundToLine && findNextLine()) {
2611
+ if (visually) ch = (dir < 0 ? lineRight : lineLeft)(lineObj);
2612
+ else ch = dir < 0 ? lineObj.text.length : 0;
2613
+ } else return (possible = false);
2614
+ } else ch = next;
2615
+ return true;
2616
+ }
2617
+
2618
+ if (unit == "char") moveOnce();
2619
+ else if (unit == "column") moveOnce(true);
2620
+ else if (unit == "word" || unit == "group") {
2621
+ var sawType = null, group = unit == "group";
2622
+ for (var first = true;; first = false) {
2623
+ if (dir < 0 && !moveOnce(!first)) break;
2624
+ var cur = lineObj.text.charAt(ch) || "\n";
2625
+ var type = isWordChar(cur) ? "w"
2626
+ : !group ? null
2627
+ : /\s/.test(cur) ? null
2628
+ : "p";
2629
+ if (sawType && sawType != type) {
2630
+ if (dir < 0) {dir = 1; moveOnce();}
2631
+ break;
2632
+ }
2633
+ if (type) sawType = type;
2634
+ if (dir > 0 && !moveOnce(!first)) break;
2635
+ }
2636
+ }
2637
+ var result = skipAtomic(doc, Pos(line, ch), dir, true);
2638
+ if (!possible) result.hitSide = true;
2639
+ return result;
2640
+ }
2641
+
2642
+ function findPosV(cm, pos, dir, unit) {
2643
+ var doc = cm.doc, x = pos.left, y;
2644
+ if (unit == "page") {
2645
+ var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight);
2646
+ y = pos.top + dir * (pageSize - (dir < 0 ? 1.5 : .5) * textHeight(cm.display));
2647
+ } else if (unit == "line") {
2648
+ y = dir > 0 ? pos.bottom + 3 : pos.top - 3;
2649
+ }
2650
+ for (;;) {
2651
+ var target = coordsChar(cm, x, y);
2652
+ if (!target.outside) break;
2653
+ if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break; }
2654
+ y += dir * 5;
2655
+ }
2656
+ return target;
2657
+ }
2658
+
2659
+ function findWordAt(line, pos) {
2660
+ var start = pos.ch, end = pos.ch;
2661
+ if (line) {
2662
+ if (pos.after === false || end == line.length) --start; else ++end;
2663
+ var startChar = line.charAt(start);
2664
+ var check = isWordChar(startChar) ? isWordChar
2665
+ : /\s/.test(startChar) ? function(ch) {return /\s/.test(ch);}
2666
+ : function(ch) {return !/\s/.test(ch) && !isWordChar(ch);};
2667
+ while (start > 0 && check(line.charAt(start - 1))) --start;
2668
+ while (end < line.length && check(line.charAt(end))) ++end;
2669
+ }
2670
+ return {from: Pos(pos.line, start), to: Pos(pos.line, end)};
2671
+ }
2672
+
2673
+ function selectLine(cm, line) {
2674
+ extendSelection(cm.doc, Pos(line, 0), clipPos(cm.doc, Pos(line + 1, 0)));
2675
+ }
2676
+
2677
+ // PROTOTYPE
2678
+
2679
+ // The publicly visible API. Note that operation(null, f) means
2680
+ // 'wrap f in an operation, performed on its `this` parameter'
2681
+
2682
+ CodeMirror.prototype = {
2683
+ focus: function(){window.focus(); focusInput(this); onFocus(this); fastPoll(this);},
2684
+
2685
+ setOption: function(option, value) {
2686
+ var options = this.options, old = options[option];
2687
+ if (options[option] == value && option != "mode") return;
2688
+ options[option] = value;
2689
+ if (optionHandlers.hasOwnProperty(option))
2690
+ operation(this, optionHandlers[option])(this, value, old);
2691
+ },
2692
+
2693
+ getOption: function(option) {return this.options[option];},
2694
+ getDoc: function() {return this.doc;},
2695
+
2696
+ addKeyMap: function(map, bottom) {
2697
+ this.state.keyMaps[bottom ? "push" : "unshift"](map);
2698
+ },
2699
+ removeKeyMap: function(map) {
2700
+ var maps = this.state.keyMaps;
2701
+ for (var i = 0; i < maps.length; ++i)
2702
+ if ((typeof map == "string" ? maps[i].name : maps[i]) == map) {
2703
+ maps.splice(i, 1);
2704
+ return true;
2705
+ }
2706
+ },
2707
+
2708
+ addOverlay: operation(null, function(spec, options) {
2709
+ var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec);
2710
+ if (mode.startState) throw new Error("Overlays may not be stateful.");
2711
+ this.state.overlays.push({mode: mode, modeSpec: spec, opaque: options && options.opaque});
2712
+ this.state.modeGen++;
2713
+ regChange(this);
2714
+ }),
2715
+ removeOverlay: operation(null, function(spec) {
2716
+ var overlays = this.state.overlays;
2717
+ for (var i = 0; i < overlays.length; ++i) {
2718
+ if (overlays[i].modeSpec == spec) {
2719
+ overlays.splice(i, 1);
2720
+ this.state.modeGen++;
2721
+ regChange(this);
2722
+ return;
2723
+ }
2724
+ }
2725
+ }),
2726
+
2727
+ indentLine: operation(null, function(n, dir, aggressive) {
2728
+ if (typeof dir != "string") {
2729
+ if (dir == null) dir = this.options.smartIndent ? "smart" : "prev";
2730
+ else dir = dir ? "add" : "subtract";
2731
+ }
2732
+ if (isLine(this.doc, n)) indentLine(this, n, dir, aggressive);
2733
+ }),
2734
+ indentSelection: operation(null, function(how) {
2735
+ var sel = this.doc.sel;
2736
+ if (posEq(sel.from, sel.to)) return indentLine(this, sel.from.line, how);
2737
+ var e = sel.to.line - (sel.to.ch ? 0 : 1);
2738
+ for (var i = sel.from.line; i <= e; ++i) indentLine(this, i, how);
2739
+ }),
2740
+
2741
+ // Fetch the parser token for a given character. Useful for hacks
2742
+ // that want to inspect the mode state (say, for completion).
2743
+ getTokenAt: function(pos) {
2744
+ var doc = this.doc;
2745
+ pos = clipPos(doc, pos);
2746
+ var state = getStateBefore(this, pos.line), mode = this.doc.mode;
2747
+ var line = getLine(doc, pos.line);
2748
+ var stream = new StringStream(line.text, this.options.tabSize);
2749
+ while (stream.pos < pos.ch && !stream.eol()) {
2750
+ stream.start = stream.pos;
2751
+ var style = mode.token(stream, state);
2752
+ }
2753
+ return {start: stream.start,
2754
+ end: stream.pos,
2755
+ string: stream.current(),
2756
+ className: style || null, // Deprecated, use 'type' instead
2757
+ type: style || null,
2758
+ state: state};
2759
+ },
2760
+
2761
+ getStateAfter: function(line) {
2762
+ var doc = this.doc;
2763
+ line = clipLine(doc, line == null ? doc.first + doc.size - 1: line);
2764
+ return getStateBefore(this, line + 1);
2765
+ },
2766
+
2767
+ cursorCoords: function(start, mode) {
2768
+ var pos, sel = this.doc.sel;
2769
+ if (start == null) pos = sel.head;
2770
+ else if (typeof start == "object") pos = clipPos(this.doc, start);
2771
+ else pos = start ? sel.from : sel.to;
2772
+ return cursorCoords(this, pos, mode || "page");
2773
+ },
2774
+
2775
+ charCoords: function(pos, mode) {
2776
+ return charCoords(this, clipPos(this.doc, pos), mode || "page");
2777
+ },
2778
+
2779
+ coordsChar: function(coords, mode) {
2780
+ coords = fromCoordSystem(this, coords, mode || "page");
2781
+ return coordsChar(this, coords.left, coords.top);
2782
+ },
2783
+
2784
+ defaultTextHeight: function() { return textHeight(this.display); },
2785
+ defaultCharWidth: function() { return charWidth(this.display); },
2786
+
2787
+ setGutterMarker: operation(null, function(line, gutterID, value) {
2788
+ return changeLine(this, line, function(line) {
2789
+ var markers = line.gutterMarkers || (line.gutterMarkers = {});
2790
+ markers[gutterID] = value;
2791
+ if (!value && isEmpty(markers)) line.gutterMarkers = null;
2792
+ return true;
2793
+ });
2794
+ }),
2795
+
2796
+ clearGutter: operation(null, function(gutterID) {
2797
+ var cm = this, doc = cm.doc, i = doc.first;
2798
+ doc.iter(function(line) {
2799
+ if (line.gutterMarkers && line.gutterMarkers[gutterID]) {
2800
+ line.gutterMarkers[gutterID] = null;
2801
+ regChange(cm, i, i + 1);
2802
+ if (isEmpty(line.gutterMarkers)) line.gutterMarkers = null;
2803
+ }
2804
+ ++i;
2805
+ });
2806
+ }),
2807
+
2808
+ addLineClass: operation(null, function(handle, where, cls) {
2809
+ return changeLine(this, handle, function(line) {
2810
+ var prop = where == "text" ? "textClass" : where == "background" ? "bgClass" : "wrapClass";
2811
+ if (!line[prop]) line[prop] = cls;
2812
+ else if (new RegExp("\\b" + cls + "\\b").test(line[prop])) return false;
2813
+ else line[prop] += " " + cls;
2814
+ return true;
2815
+ });
2816
+ }),
2817
+
2818
+ removeLineClass: operation(null, function(handle, where, cls) {
2819
+ return changeLine(this, handle, function(line) {
2820
+ var prop = where == "text" ? "textClass" : where == "background" ? "bgClass" : "wrapClass";
2821
+ var cur = line[prop];
2822
+ if (!cur) return false;
2823
+ else if (cls == null) line[prop] = null;
2824
+ else {
2825
+ var upd = cur.replace(new RegExp("^" + cls + "\\b\\s*|\\s*\\b" + cls + "\\b"), "");
2826
+ if (upd == cur) return false;
2827
+ line[prop] = upd || null;
2828
+ }
2829
+ return true;
2830
+ });
2831
+ }),
2832
+
2833
+ addLineWidget: operation(null, function(handle, node, options) {
2834
+ return addLineWidget(this, handle, node, options);
2835
+ }),
2836
+
2837
+ removeLineWidget: function(widget) { widget.clear(); },
2838
+
2839
+ lineInfo: function(line) {
2840
+ if (typeof line == "number") {
2841
+ if (!isLine(this.doc, line)) return null;
2842
+ var n = line;
2843
+ line = getLine(this.doc, line);
2844
+ if (!line) return null;
2845
+ } else {
2846
+ var n = lineNo(line);
2847
+ if (n == null) return null;
2848
+ }
2849
+ return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers,
2850
+ textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass,
2851
+ widgets: line.widgets};
2852
+ },
2853
+
2854
+ getViewport: function() { return {from: this.display.showingFrom, to: this.display.showingTo};},
2855
+
2856
+ addWidget: function(pos, node, scroll, vert, horiz) {
2857
+ var display = this.display;
2858
+ pos = cursorCoords(this, clipPos(this.doc, pos));
2859
+ var top = pos.bottom, left = pos.left;
2860
+ node.style.position = "absolute";
2861
+ display.sizer.appendChild(node);
2862
+ if (vert == "over") {
2863
+ top = pos.top;
2864
+ } else if (vert == "above" || vert == "near") {
2865
+ var vspace = Math.max(display.wrapper.clientHeight, this.doc.height),
2866
+ hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth);
2867
+ // Default to positioning above (if specified and possible); otherwise default to positioning below
2868
+ if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight)
2869
+ top = pos.top - node.offsetHeight;
2870
+ else if (pos.bottom + node.offsetHeight <= vspace)
2871
+ top = pos.bottom;
2872
+ if (left + node.offsetWidth > hspace)
2873
+ left = hspace - node.offsetWidth;
2874
+ }
2875
+ node.style.top = (top + paddingTop(display)) + "px";
2876
+ node.style.left = node.style.right = "";
2877
+ if (horiz == "right") {
2878
+ left = display.sizer.clientWidth - node.offsetWidth;
2879
+ node.style.right = "0px";
2880
+ } else {
2881
+ if (horiz == "left") left = 0;
2882
+ else if (horiz == "middle") left = (display.sizer.clientWidth - node.offsetWidth) / 2;
2883
+ node.style.left = left + "px";
2884
+ }
2885
+ if (scroll)
2886
+ scrollIntoView(this, left, top, left + node.offsetWidth, top + node.offsetHeight);
2887
+ },
2888
+
2889
+ triggerOnKeyDown: operation(null, onKeyDown),
2890
+
2891
+ execCommand: function(cmd) {return commands[cmd](this);},
2892
+
2893
+ findPosH: function(from, amount, unit, visually) {
2894
+ var dir = 1;
2895
+ if (amount < 0) { dir = -1; amount = -amount; }
2896
+ for (var i = 0, cur = clipPos(this.doc, from); i < amount; ++i) {
2897
+ cur = findPosH(this.doc, cur, dir, unit, visually);
2898
+ if (cur.hitSide) break;
2899
+ }
2900
+ return cur;
2901
+ },
2902
+
2903
+ moveH: operation(null, function(dir, unit) {
2904
+ var sel = this.doc.sel, pos;
2905
+ if (sel.shift || sel.extend || posEq(sel.from, sel.to))
2906
+ pos = findPosH(this.doc, sel.head, dir, unit, this.options.rtlMoveVisually);
2907
+ else
2908
+ pos = dir < 0 ? sel.from : sel.to;
2909
+ extendSelection(this.doc, pos, pos, dir);
2910
+ }),
2911
+
2912
+ deleteH: operation(null, function(dir, unit) {
2913
+ var sel = this.doc.sel;
2914
+ if (!posEq(sel.from, sel.to)) replaceRange(this.doc, "", sel.from, sel.to, "+delete");
2915
+ else replaceRange(this.doc, "", sel.from, findPosH(this.doc, sel.head, dir, unit, false), "+delete");
2916
+ this.curOp.userSelChange = true;
2917
+ }),
2918
+
2919
+ findPosV: function(from, amount, unit, goalColumn) {
2920
+ var dir = 1, x = goalColumn;
2921
+ if (amount < 0) { dir = -1; amount = -amount; }
2922
+ for (var i = 0, cur = clipPos(this.doc, from); i < amount; ++i) {
2923
+ var coords = cursorCoords(this, cur, "div");
2924
+ if (x == null) x = coords.left;
2925
+ else coords.left = x;
2926
+ cur = findPosV(this, coords, dir, unit);
2927
+ if (cur.hitSide) break;
2928
+ }
2929
+ return cur;
2930
+ },
2931
+
2932
+ moveV: operation(null, function(dir, unit) {
2933
+ var sel = this.doc.sel;
2934
+ var pos = cursorCoords(this, sel.head, "div");
2935
+ if (sel.goalColumn != null) pos.left = sel.goalColumn;
2936
+ var target = findPosV(this, pos, dir, unit);
2937
+
2938
+ if (unit == "page") addToScrollPos(this, 0, charCoords(this, target, "div").top - pos.top);
2939
+ extendSelection(this.doc, target, target, dir);
2940
+ sel.goalColumn = pos.left;
2941
+ }),
2942
+
2943
+ toggleOverwrite: function() {
2944
+ if (this.state.overwrite = !this.state.overwrite)
2945
+ this.display.cursor.className += " CodeMirror-overwrite";
2946
+ else
2947
+ this.display.cursor.className = this.display.cursor.className.replace(" CodeMirror-overwrite", "");
2948
+ },
2949
+ hasFocus: function() { return this.state.focused; },
2950
+
2951
+ scrollTo: operation(null, function(x, y) {
2952
+ updateScrollPos(this, x, y);
2953
+ }),
2954
+ getScrollInfo: function() {
2955
+ var scroller = this.display.scroller, co = scrollerCutOff;
2956
+ return {left: scroller.scrollLeft, top: scroller.scrollTop,
2957
+ height: scroller.scrollHeight - co, width: scroller.scrollWidth - co,
2958
+ clientHeight: scroller.clientHeight - co, clientWidth: scroller.clientWidth - co};
2959
+ },
2960
+
2961
+ scrollIntoView: function(pos, margin) {
2962
+ if (typeof pos == "number") pos = Pos(pos, 0);
2963
+ if (!pos || pos.line != null) {
2964
+ pos = pos ? clipPos(this.doc, pos) : this.doc.sel.head;
2965
+ scrollPosIntoView(this, pos, margin);
2966
+ } else {
2967
+ scrollIntoView(this, pos.left, pos.top - margin, pos.right, pos.bottom + margin);
2968
+ }
2969
+ },
2970
+
2971
+ setSize: function(width, height) {
2972
+ function interpret(val) {
2973
+ return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val;
2974
+ }
2975
+ if (width != null) this.display.wrapper.style.width = interpret(width);
2976
+ if (height != null) this.display.wrapper.style.height = interpret(height);
2977
+ this.refresh();
2978
+ },
2979
+
2980
+ on: function(type, f) {on(this, type, f);},
2981
+ off: function(type, f) {off(this, type, f);},
2982
+
2983
+ operation: function(f){return runInOp(this, f);},
2984
+
2985
+ refresh: operation(null, function() {
2986
+ clearCaches(this);
2987
+ updateScrollPos(this, this.doc.scrollLeft, this.doc.scrollTop);
2988
+ regChange(this);
2989
+ }),
2990
+
2991
+ swapDoc: operation(null, function(doc) {
2992
+ var old = this.doc;
2993
+ old.cm = null;
2994
+ attachDoc(this, doc);
2995
+ clearCaches(this);
2996
+ updateScrollPos(this, doc.scrollLeft, doc.scrollTop);
2997
+ return old;
2998
+ }),
2999
+
3000
+ getInputField: function(){return this.display.input;},
3001
+ getWrapperElement: function(){return this.display.wrapper;},
3002
+ getScrollerElement: function(){return this.display.scroller;},
3003
+ getGutterElement: function(){return this.display.gutters;}
3004
+ };
3005
+
3006
+ // OPTION DEFAULTS
3007
+
3008
+ var optionHandlers = CodeMirror.optionHandlers = {};
3009
+
3010
+ // The default configuration options.
3011
+ var defaults = CodeMirror.defaults = {};
3012
+
3013
+ function option(name, deflt, handle, notOnInit) {
3014
+ CodeMirror.defaults[name] = deflt;
3015
+ if (handle) optionHandlers[name] =
3016
+ notOnInit ? function(cm, val, old) {if (old != Init) handle(cm, val, old);} : handle;
3017
+ }
3018
+
3019
+ var Init = CodeMirror.Init = {toString: function(){return "CodeMirror.Init";}};
3020
+
3021
+ // These two are, on init, called from the constructor because they
3022
+ // have to be initialized before the editor can start at all.
3023
+ option("value", "", function(cm, val) {
3024
+ cm.setValue(val);
3025
+ }, true);
3026
+ option("mode", null, function(cm, val) {
3027
+ cm.doc.modeOption = val;
3028
+ loadMode(cm);
3029
+ }, true);
3030
+
3031
+ option("indentUnit", 2, loadMode, true);
3032
+ option("indentWithTabs", false);
3033
+ option("smartIndent", true);
3034
+ option("tabSize", 4, function(cm) {
3035
+ loadMode(cm);
3036
+ clearCaches(cm);
3037
+ regChange(cm);
3038
+ }, true);
3039
+ option("electricChars", true);
3040
+ option("rtlMoveVisually", !windows);
3041
+
3042
+ option("theme", "default", function(cm) {
3043
+ themeChanged(cm);
3044
+ guttersChanged(cm);
3045
+ }, true);
3046
+ option("keyMap", "default", keyMapChanged);
3047
+ option("extraKeys", null);
3048
+
3049
+ option("onKeyEvent", null);
3050
+ option("onDragEvent", null);
3051
+
3052
+ option("lineWrapping", false, wrappingChanged, true);
3053
+ option("gutters", [], function(cm) {
3054
+ setGuttersForLineNumbers(cm.options);
3055
+ guttersChanged(cm);
3056
+ }, true);
3057
+ option("fixedGutter", true, function(cm, val) {
3058
+ cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0";
3059
+ cm.refresh();
3060
+ }, true);
3061
+ option("lineNumbers", false, function(cm) {
3062
+ setGuttersForLineNumbers(cm.options);
3063
+ guttersChanged(cm);
3064
+ }, true);
3065
+ option("firstLineNumber", 1, guttersChanged, true);
3066
+ option("lineNumberFormatter", function(integer) {return integer;}, guttersChanged, true);
3067
+ option("showCursorWhenSelecting", false, updateSelection, true);
3068
+
3069
+ option("readOnly", false, function(cm, val) {
3070
+ if (val == "nocursor") {onBlur(cm); cm.display.input.blur();}
3071
+ else if (!val) resetInput(cm, true);
3072
+ });
3073
+ option("dragDrop", true);
3074
+
3075
+ option("cursorBlinkRate", 530);
3076
+ option("cursorHeight", 1);
3077
+ option("workTime", 100);
3078
+ option("workDelay", 100);
3079
+ option("flattenSpans", true);
3080
+ option("pollInterval", 100);
3081
+ option("undoDepth", 40, function(cm, val){cm.doc.history.undoDepth = val;});
3082
+ option("viewportMargin", 10, function(cm){cm.refresh();}, true);
3083
+
3084
+ option("tabindex", null, function(cm, val) {
3085
+ cm.display.input.tabIndex = val || "";
3086
+ });
3087
+ option("autofocus", null);
3088
+
3089
+ // MODE DEFINITION AND QUERYING
3090
+
3091
+ // Known modes, by name and by MIME
3092
+ var modes = CodeMirror.modes = {}, mimeModes = CodeMirror.mimeModes = {};
3093
+
3094
+ CodeMirror.defineMode = function(name, mode) {
3095
+ if (!CodeMirror.defaults.mode && name != "null") CodeMirror.defaults.mode = name;
3096
+ if (arguments.length > 2) {
3097
+ mode.dependencies = [];
3098
+ for (var i = 2; i < arguments.length; ++i) mode.dependencies.push(arguments[i]);
3099
+ }
3100
+ modes[name] = mode;
3101
+ };
3102
+
3103
+ CodeMirror.defineMIME = function(mime, spec) {
3104
+ mimeModes[mime] = spec;
3105
+ };
3106
+
3107
+ CodeMirror.resolveMode = function(spec) {
3108
+ if (typeof spec == "string" && mimeModes.hasOwnProperty(spec))
3109
+ spec = mimeModes[spec];
3110
+ else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec))
3111
+ return CodeMirror.resolveMode("application/xml");
3112
+ if (typeof spec == "string") return {name: spec};
3113
+ else return spec || {name: "null"};
3114
+ };
3115
+
3116
+ CodeMirror.getMode = function(options, spec) {
3117
+ spec = CodeMirror.resolveMode(spec);
3118
+ var mfactory = modes[spec.name];
3119
+ if (!mfactory) return CodeMirror.getMode(options, "text/plain");
3120
+ var modeObj = mfactory(options, spec);
3121
+ if (modeExtensions.hasOwnProperty(spec.name)) {
3122
+ var exts = modeExtensions[spec.name];
3123
+ for (var prop in exts) {
3124
+ if (!exts.hasOwnProperty(prop)) continue;
3125
+ if (modeObj.hasOwnProperty(prop)) modeObj["_" + prop] = modeObj[prop];
3126
+ modeObj[prop] = exts[prop];
3127
+ }
3128
+ }
3129
+ modeObj.name = spec.name;
3130
+ return modeObj;
3131
+ };
3132
+
3133
+ CodeMirror.defineMode("null", function() {
3134
+ return {token: function(stream) {stream.skipToEnd();}};
3135
+ });
3136
+ CodeMirror.defineMIME("text/plain", "null");
3137
+
3138
+ var modeExtensions = CodeMirror.modeExtensions = {};
3139
+ CodeMirror.extendMode = function(mode, properties) {
3140
+ var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});
3141
+ copyObj(properties, exts);
3142
+ };
3143
+
3144
+ // EXTENSIONS
3145
+
3146
+ CodeMirror.defineExtension = function(name, func) {
3147
+ CodeMirror.prototype[name] = func;
3148
+ };
3149
+
3150
+ CodeMirror.defineOption = option;
3151
+
3152
+ var initHooks = [];
3153
+ CodeMirror.defineInitHook = function(f) {initHooks.push(f);};
3154
+
3155
+ // MODE STATE HANDLING
3156
+
3157
+ // Utility functions for working with state. Exported because modes
3158
+ // sometimes need to do this.
3159
+ function copyState(mode, state) {
3160
+ if (state === true) return state;
3161
+ if (mode.copyState) return mode.copyState(state);
3162
+ var nstate = {};
3163
+ for (var n in state) {
3164
+ var val = state[n];
3165
+ if (val instanceof Array) val = val.concat([]);
3166
+ nstate[n] = val;
3167
+ }
3168
+ return nstate;
3169
+ }
3170
+ CodeMirror.copyState = copyState;
3171
+
3172
+ function startState(mode, a1, a2) {
3173
+ return mode.startState ? mode.startState(a1, a2) : true;
3174
+ }
3175
+ CodeMirror.startState = startState;
3176
+
3177
+ CodeMirror.innerMode = function(mode, state) {
3178
+ while (mode.innerMode) {
3179
+ var info = mode.innerMode(state);
3180
+ state = info.state;
3181
+ mode = info.mode;
3182
+ }
3183
+ return info || {mode: mode, state: state};
3184
+ };
3185
+
3186
+ // STANDARD COMMANDS
3187
+
3188
+ var commands = CodeMirror.commands = {
3189
+ selectAll: function(cm) {cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()));},
3190
+ killLine: function(cm) {
3191
+ var from = cm.getCursor(true), to = cm.getCursor(false), sel = !posEq(from, to);
3192
+ if (!sel && cm.getLine(from.line).length == from.ch)
3193
+ cm.replaceRange("", from, Pos(from.line + 1, 0), "+delete");
3194
+ else cm.replaceRange("", from, sel ? to : Pos(from.line), "+delete");
3195
+ },
3196
+ deleteLine: function(cm) {
3197
+ var l = cm.getCursor().line;
3198
+ cm.replaceRange("", Pos(l, 0), Pos(l), "+delete");
3199
+ },
3200
+ undo: function(cm) {cm.undo();},
3201
+ redo: function(cm) {cm.redo();},
3202
+ goDocStart: function(cm) {cm.extendSelection(Pos(cm.firstLine(), 0));},
3203
+ goDocEnd: function(cm) {cm.extendSelection(Pos(cm.lastLine()));},
3204
+ goLineStart: function(cm) {
3205
+ cm.extendSelection(lineStart(cm, cm.getCursor().line));
3206
+ },
3207
+ goLineStartSmart: function(cm) {
3208
+ var cur = cm.getCursor(), start = lineStart(cm, cur.line);
3209
+ var line = cm.getLineHandle(start.line);
3210
+ var order = getOrder(line);
3211
+ if (!order || order[0].level == 0) {
3212
+ var firstNonWS = Math.max(0, line.text.search(/\S/));
3213
+ var inWS = cur.line == start.line && cur.ch <= firstNonWS && cur.ch;
3214
+ cm.extendSelection(Pos(start.line, inWS ? 0 : firstNonWS));
3215
+ } else cm.extendSelection(start);
3216
+ },
3217
+ goLineEnd: function(cm) {
3218
+ cm.extendSelection(lineEnd(cm, cm.getCursor().line));
3219
+ },
3220
+ goLineRight: function(cm) {
3221
+ var top = cm.charCoords(cm.getCursor(), "div").top + 5;
3222
+ cm.extendSelection(cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div"));
3223
+ },
3224
+ goLineLeft: function(cm) {
3225
+ var top = cm.charCoords(cm.getCursor(), "div").top + 5;
3226
+ cm.extendSelection(cm.coordsChar({left: 0, top: top}, "div"));
3227
+ },
3228
+ goLineUp: function(cm) {cm.moveV(-1, "line");},
3229
+ goLineDown: function(cm) {cm.moveV(1, "line");},
3230
+ goPageUp: function(cm) {cm.moveV(-1, "page");},
3231
+ goPageDown: function(cm) {cm.moveV(1, "page");},
3232
+ goCharLeft: function(cm) {cm.moveH(-1, "char");},
3233
+ goCharRight: function(cm) {cm.moveH(1, "char");},
3234
+ goColumnLeft: function(cm) {cm.moveH(-1, "column");},
3235
+ goColumnRight: function(cm) {cm.moveH(1, "column");},
3236
+ goWordLeft: function(cm) {cm.moveH(-1, "word");},
3237
+ goGroupRight: function(cm) {cm.moveH(1, "group");},
3238
+ goGroupLeft: function(cm) {cm.moveH(-1, "group");},
3239
+ goWordRight: function(cm) {cm.moveH(1, "word");},
3240
+ delCharBefore: function(cm) {cm.deleteH(-1, "char");},
3241
+ delCharAfter: function(cm) {cm.deleteH(1, "char");},
3242
+ delWordBefore: function(cm) {cm.deleteH(-1, "word");},
3243
+ delWordAfter: function(cm) {cm.deleteH(1, "word");},
3244
+ delGroupBefore: function(cm) {cm.deleteH(-1, "group");},
3245
+ delGroupAfter: function(cm) {cm.deleteH(1, "group");},
3246
+ indentAuto: function(cm) {cm.indentSelection("smart");},
3247
+ indentMore: function(cm) {cm.indentSelection("add");},
3248
+ indentLess: function(cm) {cm.indentSelection("subtract");},
3249
+ insertTab: function(cm) {cm.replaceSelection("\t", "end", "+input");},
3250
+ defaultTab: function(cm) {
3251
+ if (cm.somethingSelected()) cm.indentSelection("add");
3252
+ else cm.replaceSelection("\t", "end", "+input");
3253
+ },
3254
+ transposeChars: function(cm) {
3255
+ var cur = cm.getCursor(), line = cm.getLine(cur.line);
3256
+ if (cur.ch > 0 && cur.ch < line.length - 1)
3257
+ cm.replaceRange(line.charAt(cur.ch) + line.charAt(cur.ch - 1),
3258
+ Pos(cur.line, cur.ch - 1), Pos(cur.line, cur.ch + 1));
3259
+ },
3260
+ newlineAndIndent: function(cm) {
3261
+ operation(cm, function() {
3262
+ cm.replaceSelection("\n", "end", "+input");
3263
+ cm.indentLine(cm.getCursor().line, null, true);
3264
+ })();
3265
+ },
3266
+ toggleOverwrite: function(cm) {cm.toggleOverwrite();}
3267
+ };
3268
+
3269
+ // STANDARD KEYMAPS
3270
+
3271
+ var keyMap = CodeMirror.keyMap = {};
3272
+ keyMap.basic = {
3273
+ "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
3274
+ "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
3275
+ "Delete": "delCharAfter", "Backspace": "delCharBefore", "Tab": "defaultTab", "Shift-Tab": "indentAuto",
3276
+ "Enter": "newlineAndIndent", "Insert": "toggleOverwrite"
3277
+ };
3278
+ // Note that the save and find-related commands aren't defined by
3279
+ // default. Unknown commands are simply ignored.
3280
+ keyMap.pcDefault = {
3281
+ "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
3282
+ "Ctrl-Home": "goDocStart", "Alt-Up": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Down": "goDocEnd",
3283
+ "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
3284
+ "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find",
3285
+ "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
3286
+ "Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
3287
+ fallthrough: "basic"
3288
+ };
3289
+ keyMap.macDefault = {
3290
+ "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
3291
+ "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft",
3292
+ "Alt-Right": "goGroupRight", "Cmd-Left": "goLineStart", "Cmd-Right": "goLineEnd", "Alt-Backspace": "delGroupBefore",
3293
+ "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find",
3294
+ "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
3295
+ "Cmd-[": "indentLess", "Cmd-]": "indentMore",
3296
+ fallthrough: ["basic", "emacsy"]
3297
+ };
3298
+ keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault;
3299
+ keyMap.emacsy = {
3300
+ "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
3301
+ "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
3302
+ "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore",
3303
+ "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars"
3304
+ };
3305
+
3306
+ // KEYMAP DISPATCH
3307
+
3308
+ function getKeyMap(val) {
3309
+ if (typeof val == "string") return keyMap[val];
3310
+ else return val;
3311
+ }
3312
+
3313
+ function lookupKey(name, maps, handle) {
3314
+ function lookup(map) {
3315
+ map = getKeyMap(map);
3316
+ var found = map[name];
3317
+ if (found === false) return "stop";
3318
+ if (found != null && handle(found)) return true;
3319
+ if (map.nofallthrough) return "stop";
3320
+
3321
+ var fallthrough = map.fallthrough;
3322
+ if (fallthrough == null) return false;
3323
+ if (Object.prototype.toString.call(fallthrough) != "[object Array]")
3324
+ return lookup(fallthrough);
3325
+ for (var i = 0, e = fallthrough.length; i < e; ++i) {
3326
+ var done = lookup(fallthrough[i]);
3327
+ if (done) return done;
3328
+ }
3329
+ return false;
3330
+ }
3331
+
3332
+ for (var i = 0; i < maps.length; ++i) {
3333
+ var done = lookup(maps[i]);
3334
+ if (done) return done;
3335
+ }
3336
+ }
3337
+ function isModifierKey(event) {
3338
+ var name = keyNames[event.keyCode];
3339
+ return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod";
3340
+ }
3341
+ function keyName(event, noShift) {
3342
+ var name = keyNames[event.keyCode];
3343
+ if (name == null || event.altGraphKey) return false;
3344
+ if (event.altKey) name = "Alt-" + name;
3345
+ if (flipCtrlCmd ? event.metaKey : event.ctrlKey) name = "Ctrl-" + name;
3346
+ if (flipCtrlCmd ? event.ctrlKey : event.metaKey) name = "Cmd-" + name;
3347
+ if (!noShift && event.shiftKey) name = "Shift-" + name;
3348
+ return name;
3349
+ }
3350
+ CodeMirror.lookupKey = lookupKey;
3351
+ CodeMirror.isModifierKey = isModifierKey;
3352
+ CodeMirror.keyName = keyName;
3353
+
3354
+ // FROMTEXTAREA
3355
+
3356
+ CodeMirror.fromTextArea = function(textarea, options) {
3357
+ if (!options) options = {};
3358
+ options.value = textarea.value;
3359
+ if (!options.tabindex && textarea.tabindex)
3360
+ options.tabindex = textarea.tabindex;
3361
+ if (!options.placeholder && textarea.placeholder)
3362
+ options.placeholder = textarea.placeholder;
3363
+ // Set autofocus to true if this textarea is focused, or if it has
3364
+ // autofocus and no other element is focused.
3365
+ if (options.autofocus == null) {
3366
+ var hasFocus = document.body;
3367
+ // doc.activeElement occasionally throws on IE
3368
+ try { hasFocus = document.activeElement; } catch(e) {}
3369
+ options.autofocus = hasFocus == textarea ||
3370
+ textarea.getAttribute("autofocus") != null && hasFocus == document.body;
3371
+ }
3372
+
3373
+ function save() {textarea.value = cm.getValue();}
3374
+ if (textarea.form) {
3375
+ on(textarea.form, "submit", save);
3376
+ // Deplorable hack to make the submit method do the right thing.
3377
+ if (!options.leaveSubmitMethodAlone) {
3378
+ var form = textarea.form, realSubmit = form.submit;
3379
+ try {
3380
+ var wrappedSubmit = form.submit = function() {
3381
+ save();
3382
+ form.submit = realSubmit;
3383
+ form.submit();
3384
+ form.submit = wrappedSubmit;
3385
+ };
3386
+ } catch(e) {}
3387
+ }
3388
+ }
3389
+
3390
+ textarea.style.display = "none";
3391
+ var cm = CodeMirror(function(node) {
3392
+ textarea.parentNode.insertBefore(node, textarea.nextSibling);
3393
+ }, options);
3394
+ cm.save = save;
3395
+ cm.getTextArea = function() { return textarea; };
3396
+ cm.toTextArea = function() {
3397
+ save();
3398
+ textarea.parentNode.removeChild(cm.getWrapperElement());
3399
+ textarea.style.display = "";
3400
+ if (textarea.form) {
3401
+ off(textarea.form, "submit", save);
3402
+ if (typeof textarea.form.submit == "function")
3403
+ textarea.form.submit = realSubmit;
3404
+ }
3405
+ };
3406
+ return cm;
3407
+ };
3408
+
3409
+ // STRING STREAM
3410
+
3411
+ // Fed to the mode parsers, provides helper functions to make
3412
+ // parsers more succinct.
3413
+
3414
+ // The character stream used by a mode's parser.
3415
+ function StringStream(string, tabSize) {
3416
+ this.pos = this.start = 0;
3417
+ this.string = string;
3418
+ this.tabSize = tabSize || 8;
3419
+ this.lastColumnPos = this.lastColumnValue = 0;
3420
+ }
3421
+
3422
+ StringStream.prototype = {
3423
+ eol: function() {return this.pos >= this.string.length;},
3424
+ sol: function() {return this.pos == 0;},
3425
+ peek: function() {return this.string.charAt(this.pos) || undefined;},
3426
+ next: function() {
3427
+ if (this.pos < this.string.length)
3428
+ return this.string.charAt(this.pos++);
3429
+ },
3430
+ eat: function(match) {
3431
+ var ch = this.string.charAt(this.pos);
3432
+ if (typeof match == "string") var ok = ch == match;
3433
+ else var ok = ch && (match.test ? match.test(ch) : match(ch));
3434
+ if (ok) {++this.pos; return ch;}
3435
+ },
3436
+ eatWhile: function(match) {
3437
+ var start = this.pos;
3438
+ while (this.eat(match)){}
3439
+ return this.pos > start;
3440
+ },
3441
+ eatSpace: function() {
3442
+ var start = this.pos;
3443
+ while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) ++this.pos;
3444
+ return this.pos > start;
3445
+ },
3446
+ skipToEnd: function() {this.pos = this.string.length;},
3447
+ skipTo: function(ch) {
3448
+ var found = this.string.indexOf(ch, this.pos);
3449
+ if (found > -1) {this.pos = found; return true;}
3450
+ },
3451
+ backUp: function(n) {this.pos -= n;},
3452
+ column: function() {
3453
+ if (this.lastColumnPos < this.start) {
3454
+ this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue);
3455
+ this.lastColumnPos = this.start;
3456
+ }
3457
+ return this.lastColumnValue;
3458
+ },
3459
+ indentation: function() {return countColumn(this.string, null, this.tabSize);},
3460
+ match: function(pattern, consume, caseInsensitive) {
3461
+ if (typeof pattern == "string") {
3462
+ var cased = function(str) {return caseInsensitive ? str.toLowerCase() : str;};
3463
+ var substr = this.string.substr(this.pos, pattern.length);
3464
+ if (cased(substr) == cased(pattern)) {
3465
+ if (consume !== false) this.pos += pattern.length;
3466
+ return true;
3467
+ }
3468
+ } else {
3469
+ var match = this.string.slice(this.pos).match(pattern);
3470
+ if (match && match.index > 0) return null;
3471
+ if (match && consume !== false) this.pos += match[0].length;
3472
+ return match;
3473
+ }
3474
+ },
3475
+ current: function(){return this.string.slice(this.start, this.pos);}
3476
+ };
3477
+ CodeMirror.StringStream = StringStream;
3478
+
3479
+ // TEXTMARKERS
3480
+
3481
+ function TextMarker(doc, type) {
3482
+ this.lines = [];
3483
+ this.type = type;
3484
+ this.doc = doc;
3485
+ }
3486
+ CodeMirror.TextMarker = TextMarker;
3487
+
3488
+ TextMarker.prototype.clear = function() {
3489
+ if (this.explicitlyCleared) return;
3490
+ var cm = this.doc.cm, withOp = cm && !cm.curOp;
3491
+ if (withOp) startOperation(cm);
3492
+ var min = null, max = null;
3493
+ for (var i = 0; i < this.lines.length; ++i) {
3494
+ var line = this.lines[i];
3495
+ var span = getMarkedSpanFor(line.markedSpans, this);
3496
+ if (span.to != null) max = lineNo(line);
3497
+ line.markedSpans = removeMarkedSpan(line.markedSpans, span);
3498
+ if (span.from != null)
3499
+ min = lineNo(line);
3500
+ else if (this.collapsed && !lineIsHidden(this.doc, line) && cm)
3501
+ updateLineHeight(line, textHeight(cm.display));
3502
+ }
3503
+ if (cm && this.collapsed && !cm.options.lineWrapping) for (var i = 0; i < this.lines.length; ++i) {
3504
+ var visual = visualLine(cm.doc, this.lines[i]), len = lineLength(cm.doc, visual);
3505
+ if (len > cm.display.maxLineLength) {
3506
+ cm.display.maxLine = visual;
3507
+ cm.display.maxLineLength = len;
3508
+ cm.display.maxLineChanged = true;
3509
+ }
3510
+ }
3511
+
3512
+ if (min != null && cm) regChange(cm, min, max + 1);
3513
+ this.lines.length = 0;
3514
+ this.explicitlyCleared = true;
3515
+ if (this.collapsed && this.doc.cantEdit) {
3516
+ this.doc.cantEdit = false;
3517
+ if (cm) reCheckSelection(cm);
3518
+ }
3519
+ if (withOp) endOperation(cm);
3520
+ signalLater(this, "clear");
3521
+ };
3522
+
3523
+ TextMarker.prototype.find = function() {
3524
+ var from, to;
3525
+ for (var i = 0; i < this.lines.length; ++i) {
3526
+ var line = this.lines[i];
3527
+ var span = getMarkedSpanFor(line.markedSpans, this);
3528
+ if (span.from != null || span.to != null) {
3529
+ var found = lineNo(line);
3530
+ if (span.from != null) from = Pos(found, span.from);
3531
+ if (span.to != null) to = Pos(found, span.to);
3532
+ }
3533
+ }
3534
+ if (this.type == "bookmark") return from;
3535
+ return from && {from: from, to: to};
3536
+ };
3537
+
3538
+ TextMarker.prototype.getOptions = function(copyWidget) {
3539
+ var repl = this.replacedWith;
3540
+ return {className: this.className,
3541
+ inclusiveLeft: this.inclusiveLeft, inclusiveRight: this.inclusiveRight,
3542
+ atomic: this.atomic,
3543
+ collapsed: this.collapsed,
3544
+ replacedWith: copyWidget ? repl && repl.cloneNode(true) : repl,
3545
+ readOnly: this.readOnly,
3546
+ startStyle: this.startStyle, endStyle: this.endStyle};
3547
+ };
3548
+
3549
+ TextMarker.prototype.attachLine = function(line) {
3550
+ if (!this.lines.length && this.doc.cm) {
3551
+ var op = this.doc.cm.curOp;
3552
+ if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1)
3553
+ (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this);
3554
+ }
3555
+ this.lines.push(line);
3556
+ };
3557
+ TextMarker.prototype.detachLine = function(line) {
3558
+ this.lines.splice(indexOf(this.lines, line), 1);
3559
+ if (!this.lines.length && this.doc.cm) {
3560
+ var op = this.doc.cm.curOp;
3561
+ (op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this);
3562
+ }
3563
+ };
3564
+
3565
+ function markText(doc, from, to, options, type) {
3566
+ if (options && options.shared) return markTextShared(doc, from, to, options, type);
3567
+ if (doc.cm && !doc.cm.curOp) return operation(doc.cm, markText)(doc, from, to, options, type);
3568
+
3569
+ var marker = new TextMarker(doc, type);
3570
+ if (type == "range" && !posLess(from, to)) return marker;
3571
+ if (options) copyObj(options, marker);
3572
+ if (marker.replacedWith) {
3573
+ marker.collapsed = true;
3574
+ marker.replacedWith = elt("span", [marker.replacedWith], "CodeMirror-widget");
3575
+ }
3576
+ if (marker.collapsed) sawCollapsedSpans = true;
3577
+
3578
+ var curLine = from.line, size = 0, collapsedAtStart, collapsedAtEnd, cm = doc.cm, updateMaxLine;
3579
+ doc.iter(curLine, to.line + 1, function(line) {
3580
+ if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(doc, line) == cm.display.maxLine)
3581
+ updateMaxLine = true;
3582
+ var span = {from: null, to: null, marker: marker};
3583
+ size += line.text.length;
3584
+ if (curLine == from.line) {span.from = from.ch; size -= from.ch;}
3585
+ if (curLine == to.line) {span.to = to.ch; size -= line.text.length - to.ch;}
3586
+ if (marker.collapsed) {
3587
+ if (curLine == to.line) collapsedAtEnd = collapsedSpanAt(line, to.ch);
3588
+ if (curLine == from.line) collapsedAtStart = collapsedSpanAt(line, from.ch);
3589
+ else updateLineHeight(line, 0);
3590
+ }
3591
+ addMarkedSpan(line, span);
3592
+ ++curLine;
3593
+ });
3594
+ if (marker.collapsed) doc.iter(from.line, to.line + 1, function(line) {
3595
+ if (lineIsHidden(doc, line)) updateLineHeight(line, 0);
3596
+ });
3597
+
3598
+ if (marker.clearOnEnter) on(marker, "beforeCursorEnter", function() { marker.clear(); });
3599
+
3600
+ if (marker.readOnly) {
3601
+ sawReadOnlySpans = true;
3602
+ if (doc.history.done.length || doc.history.undone.length)
3603
+ doc.clearHistory();
3604
+ }
3605
+ if (marker.collapsed) {
3606
+ if (collapsedAtStart != collapsedAtEnd)
3607
+ throw new Error("Inserting collapsed marker overlapping an existing one");
3608
+ marker.size = size;
3609
+ marker.atomic = true;
3610
+ }
3611
+ if (cm) {
3612
+ if (updateMaxLine) cm.curOp.updateMaxLine = true;
3613
+ if (marker.className || marker.startStyle || marker.endStyle || marker.collapsed)
3614
+ regChange(cm, from.line, to.line + 1);
3615
+ if (marker.atomic) reCheckSelection(cm);
3616
+ }
3617
+ return marker;
3618
+ }
3619
+
3620
+ // SHARED TEXTMARKERS
3621
+
3622
+ function SharedTextMarker(markers, primary) {
3623
+ this.markers = markers;
3624
+ this.primary = primary;
3625
+ for (var i = 0, me = this; i < markers.length; ++i) {
3626
+ markers[i].parent = this;
3627
+ on(markers[i], "clear", function(){me.clear();});
3628
+ }
3629
+ }
3630
+ CodeMirror.SharedTextMarker = SharedTextMarker;
3631
+
3632
+ SharedTextMarker.prototype.clear = function() {
3633
+ if (this.explicitlyCleared) return;
3634
+ this.explicitlyCleared = true;
3635
+ for (var i = 0; i < this.markers.length; ++i)
3636
+ this.markers[i].clear();
3637
+ signalLater(this, "clear");
3638
+ };
3639
+ SharedTextMarker.prototype.find = function() {
3640
+ return this.primary.find();
3641
+ };
3642
+ SharedTextMarker.prototype.getOptions = function(copyWidget) {
3643
+ var inner = this.primary.getOptions(copyWidget);
3644
+ inner.shared = true;
3645
+ return inner;
3646
+ };
3647
+
3648
+ function markTextShared(doc, from, to, options, type) {
3649
+ options = copyObj(options);
3650
+ options.shared = false;
3651
+ var markers = [markText(doc, from, to, options, type)], primary = markers[0];
3652
+ var widget = options.replacedWith;
3653
+ linkedDocs(doc, function(doc) {
3654
+ if (widget) options.replacedWith = widget.cloneNode(true);
3655
+ markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type));
3656
+ for (var i = 0; i < doc.linked.length; ++i)
3657
+ if (doc.linked[i].isParent) return;
3658
+ primary = lst(markers);
3659
+ });
3660
+ return new SharedTextMarker(markers, primary);
3661
+ }
3662
+
3663
+ // TEXTMARKER SPANS
3664
+
3665
+ function getMarkedSpanFor(spans, marker) {
3666
+ if (spans) for (var i = 0; i < spans.length; ++i) {
3667
+ var span = spans[i];
3668
+ if (span.marker == marker) return span;
3669
+ }
3670
+ }
3671
+ function removeMarkedSpan(spans, span) {
3672
+ for (var r, i = 0; i < spans.length; ++i)
3673
+ if (spans[i] != span) (r || (r = [])).push(spans[i]);
3674
+ return r;
3675
+ }
3676
+ function addMarkedSpan(line, span) {
3677
+ line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span];
3678
+ span.marker.attachLine(line);
3679
+ }
3680
+
3681
+ function markedSpansBefore(old, startCh, isInsert) {
3682
+ if (old) for (var i = 0, nw; i < old.length; ++i) {
3683
+ var span = old[i], marker = span.marker;
3684
+ var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh);
3685
+ if (startsBefore || marker.type == "bookmark" && span.from == startCh && (!isInsert || !span.marker.insertLeft)) {
3686
+ var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh);
3687
+ (nw || (nw = [])).push({from: span.from,
3688
+ to: endsAfter ? null : span.to,
3689
+ marker: marker});
3690
+ }
3691
+ }
3692
+ return nw;
3693
+ }
3694
+
3695
+ function markedSpansAfter(old, endCh, isInsert) {
3696
+ if (old) for (var i = 0, nw; i < old.length; ++i) {
3697
+ var span = old[i], marker = span.marker;
3698
+ var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh);
3699
+ if (endsAfter || marker.type == "bookmark" && span.from == endCh && (!isInsert || span.marker.insertLeft)) {
3700
+ var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh);
3701
+ (nw || (nw = [])).push({from: startsBefore ? null : span.from - endCh,
3702
+ to: span.to == null ? null : span.to - endCh,
3703
+ marker: marker});
3704
+ }
3705
+ }
3706
+ return nw;
3707
+ }
3708
+
3709
+ function stretchSpansOverChange(doc, change) {
3710
+ var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans;
3711
+ var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans;
3712
+ if (!oldFirst && !oldLast) return null;
3713
+
3714
+ var startCh = change.from.ch, endCh = change.to.ch, isInsert = posEq(change.from, change.to);
3715
+ // Get the spans that 'stick out' on both sides
3716
+ var first = markedSpansBefore(oldFirst, startCh, isInsert);
3717
+ var last = markedSpansAfter(oldLast, endCh, isInsert);
3718
+
3719
+ // Next, merge those two ends
3720
+ var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0);
3721
+ if (first) {
3722
+ // Fix up .to properties of first
3723
+ for (var i = 0; i < first.length; ++i) {
3724
+ var span = first[i];
3725
+ if (span.to == null) {
3726
+ var found = getMarkedSpanFor(last, span.marker);
3727
+ if (!found) span.to = startCh;
3728
+ else if (sameLine) span.to = found.to == null ? null : found.to + offset;
3729
+ }
3730
+ }
3731
+ }
3732
+ if (last) {
3733
+ // Fix up .from in last (or move them into first in case of sameLine)
3734
+ for (var i = 0; i < last.length; ++i) {
3735
+ var span = last[i];
3736
+ if (span.to != null) span.to += offset;
3737
+ if (span.from == null) {
3738
+ var found = getMarkedSpanFor(first, span.marker);
3739
+ if (!found) {
3740
+ span.from = offset;
3741
+ if (sameLine) (first || (first = [])).push(span);
3742
+ }
3743
+ } else {
3744
+ span.from += offset;
3745
+ if (sameLine) (first || (first = [])).push(span);
3746
+ }
3747
+ }
3748
+ }
3749
+
3750
+ var newMarkers = [first];
3751
+ if (!sameLine) {
3752
+ // Fill gap with whole-line-spans
3753
+ var gap = change.text.length - 2, gapMarkers;
3754
+ if (gap > 0 && first)
3755
+ for (var i = 0; i < first.length; ++i)
3756
+ if (first[i].to == null)
3757
+ (gapMarkers || (gapMarkers = [])).push({from: null, to: null, marker: first[i].marker});
3758
+ for (var i = 0; i < gap; ++i)
3759
+ newMarkers.push(gapMarkers);
3760
+ newMarkers.push(last);
3761
+ }
3762
+ return newMarkers;
3763
+ }
3764
+
3765
+ function mergeOldSpans(doc, change) {
3766
+ var old = getOldSpans(doc, change);
3767
+ var stretched = stretchSpansOverChange(doc, change);
3768
+ if (!old) return stretched;
3769
+ if (!stretched) return old;
3770
+
3771
+ for (var i = 0; i < old.length; ++i) {
3772
+ var oldCur = old[i], stretchCur = stretched[i];
3773
+ if (oldCur && stretchCur) {
3774
+ spans: for (var j = 0; j < stretchCur.length; ++j) {
3775
+ var span = stretchCur[j];
3776
+ for (var k = 0; k < oldCur.length; ++k)
3777
+ if (oldCur[k].marker == span.marker) continue spans;
3778
+ oldCur.push(span);
3779
+ }
3780
+ } else if (stretchCur) {
3781
+ old[i] = stretchCur;
3782
+ }
3783
+ }
3784
+ return old;
3785
+ }
3786
+
3787
+ function removeReadOnlyRanges(doc, from, to) {
3788
+ var markers = null;
3789
+ doc.iter(from.line, to.line + 1, function(line) {
3790
+ if (line.markedSpans) for (var i = 0; i < line.markedSpans.length; ++i) {
3791
+ var mark = line.markedSpans[i].marker;
3792
+ if (mark.readOnly && (!markers || indexOf(markers, mark) == -1))
3793
+ (markers || (markers = [])).push(mark);
3794
+ }
3795
+ });
3796
+ if (!markers) return null;
3797
+ var parts = [{from: from, to: to}];
3798
+ for (var i = 0; i < markers.length; ++i) {
3799
+ var mk = markers[i], m = mk.find();
3800
+ for (var j = 0; j < parts.length; ++j) {
3801
+ var p = parts[j];
3802
+ if (posLess(p.to, m.from) || posLess(m.to, p.from)) continue;
3803
+ var newParts = [j, 1];
3804
+ if (posLess(p.from, m.from) || !mk.inclusiveLeft && posEq(p.from, m.from))
3805
+ newParts.push({from: p.from, to: m.from});
3806
+ if (posLess(m.to, p.to) || !mk.inclusiveRight && posEq(p.to, m.to))
3807
+ newParts.push({from: m.to, to: p.to});
3808
+ parts.splice.apply(parts, newParts);
3809
+ j += newParts.length - 1;
3810
+ }
3811
+ }
3812
+ return parts;
3813
+ }
3814
+
3815
+ function collapsedSpanAt(line, ch) {
3816
+ var sps = sawCollapsedSpans && line.markedSpans, found;
3817
+ if (sps) for (var sp, i = 0; i < sps.length; ++i) {
3818
+ sp = sps[i];
3819
+ if (!sp.marker.collapsed) continue;
3820
+ if ((sp.from == null || sp.from < ch) &&
3821
+ (sp.to == null || sp.to > ch) &&
3822
+ (!found || found.width < sp.marker.width))
3823
+ found = sp.marker;
3824
+ }
3825
+ return found;
3826
+ }
3827
+ function collapsedSpanAtStart(line) { return collapsedSpanAt(line, -1); }
3828
+ function collapsedSpanAtEnd(line) { return collapsedSpanAt(line, line.text.length + 1); }
3829
+
3830
+ function visualLine(doc, line) {
3831
+ var merged;
3832
+ while (merged = collapsedSpanAtStart(line))
3833
+ line = getLine(doc, merged.find().from.line);
3834
+ return line;
3835
+ }
3836
+
3837
+ function lineIsHidden(doc, line) {
3838
+ var sps = sawCollapsedSpans && line.markedSpans;
3839
+ if (sps) for (var sp, i = 0; i < sps.length; ++i) {
3840
+ sp = sps[i];
3841
+ if (!sp.marker.collapsed) continue;
3842
+ if (sp.from == null) return true;
3843
+ if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp))
3844
+ return true;
3845
+ }
3846
+ }
3847
+ function lineIsHiddenInner(doc, line, span) {
3848
+ if (span.to == null) {
3849
+ var end = span.marker.find().to, endLine = getLine(doc, end.line);
3850
+ return lineIsHiddenInner(doc, endLine, getMarkedSpanFor(endLine.markedSpans, span.marker));
3851
+ }
3852
+ if (span.marker.inclusiveRight && span.to == line.text.length)
3853
+ return true;
3854
+ for (var sp, i = 0; i < line.markedSpans.length; ++i) {
3855
+ sp = line.markedSpans[i];
3856
+ if (sp.marker.collapsed && sp.from == span.to &&
3857
+ (sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
3858
+ lineIsHiddenInner(doc, line, sp)) return true;
3859
+ }
3860
+ }
3861
+
3862
+ function detachMarkedSpans(line) {
3863
+ var spans = line.markedSpans;
3864
+ if (!spans) return;
3865
+ for (var i = 0; i < spans.length; ++i)
3866
+ spans[i].marker.detachLine(line);
3867
+ line.markedSpans = null;
3868
+ }
3869
+
3870
+ function attachMarkedSpans(line, spans) {
3871
+ if (!spans) return;
3872
+ for (var i = 0; i < spans.length; ++i)
3873
+ spans[i].marker.attachLine(line);
3874
+ line.markedSpans = spans;
3875
+ }
3876
+
3877
+ // LINE WIDGETS
3878
+
3879
+ var LineWidget = CodeMirror.LineWidget = function(cm, node, options) {
3880
+ for (var opt in options) if (options.hasOwnProperty(opt))
3881
+ this[opt] = options[opt];
3882
+ this.cm = cm;
3883
+ this.node = node;
3884
+ };
3885
+ function widgetOperation(f) {
3886
+ return function() {
3887
+ var withOp = !this.cm.curOp;
3888
+ if (withOp) startOperation(this.cm);
3889
+ try {var result = f.apply(this, arguments);}
3890
+ finally {if (withOp) endOperation(this.cm);}
3891
+ return result;
3892
+ };
3893
+ }
3894
+ LineWidget.prototype.clear = widgetOperation(function() {
3895
+ var ws = this.line.widgets, no = lineNo(this.line);
3896
+ if (no == null || !ws) return;
3897
+ for (var i = 0; i < ws.length; ++i) if (ws[i] == this) ws.splice(i--, 1);
3898
+ if (!ws.length) this.line.widgets = null;
3899
+ updateLineHeight(this.line, Math.max(0, this.line.height - widgetHeight(this)));
3900
+ regChange(this.cm, no, no + 1);
3901
+ });
3902
+ LineWidget.prototype.changed = widgetOperation(function() {
3903
+ var oldH = this.height;
3904
+ this.height = null;
3905
+ var diff = widgetHeight(this) - oldH;
3906
+ if (!diff) return;
3907
+ updateLineHeight(this.line, this.line.height + diff);
3908
+ var no = lineNo(this.line);
3909
+ regChange(this.cm, no, no + 1);
3910
+ });
3911
+
3912
+ function widgetHeight(widget) {
3913
+ if (widget.height != null) return widget.height;
3914
+ if (!widget.node.parentNode || widget.node.parentNode.nodeType != 1)
3915
+ removeChildrenAndAdd(widget.cm.display.measure, elt("div", [widget.node], null, "position: relative"));
3916
+ return widget.height = widget.node.offsetHeight;
3917
+ }
3918
+
3919
+ function addLineWidget(cm, handle, node, options) {
3920
+ var widget = new LineWidget(cm, node, options);
3921
+ if (widget.noHScroll) cm.display.alignWidgets = true;
3922
+ changeLine(cm, handle, function(line) {
3923
+ (line.widgets || (line.widgets = [])).push(widget);
3924
+ widget.line = line;
3925
+ if (!lineIsHidden(cm.doc, line) || widget.showIfHidden) {
3926
+ var aboveVisible = heightAtLine(cm, line) < cm.display.scroller.scrollTop;
3927
+ updateLineHeight(line, line.height + widgetHeight(widget));
3928
+ if (aboveVisible) addToScrollPos(cm, 0, widget.height);
3929
+ }
3930
+ return true;
3931
+ });
3932
+ return widget;
3933
+ }
3934
+
3935
+ // LINE DATA STRUCTURE
3936
+
3937
+ // Line objects. These hold state related to a line, including
3938
+ // highlighting info (the styles array).
3939
+ function makeLine(text, markedSpans, estimateHeight) {
3940
+ var line = {text: text};
3941
+ attachMarkedSpans(line, markedSpans);
3942
+ line.height = estimateHeight ? estimateHeight(line) : 1;
3943
+ return line;
3944
+ }
3945
+
3946
+ function updateLine(line, text, markedSpans, estimateHeight) {
3947
+ line.text = text;
3948
+ if (line.stateAfter) line.stateAfter = null;
3949
+ if (line.styles) line.styles = null;
3950
+ if (line.order != null) line.order = null;
3951
+ detachMarkedSpans(line);
3952
+ attachMarkedSpans(line, markedSpans);
3953
+ var estHeight = estimateHeight ? estimateHeight(line) : 1;
3954
+ if (estHeight != line.height) updateLineHeight(line, estHeight);
3955
+ }
3956
+
3957
+ function cleanUpLine(line) {
3958
+ line.parent = null;
3959
+ detachMarkedSpans(line);
3960
+ }
3961
+
3962
+ // Run the given mode's parser over a line, update the styles
3963
+ // array, which contains alternating fragments of text and CSS
3964
+ // classes.
3965
+ function runMode(cm, text, mode, state, f) {
3966
+ var flattenSpans = mode.flattenSpans;
3967
+ if (flattenSpans == null) flattenSpans = cm.options.flattenSpans;
3968
+ var curText = "", curStyle = null;
3969
+ var stream = new StringStream(text, cm.options.tabSize);
3970
+ if (text == "" && mode.blankLine) mode.blankLine(state);
3971
+ while (!stream.eol()) {
3972
+ var style = mode.token(stream, state);
3973
+ if (stream.pos > 5000) {
3974
+ flattenSpans = false;
3975
+ // Webkit seems to refuse to render text nodes longer than 57444 characters
3976
+ stream.pos = Math.min(text.length, stream.start + 50000);
3977
+ style = null;
3978
+ }
3979
+ var substr = stream.current();
3980
+ stream.start = stream.pos;
3981
+ if (!flattenSpans || curStyle != style) {
3982
+ if (curText) f(curText, curStyle);
3983
+ curText = substr; curStyle = style;
3984
+ } else curText = curText + substr;
3985
+ }
3986
+ if (curText) f(curText, curStyle);
3987
+ }
3988
+
3989
+ function highlightLine(cm, line, state) {
3990
+ // A styles array always starts with a number identifying the
3991
+ // mode/overlays that it is based on (for easy invalidation).
3992
+ var st = [cm.state.modeGen];
3993
+ // Compute the base array of styles
3994
+ runMode(cm, line.text, cm.doc.mode, state, function(txt, style) {st.push(txt, style);});
3995
+
3996
+ // Run overlays, adjust style array.
3997
+ for (var o = 0; o < cm.state.overlays.length; ++o) {
3998
+ var overlay = cm.state.overlays[o], i = 1;
3999
+ runMode(cm, line.text, overlay.mode, true, function(txt, style) {
4000
+ var start = i, len = txt.length;
4001
+ // Ensure there's a token end at the current position, and that i points at it
4002
+ while (len) {
4003
+ var cur = st[i], len_ = cur.length;
4004
+ if (len_ <= len) {
4005
+ len -= len_;
4006
+ } else {
4007
+ st.splice(i, 1, cur.slice(0, len), st[i+1], cur.slice(len));
4008
+ len = 0;
4009
+ }
4010
+ i += 2;
4011
+ }
4012
+ if (!style) return;
4013
+ if (overlay.opaque) {
4014
+ st.splice(start, i - start, txt, style);
4015
+ i = start + 2;
4016
+ } else {
4017
+ for (; start < i; start += 2) {
4018
+ var cur = st[start+1];
4019
+ st[start+1] = cur ? cur + " " + style : style;
4020
+ }
4021
+ }
4022
+ });
4023
+ }
4024
+
4025
+ return st;
4026
+ }
4027
+
4028
+ function getLineStyles(cm, line) {
4029
+ if (!line.styles || line.styles[0] != cm.state.modeGen)
4030
+ line.styles = highlightLine(cm, line, line.stateAfter = getStateBefore(cm, lineNo(line)));
4031
+ return line.styles;
4032
+ }
4033
+
4034
+ // Lightweight form of highlight -- proceed over this line and
4035
+ // update state, but don't save a style array.
4036
+ function processLine(cm, line, state) {
4037
+ var mode = cm.doc.mode;
4038
+ var stream = new StringStream(line.text, cm.options.tabSize);
4039
+ if (line.text == "" && mode.blankLine) mode.blankLine(state);
4040
+ while (!stream.eol() && stream.pos <= 5000) {
4041
+ mode.token(stream, state);
4042
+ stream.start = stream.pos;
4043
+ }
4044
+ }
4045
+
4046
+ var styleToClassCache = {};
4047
+ function styleToClass(style) {
4048
+ if (!style) return null;
4049
+ return styleToClassCache[style] ||
4050
+ (styleToClassCache[style] = "cm-" + style.replace(/ +/g, " cm-"));
4051
+ }
4052
+
4053
+ function lineContent(cm, realLine, measure) {
4054
+ var merged, line = realLine, lineBefore, sawBefore, simple = true;
4055
+ while (merged = collapsedSpanAtStart(line)) {
4056
+ simple = false;
4057
+ line = getLine(cm.doc, merged.find().from.line);
4058
+ if (!lineBefore) lineBefore = line;
4059
+ }
4060
+
4061
+ var builder = {pre: elt("pre"), col: 0, pos: 0, display: !measure,
4062
+ measure: null, addedOne: false, cm: cm};
4063
+ if (line.textClass) builder.pre.className = line.textClass;
4064
+
4065
+ do {
4066
+ builder.measure = line == realLine && measure;
4067
+ builder.pos = 0;
4068
+ builder.addToken = builder.measure ? buildTokenMeasure : buildToken;
4069
+ if ((ie || webkit) && cm.getOption("lineWrapping"))
4070
+ builder.addToken = buildTokenSplitSpaces(builder.addToken);
4071
+ if (measure && sawBefore && line != realLine && !builder.addedOne) {
4072
+ measure[0] = builder.pre.appendChild(zeroWidthElement(cm.display.measure));
4073
+ builder.addedOne = true;
4074
+ }
4075
+ var next = insertLineContent(line, builder, getLineStyles(cm, line));
4076
+ sawBefore = line == lineBefore;
4077
+ if (next) {
4078
+ line = getLine(cm.doc, next.to.line);
4079
+ simple = false;
4080
+ }
4081
+ } while (next);
4082
+
4083
+ if (measure && !builder.addedOne)
4084
+ measure[0] = builder.pre.appendChild(simple ? elt("span", "\u00a0") : zeroWidthElement(cm.display.measure));
4085
+ if (!builder.pre.firstChild && !lineIsHidden(cm.doc, realLine))
4086
+ builder.pre.appendChild(document.createTextNode("\u00a0"));
4087
+
4088
+ var order;
4089
+ // Work around problem with the reported dimensions of single-char
4090
+ // direction spans on IE (issue #1129). See also the comment in
4091
+ // cursorCoords.
4092
+ if (measure && ie && (order = getOrder(line))) {
4093
+ var l = order.length - 1;
4094
+ if (order[l].from == order[l].to) --l;
4095
+ var last = order[l], prev = order[l - 1];
4096
+ if (last.from + 1 == last.to && prev && last.level < prev.level) {
4097
+ var span = measure[builder.pos - 1];
4098
+ if (span) span.parentNode.insertBefore(span.measureRight = zeroWidthElement(cm.display.measure),
4099
+ span.nextSibling);
4100
+ }
4101
+ }
4102
+
4103
+ signal(cm, "renderLine", cm, realLine, builder.pre);
4104
+ return builder.pre;
4105
+ }
4106
+
4107
+ var tokenSpecialChars = /[\t\u0000-\u0019\u00ad\u200b\u2028\u2029\uFEFF]/g;
4108
+ function buildToken(builder, text, style, startStyle, endStyle) {
4109
+ if (!text) return;
4110
+ if (!tokenSpecialChars.test(text)) {
4111
+ builder.col += text.length;
4112
+ var content = document.createTextNode(text);
4113
+ } else {
4114
+ var content = document.createDocumentFragment(), pos = 0;
4115
+ while (true) {
4116
+ tokenSpecialChars.lastIndex = pos;
4117
+ var m = tokenSpecialChars.exec(text);
4118
+ var skipped = m ? m.index - pos : text.length - pos;
4119
+ if (skipped) {
4120
+ content.appendChild(document.createTextNode(text.slice(pos, pos + skipped)));
4121
+ builder.col += skipped;
4122
+ }
4123
+ if (!m) break;
4124
+ pos += skipped + 1;
4125
+ if (m[0] == "\t") {
4126
+ var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize;
4127
+ content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab"));
4128
+ builder.col += tabWidth;
4129
+ } else {
4130
+ var token = elt("span", "\u2022", "cm-invalidchar");
4131
+ token.title = "\\u" + m[0].charCodeAt(0).toString(16);
4132
+ content.appendChild(token);
4133
+ builder.col += 1;
4134
+ }
4135
+ }
4136
+ }
4137
+ if (style || startStyle || endStyle || builder.measure) {
4138
+ var fullStyle = style || "";
4139
+ if (startStyle) fullStyle += startStyle;
4140
+ if (endStyle) fullStyle += endStyle;
4141
+ return builder.pre.appendChild(elt("span", [content], fullStyle));
4142
+ }
4143
+ builder.pre.appendChild(content);
4144
+ }
4145
+
4146
+ function buildTokenMeasure(builder, text, style, startStyle, endStyle) {
4147
+ var wrapping = builder.cm.options.lineWrapping;
4148
+ for (var i = 0; i < text.length; ++i) {
4149
+ var ch = text.charAt(i), start = i == 0;
4150
+ if (ch >= "\ud800" && ch < "\udbff" && i < text.length - 1) {
4151
+ ch = text.slice(i, i + 2);
4152
+ ++i;
4153
+ } else if (i && wrapping &&
4154
+ spanAffectsWrapping.test(text.slice(i - 1, i + 1))) {
4155
+ builder.pre.appendChild(elt("wbr"));
4156
+ }
4157
+ var span = builder.measure[builder.pos] =
4158
+ buildToken(builder, ch, style,
4159
+ start && startStyle, i == text.length - 1 && endStyle);
4160
+ // In IE single-space nodes wrap differently than spaces
4161
+ // embedded in larger text nodes, except when set to
4162
+ // white-space: normal (issue #1268).
4163
+ if (ie && wrapping && ch == " " && i && !/\s/.test(text.charAt(i - 1)) &&
4164
+ i < text.length - 1 && !/\s/.test(text.charAt(i + 1)))
4165
+ span.style.whiteSpace = "normal";
4166
+ builder.pos += ch.length;
4167
+ }
4168
+ if (text.length) builder.addedOne = true;
4169
+ }
4170
+
4171
+ function buildTokenSplitSpaces(inner) {
4172
+ function split(old) {
4173
+ var out = " ";
4174
+ for (var i = 0; i < old.length - 2; ++i) out += i % 2 ? " " : "\u00a0";
4175
+ out += " ";
4176
+ return out;
4177
+ }
4178
+ return function(builder, text, style, startStyle, endStyle) {
4179
+ return inner(builder, text.replace(/ {3,}/, split), style, startStyle, endStyle);
4180
+ };
4181
+ }
4182
+
4183
+ function buildCollapsedSpan(builder, size, widget) {
4184
+ if (widget) {
4185
+ if (!builder.display) widget = widget.cloneNode(true);
4186
+ builder.pre.appendChild(widget);
4187
+ if (builder.measure && size) {
4188
+ builder.measure[builder.pos] = widget;
4189
+ builder.addedOne = true;
4190
+ }
4191
+ }
4192
+ builder.pos += size;
4193
+ }
4194
+
4195
+ // Outputs a number of spans to make up a line, taking highlighting
4196
+ // and marked text into account.
4197
+ function insertLineContent(line, builder, styles) {
4198
+ var spans = line.markedSpans;
4199
+ if (!spans) {
4200
+ for (var i = 1; i < styles.length; i+=2)
4201
+ builder.addToken(builder, styles[i], styleToClass(styles[i+1]));
4202
+ return;
4203
+ }
4204
+
4205
+ var allText = line.text, len = allText.length;
4206
+ var pos = 0, i = 1, text = "", style;
4207
+ var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, collapsed;
4208
+ for (;;) {
4209
+ if (nextChange == pos) { // Update current marker set
4210
+ spanStyle = spanEndStyle = spanStartStyle = "";
4211
+ collapsed = null; nextChange = Infinity;
4212
+ var foundBookmark = null;
4213
+ for (var j = 0; j < spans.length; ++j) {
4214
+ var sp = spans[j], m = sp.marker;
4215
+ if (sp.from <= pos && (sp.to == null || sp.to > pos)) {
4216
+ if (sp.to != null && nextChange > sp.to) { nextChange = sp.to; spanEndStyle = ""; }
4217
+ if (m.className) spanStyle += " " + m.className;
4218
+ if (m.startStyle && sp.from == pos) spanStartStyle += " " + m.startStyle;
4219
+ if (m.endStyle && sp.to == nextChange) spanEndStyle += " " + m.endStyle;
4220
+ if (m.collapsed && (!collapsed || collapsed.marker.width < m.width))
4221
+ collapsed = sp;
4222
+ } else if (sp.from > pos && nextChange > sp.from) {
4223
+ nextChange = sp.from;
4224
+ }
4225
+ if (m.type == "bookmark" && sp.from == pos && m.replacedWith)
4226
+ foundBookmark = m.replacedWith;
4227
+ }
4228
+ if (collapsed && (collapsed.from || 0) == pos) {
4229
+ buildCollapsedSpan(builder, (collapsed.to == null ? len : collapsed.to) - pos,
4230
+ collapsed.from != null && collapsed.marker.replacedWith);
4231
+ if (collapsed.to == null) return collapsed.marker.find();
4232
+ }
4233
+ if (foundBookmark && !collapsed) buildCollapsedSpan(builder, 0, foundBookmark);
4234
+ }
4235
+ if (pos >= len) break;
4236
+
4237
+ var upto = Math.min(len, nextChange);
4238
+ while (true) {
4239
+ if (text) {
4240
+ var end = pos + text.length;
4241
+ if (!collapsed) {
4242
+ var tokenText = end > upto ? text.slice(0, upto - pos) : text;
4243
+ builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle,
4244
+ spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "");
4245
+ }
4246
+ if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;}
4247
+ pos = end;
4248
+ spanStartStyle = "";
4249
+ }
4250
+ text = styles[i++]; style = styleToClass(styles[i++]);
4251
+ }
4252
+ }
4253
+ }
4254
+
4255
+ // DOCUMENT DATA STRUCTURE
4256
+
4257
+ function updateDoc(doc, change, markedSpans, selAfter, estimateHeight) {
4258
+ function spansFor(n) {return markedSpans ? markedSpans[n] : null;}
4259
+ function update(line, text, spans) {
4260
+ updateLine(line, text, spans, estimateHeight);
4261
+ signalLater(line, "change", line, change);
4262
+ }
4263
+
4264
+ var from = change.from, to = change.to, text = change.text;
4265
+ var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line);
4266
+ var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line;
4267
+
4268
+ // First adjust the line structure
4269
+ if (from.ch == 0 && to.ch == 0 && lastText == "") {
4270
+ // This is a whole-line replace. Treated specially to make
4271
+ // sure line objects move the way they are supposed to.
4272
+ for (var i = 0, e = text.length - 1, added = []; i < e; ++i)
4273
+ added.push(makeLine(text[i], spansFor(i), estimateHeight));
4274
+ update(lastLine, lastLine.text, lastSpans);
4275
+ if (nlines) doc.remove(from.line, nlines);
4276
+ if (added.length) doc.insert(from.line, added);
4277
+ } else if (firstLine == lastLine) {
4278
+ if (text.length == 1) {
4279
+ update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans);
4280
+ } else {
4281
+ for (var added = [], i = 1, e = text.length - 1; i < e; ++i)
4282
+ added.push(makeLine(text[i], spansFor(i), estimateHeight));
4283
+ added.push(makeLine(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight));
4284
+ update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
4285
+ doc.insert(from.line + 1, added);
4286
+ }
4287
+ } else if (text.length == 1) {
4288
+ update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0));
4289
+ doc.remove(from.line + 1, nlines);
4290
+ } else {
4291
+ update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
4292
+ update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans);
4293
+ for (var i = 1, e = text.length - 1, added = []; i < e; ++i)
4294
+ added.push(makeLine(text[i], spansFor(i), estimateHeight));
4295
+ if (nlines > 1) doc.remove(from.line + 1, nlines - 1);
4296
+ doc.insert(from.line + 1, added);
4297
+ }
4298
+
4299
+ signalLater(doc, "change", doc, change);
4300
+ setSelection(doc, selAfter.anchor, selAfter.head, null, true);
4301
+ }
4302
+
4303
+ function LeafChunk(lines) {
4304
+ this.lines = lines;
4305
+ this.parent = null;
4306
+ for (var i = 0, e = lines.length, height = 0; i < e; ++i) {
4307
+ lines[i].parent = this;
4308
+ height += lines[i].height;
4309
+ }
4310
+ this.height = height;
4311
+ }
4312
+
4313
+ LeafChunk.prototype = {
4314
+ chunkSize: function() { return this.lines.length; },
4315
+ removeInner: function(at, n) {
4316
+ for (var i = at, e = at + n; i < e; ++i) {
4317
+ var line = this.lines[i];
4318
+ this.height -= line.height;
4319
+ cleanUpLine(line);
4320
+ signalLater(line, "delete");
4321
+ }
4322
+ this.lines.splice(at, n);
4323
+ },
4324
+ collapse: function(lines) {
4325
+ lines.splice.apply(lines, [lines.length, 0].concat(this.lines));
4326
+ },
4327
+ insertInner: function(at, lines, height) {
4328
+ this.height += height;
4329
+ this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at));
4330
+ for (var i = 0, e = lines.length; i < e; ++i) lines[i].parent = this;
4331
+ },
4332
+ iterN: function(at, n, op) {
4333
+ for (var e = at + n; at < e; ++at)
4334
+ if (op(this.lines[at])) return true;
4335
+ }
4336
+ };
4337
+
4338
+ function BranchChunk(children) {
4339
+ this.children = children;
4340
+ var size = 0, height = 0;
4341
+ for (var i = 0, e = children.length; i < e; ++i) {
4342
+ var ch = children[i];
4343
+ size += ch.chunkSize(); height += ch.height;
4344
+ ch.parent = this;
4345
+ }
4346
+ this.size = size;
4347
+ this.height = height;
4348
+ this.parent = null;
4349
+ }
4350
+
4351
+ BranchChunk.prototype = {
4352
+ chunkSize: function() { return this.size; },
4353
+ removeInner: function(at, n) {
4354
+ this.size -= n;
4355
+ for (var i = 0; i < this.children.length; ++i) {
4356
+ var child = this.children[i], sz = child.chunkSize();
4357
+ if (at < sz) {
4358
+ var rm = Math.min(n, sz - at), oldHeight = child.height;
4359
+ child.removeInner(at, rm);
4360
+ this.height -= oldHeight - child.height;
4361
+ if (sz == rm) { this.children.splice(i--, 1); child.parent = null; }
4362
+ if ((n -= rm) == 0) break;
4363
+ at = 0;
4364
+ } else at -= sz;
4365
+ }
4366
+ if (this.size - n < 25) {
4367
+ var lines = [];
4368
+ this.collapse(lines);
4369
+ this.children = [new LeafChunk(lines)];
4370
+ this.children[0].parent = this;
4371
+ }
4372
+ },
4373
+ collapse: function(lines) {
4374
+ for (var i = 0, e = this.children.length; i < e; ++i) this.children[i].collapse(lines);
4375
+ },
4376
+ insertInner: function(at, lines, height) {
4377
+ this.size += lines.length;
4378
+ this.height += height;
4379
+ for (var i = 0, e = this.children.length; i < e; ++i) {
4380
+ var child = this.children[i], sz = child.chunkSize();
4381
+ if (at <= sz) {
4382
+ child.insertInner(at, lines, height);
4383
+ if (child.lines && child.lines.length > 50) {
4384
+ while (child.lines.length > 50) {
4385
+ var spilled = child.lines.splice(child.lines.length - 25, 25);
4386
+ var newleaf = new LeafChunk(spilled);
4387
+ child.height -= newleaf.height;
4388
+ this.children.splice(i + 1, 0, newleaf);
4389
+ newleaf.parent = this;
4390
+ }
4391
+ this.maybeSpill();
4392
+ }
4393
+ break;
4394
+ }
4395
+ at -= sz;
4396
+ }
4397
+ },
4398
+ maybeSpill: function() {
4399
+ if (this.children.length <= 10) return;
4400
+ var me = this;
4401
+ do {
4402
+ var spilled = me.children.splice(me.children.length - 5, 5);
4403
+ var sibling = new BranchChunk(spilled);
4404
+ if (!me.parent) { // Become the parent node
4405
+ var copy = new BranchChunk(me.children);
4406
+ copy.parent = me;
4407
+ me.children = [copy, sibling];
4408
+ me = copy;
4409
+ } else {
4410
+ me.size -= sibling.size;
4411
+ me.height -= sibling.height;
4412
+ var myIndex = indexOf(me.parent.children, me);
4413
+ me.parent.children.splice(myIndex + 1, 0, sibling);
4414
+ }
4415
+ sibling.parent = me.parent;
4416
+ } while (me.children.length > 10);
4417
+ me.parent.maybeSpill();
4418
+ },
4419
+ iterN: function(at, n, op) {
4420
+ for (var i = 0, e = this.children.length; i < e; ++i) {
4421
+ var child = this.children[i], sz = child.chunkSize();
4422
+ if (at < sz) {
4423
+ var used = Math.min(n, sz - at);
4424
+ if (child.iterN(at, used, op)) return true;
4425
+ if ((n -= used) == 0) break;
4426
+ at = 0;
4427
+ } else at -= sz;
4428
+ }
4429
+ }
4430
+ };
4431
+
4432
+ var nextDocId = 0;
4433
+ var Doc = CodeMirror.Doc = function(text, mode, firstLine) {
4434
+ if (!(this instanceof Doc)) return new Doc(text, mode, firstLine);
4435
+ if (firstLine == null) firstLine = 0;
4436
+
4437
+ BranchChunk.call(this, [new LeafChunk([makeLine("", null)])]);
4438
+ this.first = firstLine;
4439
+ this.scrollTop = this.scrollLeft = 0;
4440
+ this.cantEdit = false;
4441
+ this.history = makeHistory();
4442
+ this.frontier = firstLine;
4443
+ var start = Pos(firstLine, 0);
4444
+ this.sel = {from: start, to: start, head: start, anchor: start, shift: false, extend: false, goalColumn: null};
4445
+ this.id = ++nextDocId;
4446
+ this.modeOption = mode;
4447
+
4448
+ if (typeof text == "string") text = splitLines(text);
4449
+ updateDoc(this, {from: start, to: start, text: text}, null, {head: start, anchor: start});
4450
+ };
4451
+
4452
+ Doc.prototype = createObj(BranchChunk.prototype, {
4453
+ iter: function(from, to, op) {
4454
+ if (op) this.iterN(from - this.first, to - from, op);
4455
+ else this.iterN(this.first, this.first + this.size, from);
4456
+ },
4457
+
4458
+ insert: function(at, lines) {
4459
+ var height = 0;
4460
+ for (var i = 0, e = lines.length; i < e; ++i) height += lines[i].height;
4461
+ this.insertInner(at - this.first, lines, height);
4462
+ },
4463
+ remove: function(at, n) { this.removeInner(at - this.first, n); },
4464
+
4465
+ getValue: function(lineSep) {
4466
+ var lines = getLines(this, this.first, this.first + this.size);
4467
+ if (lineSep === false) return lines;
4468
+ return lines.join(lineSep || "\n");
4469
+ },
4470
+ setValue: function(code) {
4471
+ var top = Pos(this.first, 0), last = this.first + this.size - 1;
4472
+ makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length),
4473
+ text: splitLines(code), origin: "setValue"},
4474
+ {head: top, anchor: top}, true);
4475
+ },
4476
+ replaceRange: function(code, from, to, origin) {
4477
+ from = clipPos(this, from);
4478
+ to = to ? clipPos(this, to) : from;
4479
+ replaceRange(this, code, from, to, origin);
4480
+ },
4481
+ getRange: function(from, to, lineSep) {
4482
+ var lines = getBetween(this, clipPos(this, from), clipPos(this, to));
4483
+ if (lineSep === false) return lines;
4484
+ return lines.join(lineSep || "\n");
4485
+ },
4486
+
4487
+ getLine: function(line) {var l = this.getLineHandle(line); return l && l.text;},
4488
+ setLine: function(line, text) {
4489
+ if (isLine(this, line))
4490
+ replaceRange(this, text, Pos(line, 0), clipPos(this, Pos(line)));
4491
+ },
4492
+ removeLine: function(line) {
4493
+ if (isLine(this, line))
4494
+ replaceRange(this, "", Pos(line, 0), clipPos(this, Pos(line + 1, 0)));
4495
+ },
4496
+
4497
+ getLineHandle: function(line) {if (isLine(this, line)) return getLine(this, line);},
4498
+ getLineNumber: function(line) {return lineNo(line);},
4499
+
4500
+ lineCount: function() {return this.size;},
4501
+ firstLine: function() {return this.first;},
4502
+ lastLine: function() {return this.first + this.size - 1;},
4503
+
4504
+ clipPos: function(pos) {return clipPos(this, pos);},
4505
+
4506
+ getCursor: function(start) {
4507
+ var sel = this.sel, pos;
4508
+ if (start == null || start == "head") pos = sel.head;
4509
+ else if (start == "anchor") pos = sel.anchor;
4510
+ else if (start == "end" || start === false) pos = sel.to;
4511
+ else pos = sel.from;
4512
+ return copyPos(pos);
4513
+ },
4514
+ somethingSelected: function() {return !posEq(this.sel.head, this.sel.anchor);},
4515
+
4516
+ setCursor: docOperation(function(line, ch, extend) {
4517
+ var pos = clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line);
4518
+ if (extend) extendSelection(this, pos);
4519
+ else setSelection(this, pos, pos);
4520
+ }),
4521
+ setSelection: docOperation(function(anchor, head) {
4522
+ setSelection(this, clipPos(this, anchor), clipPos(this, head || anchor));
4523
+ }),
4524
+ extendSelection: docOperation(function(from, to) {
4525
+ extendSelection(this, clipPos(this, from), to && clipPos(this, to));
4526
+ }),
4527
+
4528
+ getSelection: function(lineSep) {return this.getRange(this.sel.from, this.sel.to, lineSep);},
4529
+ replaceSelection: function(code, collapse, origin) {
4530
+ makeChange(this, {from: this.sel.from, to: this.sel.to, text: splitLines(code), origin: origin}, collapse || "around");
4531
+ },
4532
+ undo: docOperation(function() {makeChangeFromHistory(this, "undo");}),
4533
+ redo: docOperation(function() {makeChangeFromHistory(this, "redo");}),
4534
+
4535
+ setExtending: function(val) {this.sel.extend = val;},
4536
+
4537
+ historySize: function() {
4538
+ var hist = this.history;
4539
+ return {undo: hist.done.length, redo: hist.undone.length};
4540
+ },
4541
+ clearHistory: function() {this.history = makeHistory();},
4542
+
4543
+ markClean: function() {
4544
+ this.history.dirtyCounter = 0;
4545
+ this.history.lastOp = this.history.lastOrigin = null;
4546
+ },
4547
+ isClean: function () {return this.history.dirtyCounter == 0;},
4548
+
4549
+ getHistory: function() {
4550
+ return {done: copyHistoryArray(this.history.done),
4551
+ undone: copyHistoryArray(this.history.undone)};
4552
+ },
4553
+ setHistory: function(histData) {
4554
+ var hist = this.history = makeHistory();
4555
+ hist.done = histData.done.slice(0);
4556
+ hist.undone = histData.undone.slice(0);
4557
+ },
4558
+
4559
+ markText: function(from, to, options) {
4560
+ return markText(this, clipPos(this, from), clipPos(this, to), options, "range");
4561
+ },
4562
+ setBookmark: function(pos, options) {
4563
+ var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options),
4564
+ insertLeft: options && options.insertLeft};
4565
+ pos = clipPos(this, pos);
4566
+ return markText(this, pos, pos, realOpts, "bookmark");
4567
+ },
4568
+ findMarksAt: function(pos) {
4569
+ pos = clipPos(this, pos);
4570
+ var markers = [], spans = getLine(this, pos.line).markedSpans;
4571
+ if (spans) for (var i = 0; i < spans.length; ++i) {
4572
+ var span = spans[i];
4573
+ if ((span.from == null || span.from <= pos.ch) &&
4574
+ (span.to == null || span.to >= pos.ch))
4575
+ markers.push(span.marker.parent || span.marker);
4576
+ }
4577
+ return markers;
4578
+ },
4579
+ getAllMarks: function() {
4580
+ var markers = [];
4581
+ this.iter(function(line) {
4582
+ var sps = line.markedSpans;
4583
+ if (sps) for (var i = 0; i < sps.length; ++i)
4584
+ if (sps[i].from != null) markers.push(sps[i].marker);
4585
+ });
4586
+ return markers;
4587
+ },
4588
+
4589
+ posFromIndex: function(off) {
4590
+ var ch, lineNo = this.first;
4591
+ this.iter(function(line) {
4592
+ var sz = line.text.length + 1;
4593
+ if (sz > off) { ch = off; return true; }
4594
+ off -= sz;
4595
+ ++lineNo;
4596
+ });
4597
+ return clipPos(this, Pos(lineNo, ch));
4598
+ },
4599
+ indexFromPos: function (coords) {
4600
+ coords = clipPos(this, coords);
4601
+ var index = coords.ch;
4602
+ if (coords.line < this.first || coords.ch < 0) return 0;
4603
+ this.iter(this.first, coords.line, function (line) {
4604
+ index += line.text.length + 1;
4605
+ });
4606
+ return index;
4607
+ },
4608
+
4609
+ copy: function(copyHistory) {
4610
+ var doc = new Doc(getLines(this, this.first, this.first + this.size), this.modeOption, this.first);
4611
+ doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft;
4612
+ doc.sel = {from: this.sel.from, to: this.sel.to, head: this.sel.head, anchor: this.sel.anchor,
4613
+ shift: this.sel.shift, extend: false, goalColumn: this.sel.goalColumn};
4614
+ if (copyHistory) {
4615
+ doc.history.undoDepth = this.history.undoDepth;
4616
+ doc.setHistory(this.getHistory());
4617
+ }
4618
+ return doc;
4619
+ },
4620
+
4621
+ linkedDoc: function(options) {
4622
+ if (!options) options = {};
4623
+ var from = this.first, to = this.first + this.size;
4624
+ if (options.from != null && options.from > from) from = options.from;
4625
+ if (options.to != null && options.to < to) to = options.to;
4626
+ var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from);
4627
+ if (options.sharedHist) copy.history = this.history;
4628
+ (this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist});
4629
+ copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}];
4630
+ return copy;
4631
+ },
4632
+ unlinkDoc: function(other) {
4633
+ if (other instanceof CodeMirror) other = other.doc;
4634
+ if (this.linked) for (var i = 0; i < this.linked.length; ++i) {
4635
+ var link = this.linked[i];
4636
+ if (link.doc != other) continue;
4637
+ this.linked.splice(i, 1);
4638
+ other.unlinkDoc(this);
4639
+ break;
4640
+ }
4641
+ // If the histories were shared, split them again
4642
+ if (other.history == this.history) {
4643
+ var splitIds = [other.id];
4644
+ linkedDocs(other, function(doc) {splitIds.push(doc.id);}, true);
4645
+ other.history = makeHistory();
4646
+ other.history.done = copyHistoryArray(this.history.done, splitIds);
4647
+ other.history.undone = copyHistoryArray(this.history.undone, splitIds);
4648
+ }
4649
+ },
4650
+ iterLinkedDocs: function(f) {linkedDocs(this, f);},
4651
+
4652
+ getMode: function() {return this.mode;},
4653
+ getEditor: function() {return this.cm;}
4654
+ });
4655
+
4656
+ Doc.prototype.eachLine = Doc.prototype.iter;
4657
+
4658
+ // The Doc methods that should be available on CodeMirror instances
4659
+ var dontDelegate = "iter insert remove copy getEditor".split(" ");
4660
+ for (var prop in Doc.prototype) if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0)
4661
+ CodeMirror.prototype[prop] = (function(method) {
4662
+ return function() {return method.apply(this.doc, arguments);};
4663
+ })(Doc.prototype[prop]);
4664
+
4665
+ function linkedDocs(doc, f, sharedHistOnly) {
4666
+ function propagate(doc, skip, sharedHist) {
4667
+ if (doc.linked) for (var i = 0; i < doc.linked.length; ++i) {
4668
+ var rel = doc.linked[i];
4669
+ if (rel.doc == skip) continue;
4670
+ var shared = sharedHist && rel.sharedHist;
4671
+ if (sharedHistOnly && !shared) continue;
4672
+ f(rel.doc, shared);
4673
+ propagate(rel.doc, doc, shared);
4674
+ }
4675
+ }
4676
+ propagate(doc, null, true);
4677
+ }
4678
+
4679
+ function attachDoc(cm, doc) {
4680
+ if (doc.cm) throw new Error("This document is already in use.");
4681
+ cm.doc = doc;
4682
+ doc.cm = cm;
4683
+ estimateLineHeights(cm);
4684
+ loadMode(cm);
4685
+ if (!cm.options.lineWrapping) computeMaxLength(cm);
4686
+ cm.options.mode = doc.modeOption;
4687
+ regChange(cm);
4688
+ }
4689
+
4690
+ // LINE UTILITIES
4691
+
4692
+ function getLine(chunk, n) {
4693
+ n -= chunk.first;
4694
+ while (!chunk.lines) {
4695
+ for (var i = 0;; ++i) {
4696
+ var child = chunk.children[i], sz = child.chunkSize();
4697
+ if (n < sz) { chunk = child; break; }
4698
+ n -= sz;
4699
+ }
4700
+ }
4701
+ return chunk.lines[n];
4702
+ }
4703
+
4704
+ function getBetween(doc, start, end) {
4705
+ var out = [], n = start.line;
4706
+ doc.iter(start.line, end.line + 1, function(line) {
4707
+ var text = line.text;
4708
+ if (n == end.line) text = text.slice(0, end.ch);
4709
+ if (n == start.line) text = text.slice(start.ch);
4710
+ out.push(text);
4711
+ ++n;
4712
+ });
4713
+ return out;
4714
+ }
4715
+ function getLines(doc, from, to) {
4716
+ var out = [];
4717
+ doc.iter(from, to, function(line) { out.push(line.text); });
4718
+ return out;
4719
+ }
4720
+
4721
+ function updateLineHeight(line, height) {
4722
+ var diff = height - line.height;
4723
+ for (var n = line; n; n = n.parent) n.height += diff;
4724
+ }
4725
+
4726
+ function lineNo(line) {
4727
+ if (line.parent == null) return null;
4728
+ var cur = line.parent, no = indexOf(cur.lines, line);
4729
+ for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {
4730
+ for (var i = 0;; ++i) {
4731
+ if (chunk.children[i] == cur) break;
4732
+ no += chunk.children[i].chunkSize();
4733
+ }
4734
+ }
4735
+ return no + cur.first;
4736
+ }
4737
+
4738
+ function lineAtHeight(chunk, h) {
4739
+ var n = chunk.first;
4740
+ outer: do {
4741
+ for (var i = 0, e = chunk.children.length; i < e; ++i) {
4742
+ var child = chunk.children[i], ch = child.height;
4743
+ if (h < ch) { chunk = child; continue outer; }
4744
+ h -= ch;
4745
+ n += child.chunkSize();
4746
+ }
4747
+ return n;
4748
+ } while (!chunk.lines);
4749
+ for (var i = 0, e = chunk.lines.length; i < e; ++i) {
4750
+ var line = chunk.lines[i], lh = line.height;
4751
+ if (h < lh) break;
4752
+ h -= lh;
4753
+ }
4754
+ return n + i;
4755
+ }
4756
+
4757
+ function heightAtLine(cm, lineObj) {
4758
+ lineObj = visualLine(cm.doc, lineObj);
4759
+
4760
+ var h = 0, chunk = lineObj.parent;
4761
+ for (var i = 0; i < chunk.lines.length; ++i) {
4762
+ var line = chunk.lines[i];
4763
+ if (line == lineObj) break;
4764
+ else h += line.height;
4765
+ }
4766
+ for (var p = chunk.parent; p; chunk = p, p = chunk.parent) {
4767
+ for (var i = 0; i < p.children.length; ++i) {
4768
+ var cur = p.children[i];
4769
+ if (cur == chunk) break;
4770
+ else h += cur.height;
4771
+ }
4772
+ }
4773
+ return h;
4774
+ }
4775
+
4776
+ function getOrder(line) {
4777
+ var order = line.order;
4778
+ if (order == null) order = line.order = bidiOrdering(line.text);
4779
+ return order;
4780
+ }
4781
+
4782
+ // HISTORY
4783
+
4784
+ function makeHistory() {
4785
+ return {
4786
+ // Arrays of history events. Doing something adds an event to
4787
+ // done and clears undo. Undoing moves events from done to
4788
+ // undone, redoing moves them in the other direction.
4789
+ done: [], undone: [], undoDepth: Infinity,
4790
+ // Used to track when changes can be merged into a single undo
4791
+ // event
4792
+ lastTime: 0, lastOp: null, lastOrigin: null,
4793
+ // Used by the isClean() method
4794
+ dirtyCounter: 0
4795
+ };
4796
+ }
4797
+
4798
+ function attachLocalSpans(doc, change, from, to) {
4799
+ var existing = change["spans_" + doc.id], n = 0;
4800
+ doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function(line) {
4801
+ if (line.markedSpans)
4802
+ (existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans;
4803
+ ++n;
4804
+ });
4805
+ }
4806
+
4807
+ function historyChangeFromChange(doc, change) {
4808
+ var histChange = {from: change.from, to: changeEnd(change), text: getBetween(doc, change.from, change.to)};
4809
+ attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);
4810
+ linkedDocs(doc, function(doc) {attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);}, true);
4811
+ return histChange;
4812
+ }
4813
+
4814
+ function addToHistory(doc, change, selAfter, opId) {
4815
+ var hist = doc.history;
4816
+ hist.undone.length = 0;
4817
+ var time = +new Date, cur = lst(hist.done);
4818
+
4819
+ if (cur &&
4820
+ (hist.lastOp == opId ||
4821
+ hist.lastOrigin == change.origin && change.origin &&
4822
+ ((change.origin.charAt(0) == "+" && hist.lastTime > time - 600) || change.origin.charAt(0) == "*"))) {
4823
+ // Merge this change into the last event
4824
+ var last = lst(cur.changes);
4825
+ if (posEq(change.from, change.to) && posEq(change.from, last.to)) {
4826
+ // Optimized case for simple insertion -- don't want to add
4827
+ // new changesets for every character typed
4828
+ last.to = changeEnd(change);
4829
+ } else {
4830
+ // Add new sub-event
4831
+ cur.changes.push(historyChangeFromChange(doc, change));
4832
+ }
4833
+ cur.anchorAfter = selAfter.anchor; cur.headAfter = selAfter.head;
4834
+ } else {
4835
+ // Can not be merged, start a new event.
4836
+ cur = {changes: [historyChangeFromChange(doc, change)],
4837
+ anchorBefore: doc.sel.anchor, headBefore: doc.sel.head,
4838
+ anchorAfter: selAfter.anchor, headAfter: selAfter.head};
4839
+ hist.done.push(cur);
4840
+ while (hist.done.length > hist.undoDepth)
4841
+ hist.done.shift();
4842
+ if (hist.dirtyCounter < 0)
4843
+ // The user has made a change after undoing past the last clean state.
4844
+ // We can never get back to a clean state now until markClean() is called.
4845
+ hist.dirtyCounter = NaN;
4846
+ else
4847
+ hist.dirtyCounter++;
4848
+ }
4849
+ hist.lastTime = time;
4850
+ hist.lastOp = opId;
4851
+ hist.lastOrigin = change.origin;
4852
+ }
4853
+
4854
+ function removeClearedSpans(spans) {
4855
+ if (!spans) return null;
4856
+ for (var i = 0, out; i < spans.length; ++i) {
4857
+ if (spans[i].marker.explicitlyCleared) { if (!out) out = spans.slice(0, i); }
4858
+ else if (out) out.push(spans[i]);
4859
+ }
4860
+ return !out ? spans : out.length ? out : null;
4861
+ }
4862
+
4863
+ function getOldSpans(doc, change) {
4864
+ var found = change["spans_" + doc.id];
4865
+ if (!found) return null;
4866
+ for (var i = 0, nw = []; i < change.text.length; ++i)
4867
+ nw.push(removeClearedSpans(found[i]));
4868
+ return nw;
4869
+ }
4870
+
4871
+ // Used both to provide a JSON-safe object in .getHistory, and, when
4872
+ // detaching a document, to split the history in two
4873
+ function copyHistoryArray(events, newGroup) {
4874
+ for (var i = 0, copy = []; i < events.length; ++i) {
4875
+ var event = events[i], changes = event.changes, newChanges = [];
4876
+ copy.push({changes: newChanges, anchorBefore: event.anchorBefore, headBefore: event.headBefore,
4877
+ anchorAfter: event.anchorAfter, headAfter: event.headAfter});
4878
+ for (var j = 0; j < changes.length; ++j) {
4879
+ var change = changes[j], m;
4880
+ newChanges.push({from: change.from, to: change.to, text: change.text});
4881
+ if (newGroup) for (var prop in change) if (m = prop.match(/^spans_(\d+)$/)) {
4882
+ if (indexOf(newGroup, Number(m[1])) > -1) {
4883
+ lst(newChanges)[prop] = change[prop];
4884
+ delete change[prop];
4885
+ }
4886
+ }
4887
+ }
4888
+ }
4889
+ return copy;
4890
+ }
4891
+
4892
+ // Rebasing/resetting history to deal with externally-sourced changes
4893
+
4894
+ function rebaseHistSel(pos, from, to, diff) {
4895
+ if (to < pos.line) {
4896
+ pos.line += diff;
4897
+ } else if (from < pos.line) {
4898
+ pos.line = from;
4899
+ pos.ch = 0;
4900
+ }
4901
+ }
4902
+
4903
+ // Tries to rebase an array of history events given a change in the
4904
+ // document. If the change touches the same lines as the event, the
4905
+ // event, and everything 'behind' it, is discarded. If the change is
4906
+ // before the event, the event's positions are updated. Uses a
4907
+ // copy-on-write scheme for the positions, to avoid having to
4908
+ // reallocate them all on every rebase, but also avoid problems with
4909
+ // shared position objects being unsafely updated.
4910
+ function rebaseHistArray(array, from, to, diff) {
4911
+ for (var i = 0; i < array.length; ++i) {
4912
+ var sub = array[i], ok = true;
4913
+ for (var j = 0; j < sub.changes.length; ++j) {
4914
+ var cur = sub.changes[j];
4915
+ if (!sub.copied) { cur.from = copyPos(cur.from); cur.to = copyPos(cur.to); }
4916
+ if (to < cur.from.line) {
4917
+ cur.from.line += diff;
4918
+ cur.to.line += diff;
4919
+ } else if (from <= cur.to.line) {
4920
+ ok = false;
4921
+ break;
4922
+ }
4923
+ }
4924
+ if (!sub.copied) {
4925
+ sub.anchorBefore = copyPos(sub.anchorBefore); sub.headBefore = copyPos(sub.headBefore);
4926
+ sub.anchorAfter = copyPos(sub.anchorAfter); sub.readAfter = copyPos(sub.headAfter);
4927
+ sub.copied = true;
4928
+ }
4929
+ if (!ok) {
4930
+ array.splice(0, i + 1);
4931
+ i = 0;
4932
+ } else {
4933
+ rebaseHistSel(sub.anchorBefore); rebaseHistSel(sub.headBefore);
4934
+ rebaseHistSel(sub.anchorAfter); rebaseHistSel(sub.headAfter);
4935
+ }
4936
+ }
4937
+ }
4938
+
4939
+ function rebaseHist(hist, change) {
4940
+ var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1;
4941
+ rebaseHistArray(hist.done, from, to, diff);
4942
+ rebaseHistArray(hist.undone, from, to, diff);
4943
+ }
4944
+
4945
+ // EVENT OPERATORS
4946
+
4947
+ function stopMethod() {e_stop(this);}
4948
+ // Ensure an event has a stop method.
4949
+ function addStop(event) {
4950
+ if (!event.stop) event.stop = stopMethod;
4951
+ return event;
4952
+ }
4953
+
4954
+ function e_preventDefault(e) {
4955
+ if (e.preventDefault) e.preventDefault();
4956
+ else e.returnValue = false;
4957
+ }
4958
+ function e_stopPropagation(e) {
4959
+ if (e.stopPropagation) e.stopPropagation();
4960
+ else e.cancelBubble = true;
4961
+ }
4962
+ function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);}
4963
+ CodeMirror.e_stop = e_stop;
4964
+ CodeMirror.e_preventDefault = e_preventDefault;
4965
+ CodeMirror.e_stopPropagation = e_stopPropagation;
4966
+
4967
+ function e_target(e) {return e.target || e.srcElement;}
4968
+ function e_button(e) {
4969
+ var b = e.which;
4970
+ if (b == null) {
4971
+ if (e.button & 1) b = 1;
4972
+ else if (e.button & 2) b = 3;
4973
+ else if (e.button & 4) b = 2;
4974
+ }
4975
+ if (mac && e.ctrlKey && b == 1) b = 3;
4976
+ return b;
4977
+ }
4978
+
4979
+ // EVENT HANDLING
4980
+
4981
+ function on(emitter, type, f) {
4982
+ if (emitter.addEventListener)
4983
+ emitter.addEventListener(type, f, false);
4984
+ else if (emitter.attachEvent)
4985
+ emitter.attachEvent("on" + type, f);
4986
+ else {
4987
+ var map = emitter._handlers || (emitter._handlers = {});
4988
+ var arr = map[type] || (map[type] = []);
4989
+ arr.push(f);
4990
+ }
4991
+ }
4992
+
4993
+ function off(emitter, type, f) {
4994
+ if (emitter.removeEventListener)
4995
+ emitter.removeEventListener(type, f, false);
4996
+ else if (emitter.detachEvent)
4997
+ emitter.detachEvent("on" + type, f);
4998
+ else {
4999
+ var arr = emitter._handlers && emitter._handlers[type];
5000
+ if (!arr) return;
5001
+ for (var i = 0; i < arr.length; ++i)
5002
+ if (arr[i] == f) { arr.splice(i, 1); break; }
5003
+ }
5004
+ }
5005
+
5006
+ function signal(emitter, type /*, values...*/) {
5007
+ var arr = emitter._handlers && emitter._handlers[type];
5008
+ if (!arr) return;
5009
+ var args = Array.prototype.slice.call(arguments, 2);
5010
+ for (var i = 0; i < arr.length; ++i) arr[i].apply(null, args);
5011
+ }
5012
+
5013
+ var delayedCallbacks, delayedCallbackDepth = 0;
5014
+ function signalLater(emitter, type /*, values...*/) {
5015
+ var arr = emitter._handlers && emitter._handlers[type];
5016
+ if (!arr) return;
5017
+ var args = Array.prototype.slice.call(arguments, 2);
5018
+ if (!delayedCallbacks) {
5019
+ ++delayedCallbackDepth;
5020
+ delayedCallbacks = [];
5021
+ setTimeout(fireDelayed, 0);
5022
+ }
5023
+ function bnd(f) {return function(){f.apply(null, args);};};
5024
+ for (var i = 0; i < arr.length; ++i)
5025
+ delayedCallbacks.push(bnd(arr[i]));
5026
+ }
5027
+
5028
+ function fireDelayed() {
5029
+ --delayedCallbackDepth;
5030
+ var delayed = delayedCallbacks;
5031
+ delayedCallbacks = null;
5032
+ for (var i = 0; i < delayed.length; ++i) delayed[i]();
5033
+ }
5034
+
5035
+ function hasHandler(emitter, type) {
5036
+ var arr = emitter._handlers && emitter._handlers[type];
5037
+ return arr && arr.length > 0;
5038
+ }
5039
+
5040
+ CodeMirror.on = on; CodeMirror.off = off; CodeMirror.signal = signal;
5041
+
5042
+ // MISC UTILITIES
5043
+
5044
+ // Number of pixels added to scroller and sizer to hide scrollbar
5045
+ var scrollerCutOff = 30;
5046
+
5047
+ // Returned or thrown by various protocols to signal 'I'm not
5048
+ // handling this'.
5049
+ var Pass = CodeMirror.Pass = {toString: function(){return "CodeMirror.Pass";}};
5050
+
5051
+ function Delayed() {this.id = null;}
5052
+ Delayed.prototype = {set: function(ms, f) {clearTimeout(this.id); this.id = setTimeout(f, ms);}};
5053
+
5054
+ // Counts the column offset in a string, taking tabs into account.
5055
+ // Used mostly to find indentation.
5056
+ function countColumn(string, end, tabSize, startIndex, startValue) {
5057
+ if (end == null) {
5058
+ end = string.search(/[^\s\u00a0]/);
5059
+ if (end == -1) end = string.length;
5060
+ }
5061
+ for (var i = startIndex || 0, n = startValue || 0; i < end; ++i) {
5062
+ if (string.charAt(i) == "\t") n += tabSize - (n % tabSize);
5063
+ else ++n;
5064
+ }
5065
+ return n;
5066
+ }
5067
+ CodeMirror.countColumn = countColumn;
5068
+
5069
+ var spaceStrs = [""];
5070
+ function spaceStr(n) {
5071
+ while (spaceStrs.length <= n)
5072
+ spaceStrs.push(lst(spaceStrs) + " ");
5073
+ return spaceStrs[n];
5074
+ }
5075
+
5076
+ function lst(arr) { return arr[arr.length-1]; }
5077
+
5078
+ function selectInput(node) {
5079
+ if (ios) { // Mobile Safari apparently has a bug where select() is broken.
5080
+ node.selectionStart = 0;
5081
+ node.selectionEnd = node.value.length;
5082
+ } else node.select();
5083
+ }
5084
+
5085
+ function indexOf(collection, elt) {
5086
+ if (collection.indexOf) return collection.indexOf(elt);
5087
+ for (var i = 0, e = collection.length; i < e; ++i)
5088
+ if (collection[i] == elt) return i;
5089
+ return -1;
5090
+ }
5091
+
5092
+ function createObj(base, props) {
5093
+ function Obj() {}
5094
+ Obj.prototype = base;
5095
+ var inst = new Obj();
5096
+ if (props) copyObj(props, inst);
5097
+ return inst;
5098
+ }
5099
+
5100
+ function copyObj(obj, target) {
5101
+ if (!target) target = {};
5102
+ for (var prop in obj) if (obj.hasOwnProperty(prop)) target[prop] = obj[prop];
5103
+ return target;
5104
+ }
5105
+
5106
+ function emptyArray(size) {
5107
+ for (var a = [], i = 0; i < size; ++i) a.push(undefined);
5108
+ return a;
5109
+ }
5110
+
5111
+ function bind(f) {
5112
+ var args = Array.prototype.slice.call(arguments, 1);
5113
+ return function(){return f.apply(null, args);};
5114
+ }
5115
+
5116
+ var nonASCIISingleCaseWordChar = /[\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc]/;
5117
+ function isWordChar(ch) {
5118
+ return /\w/.test(ch) || ch > "\x80" &&
5119
+ (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch));
5120
+ }
5121
+
5122
+ function isEmpty(obj) {
5123
+ for (var n in obj) if (obj.hasOwnProperty(n) && obj[n]) return false;
5124
+ return true;
5125
+ }
5126
+
5127
+ var isExtendingChar = /[\u0300-\u036F\u0483-\u0487\u0488-\u0489\u0591-\u05BD\u05BF\u05C1-\u05C2\u05C4-\u05C5\u05C7\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7-\u06E8\u06EA-\u06ED\uA66F\uA670-\uA672\uA674-\uA67D\uA69F\udc00-\udfff]/;
5128
+
5129
+ // DOM UTILITIES
5130
+
5131
+ function elt(tag, content, className, style) {
5132
+ var e = document.createElement(tag);
5133
+ if (className) e.className = className;
5134
+ if (style) e.style.cssText = style;
5135
+ if (typeof content == "string") setTextContent(e, content);
5136
+ else if (content) for (var i = 0; i < content.length; ++i) e.appendChild(content[i]);
5137
+ return e;
5138
+ }
5139
+
5140
+ function removeChildren(e) {
5141
+ for (var count = e.childNodes.length; count > 0; --count)
5142
+ e.removeChild(e.firstChild);
5143
+ return e;
5144
+ }
5145
+
5146
+ function removeChildrenAndAdd(parent, e) {
5147
+ return removeChildren(parent).appendChild(e);
5148
+ }
5149
+
5150
+ function setTextContent(e, str) {
5151
+ if (ie_lt9) {
5152
+ e.innerHTML = "";
5153
+ e.appendChild(document.createTextNode(str));
5154
+ } else e.textContent = str;
5155
+ }
5156
+
5157
+ function getRect(node) {
5158
+ return node.getBoundingClientRect();
5159
+ }
5160
+ CodeMirror.replaceGetRect = function(f) { getRect = f; };
5161
+
5162
+ // FEATURE DETECTION
5163
+
5164
+ // Detect drag-and-drop
5165
+ var dragAndDrop = function() {
5166
+ // There is *some* kind of drag-and-drop support in IE6-8, but I
5167
+ // couldn't get it to work yet.
5168
+ if (ie_lt9) return false;
5169
+ var div = elt('div');
5170
+ return "draggable" in div || "dragDrop" in div;
5171
+ }();
5172
+
5173
+ // For a reason I have yet to figure out, some browsers disallow
5174
+ // word wrapping between certain characters *only* if a new inline
5175
+ // element is started between them. This makes it hard to reliably
5176
+ // measure the position of things, since that requires inserting an
5177
+ // extra span. This terribly fragile set of regexps matches the
5178
+ // character combinations that suffer from this phenomenon on the
5179
+ // various browsers.
5180
+ var spanAffectsWrapping = /^$/; // Won't match any two-character string
5181
+ if (gecko) spanAffectsWrapping = /$'/;
5182
+ else if (safari && !/Version\/([6-9]|\d\d)\b/.test(navigator.userAgent)) spanAffectsWrapping = /\-[^ \-?]|\?[^ !'\"\),.\-\/:;\?\]\}]/;
5183
+ else if (webkit) spanAffectsWrapping = /[~!#%&*)=+}\]|\"\.>,:;][({[<]|-[^\-?\.]|\?[\w~`@#$%\^&*(_=+{[|><]/;
5184
+
5185
+ var knownScrollbarWidth;
5186
+ function scrollbarWidth(measure) {
5187
+ if (knownScrollbarWidth != null) return knownScrollbarWidth;
5188
+ var test = elt("div", null, null, "width: 50px; height: 50px; overflow-x: scroll");
5189
+ removeChildrenAndAdd(measure, test);
5190
+ if (test.offsetWidth)
5191
+ knownScrollbarWidth = test.offsetHeight - test.clientHeight;
5192
+ return knownScrollbarWidth || 0;
5193
+ }
5194
+
5195
+ var zwspSupported;
5196
+ function zeroWidthElement(measure) {
5197
+ if (zwspSupported == null) {
5198
+ var test = elt("span", "\u200b");
5199
+ removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")]));
5200
+ if (measure.firstChild.offsetHeight != 0)
5201
+ zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !ie_lt8;
5202
+ }
5203
+ if (zwspSupported) return elt("span", "\u200b");
5204
+ else return elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px");
5205
+ }
5206
+
5207
+ // See if "".split is the broken IE version, if so, provide an
5208
+ // alternative way to split lines.
5209
+ var splitLines = "\n\nb".split(/\n/).length != 3 ? function(string) {
5210
+ var pos = 0, result = [], l = string.length;
5211
+ while (pos <= l) {
5212
+ var nl = string.indexOf("\n", pos);
5213
+ if (nl == -1) nl = string.length;
5214
+ var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl);
5215
+ var rt = line.indexOf("\r");
5216
+ if (rt != -1) {
5217
+ result.push(line.slice(0, rt));
5218
+ pos += rt + 1;
5219
+ } else {
5220
+ result.push(line);
5221
+ pos = nl + 1;
5222
+ }
5223
+ }
5224
+ return result;
5225
+ } : function(string){return string.split(/\r\n?|\n/);};
5226
+ CodeMirror.splitLines = splitLines;
5227
+
5228
+ var hasSelection = window.getSelection ? function(te) {
5229
+ try { return te.selectionStart != te.selectionEnd; }
5230
+ catch(e) { return false; }
5231
+ } : function(te) {
5232
+ try {var range = te.ownerDocument.selection.createRange();}
5233
+ catch(e) {}
5234
+ if (!range || range.parentElement() != te) return false;
5235
+ return range.compareEndPoints("StartToEnd", range) != 0;
5236
+ };
5237
+
5238
+ var hasCopyEvent = (function() {
5239
+ var e = elt("div");
5240
+ if ("oncopy" in e) return true;
5241
+ e.setAttribute("oncopy", "return;");
5242
+ return typeof e.oncopy == 'function';
5243
+ })();
5244
+
5245
+ // KEY NAMING
5246
+
5247
+ var keyNames = {3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
5248
+ 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
5249
+ 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
5250
+ 46: "Delete", 59: ";", 91: "Mod", 92: "Mod", 93: "Mod", 109: "-", 107: "=", 127: "Delete",
5251
+ 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\",
5252
+ 221: "]", 222: "'", 63276: "PageUp", 63277: "PageDown", 63275: "End", 63273: "Home",
5253
+ 63234: "Left", 63232: "Up", 63235: "Right", 63233: "Down", 63302: "Insert", 63272: "Delete"};
5254
+ CodeMirror.keyNames = keyNames;
5255
+ (function() {
5256
+ // Number keys
5257
+ for (var i = 0; i < 10; i++) keyNames[i + 48] = String(i);
5258
+ // Alphabetic keys
5259
+ for (var i = 65; i <= 90; i++) keyNames[i] = String.fromCharCode(i);
5260
+ // Function keys
5261
+ for (var i = 1; i <= 12; i++) keyNames[i + 111] = keyNames[i + 63235] = "F" + i;
5262
+ })();
5263
+
5264
+ // BIDI HELPERS
5265
+
5266
+ function iterateBidiSections(order, from, to, f) {
5267
+ if (!order) return f(from, to, "ltr");
5268
+ for (var i = 0; i < order.length; ++i) {
5269
+ var part = order[i];
5270
+ if (part.from < to && part.to > from || from == to && part.to == from)
5271
+ f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr");
5272
+ }
5273
+ }
5274
+
5275
+ function bidiLeft(part) { return part.level % 2 ? part.to : part.from; }
5276
+ function bidiRight(part) { return part.level % 2 ? part.from : part.to; }
5277
+
5278
+ function lineLeft(line) { var order = getOrder(line); return order ? bidiLeft(order[0]) : 0; }
5279
+ function lineRight(line) {
5280
+ var order = getOrder(line);
5281
+ if (!order) return line.text.length;
5282
+ return bidiRight(lst(order));
5283
+ }
5284
+
5285
+ function lineStart(cm, lineN) {
5286
+ var line = getLine(cm.doc, lineN);
5287
+ var visual = visualLine(cm.doc, line);
5288
+ if (visual != line) lineN = lineNo(visual);
5289
+ var order = getOrder(visual);
5290
+ var ch = !order ? 0 : order[0].level % 2 ? lineRight(visual) : lineLeft(visual);
5291
+ return Pos(lineN, ch);
5292
+ }
5293
+ function lineEnd(cm, lineN) {
5294
+ var merged, line;
5295
+ while (merged = collapsedSpanAtEnd(line = getLine(cm.doc, lineN)))
5296
+ lineN = merged.find().to.line;
5297
+ var order = getOrder(line);
5298
+ var ch = !order ? line.text.length : order[0].level % 2 ? lineLeft(line) : lineRight(line);
5299
+ return Pos(lineN, ch);
5300
+ }
5301
+
5302
+ // This is somewhat involved. It is needed in order to move
5303
+ // 'visually' through bi-directional text -- i.e., pressing left
5304
+ // should make the cursor go left, even when in RTL text. The
5305
+ // tricky part is the 'jumps', where RTL and LTR text touch each
5306
+ // other. This often requires the cursor offset to move more than
5307
+ // one unit, in order to visually move one unit.
5308
+ function moveVisually(line, start, dir, byUnit) {
5309
+ var bidi = getOrder(line);
5310
+ if (!bidi) return moveLogically(line, start, dir, byUnit);
5311
+ var moveOneUnit = byUnit ? function(pos, dir) {
5312
+ do pos += dir;
5313
+ while (pos > 0 && isExtendingChar.test(line.text.charAt(pos)));
5314
+ return pos;
5315
+ } : function(pos, dir) { return pos + dir; };
5316
+ var linedir = bidi[0].level;
5317
+ for (var i = 0; i < bidi.length; ++i) {
5318
+ var part = bidi[i], sticky = part.level % 2 == linedir;
5319
+ if ((part.from < start && part.to > start) ||
5320
+ (sticky && (part.from == start || part.to == start))) break;
5321
+ }
5322
+ var target = moveOneUnit(start, part.level % 2 ? -dir : dir);
5323
+
5324
+ while (target != null) {
5325
+ if (part.level % 2 == linedir) {
5326
+ if (target < part.from || target > part.to) {
5327
+ part = bidi[i += dir];
5328
+ target = part && (dir > 0 == part.level % 2 ? moveOneUnit(part.to, -1) : moveOneUnit(part.from, 1));
5329
+ } else break;
5330
+ } else {
5331
+ if (target == bidiLeft(part)) {
5332
+ part = bidi[--i];
5333
+ target = part && bidiRight(part);
5334
+ } else if (target == bidiRight(part)) {
5335
+ part = bidi[++i];
5336
+ target = part && bidiLeft(part);
5337
+ } else break;
5338
+ }
5339
+ }
5340
+
5341
+ return target < 0 || target > line.text.length ? null : target;
5342
+ }
5343
+
5344
+ function moveLogically(line, start, dir, byUnit) {
5345
+ var target = start + dir;
5346
+ if (byUnit) while (target > 0 && isExtendingChar.test(line.text.charAt(target))) target += dir;
5347
+ return target < 0 || target > line.text.length ? null : target;
5348
+ }
5349
+
5350
+ // Bidirectional ordering algorithm
5351
+ // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm
5352
+ // that this (partially) implements.
5353
+
5354
+ // One-char codes used for character types:
5355
+ // L (L): Left-to-Right
5356
+ // R (R): Right-to-Left
5357
+ // r (AL): Right-to-Left Arabic
5358
+ // 1 (EN): European Number
5359
+ // + (ES): European Number Separator
5360
+ // % (ET): European Number Terminator
5361
+ // n (AN): Arabic Number
5362
+ // , (CS): Common Number Separator
5363
+ // m (NSM): Non-Spacing Mark
5364
+ // b (BN): Boundary Neutral
5365
+ // s (B): Paragraph Separator
5366
+ // t (S): Segment Separator
5367
+ // w (WS): Whitespace
5368
+ // N (ON): Other Neutrals
5369
+
5370
+ // Returns null if characters are ordered as they appear
5371
+ // (left-to-right), or an array of sections ({from, to, level}
5372
+ // objects) in the order in which they occur visually.
5373
+ var bidiOrdering = (function() {
5374
+ // Character types for codepoints 0 to 0xff
5375
+ var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLL";
5376
+ // Character types for codepoints 0x600 to 0x6ff
5377
+ var arabicTypes = "rrrrrrrrrrrr,rNNmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmrrrrrrrnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmNmmmmrrrrrrrrrrrrrrrrrr";
5378
+ function charType(code) {
5379
+ if (code <= 0xff) return lowTypes.charAt(code);
5380
+ else if (0x590 <= code && code <= 0x5f4) return "R";
5381
+ else if (0x600 <= code && code <= 0x6ff) return arabicTypes.charAt(code - 0x600);
5382
+ else if (0x700 <= code && code <= 0x8ac) return "r";
5383
+ else return "L";
5384
+ }
5385
+
5386
+ var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;
5387
+ var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/;
5388
+ // Browsers seem to always treat the boundaries of block elements as being L.
5389
+ var outerType = "L";
5390
+
5391
+ return function(str) {
5392
+ if (!bidiRE.test(str)) return false;
5393
+ var len = str.length, types = [];
5394
+ for (var i = 0, type; i < len; ++i)
5395
+ types.push(type = charType(str.charCodeAt(i)));
5396
+
5397
+ // W1. Examine each non-spacing mark (NSM) in the level run, and
5398
+ // change the type of the NSM to the type of the previous
5399
+ // character. If the NSM is at the start of the level run, it will
5400
+ // get the type of sor.
5401
+ for (var i = 0, prev = outerType; i < len; ++i) {
5402
+ var type = types[i];
5403
+ if (type == "m") types[i] = prev;
5404
+ else prev = type;
5405
+ }
5406
+
5407
+ // W2. Search backwards from each instance of a European number
5408
+ // until the first strong type (R, L, AL, or sor) is found. If an
5409
+ // AL is found, change the type of the European number to Arabic
5410
+ // number.
5411
+ // W3. Change all ALs to R.
5412
+ for (var i = 0, cur = outerType; i < len; ++i) {
5413
+ var type = types[i];
5414
+ if (type == "1" && cur == "r") types[i] = "n";
5415
+ else if (isStrong.test(type)) { cur = type; if (type == "r") types[i] = "R"; }
5416
+ }
5417
+
5418
+ // W4. A single European separator between two European numbers
5419
+ // changes to a European number. A single common separator between
5420
+ // two numbers of the same type changes to that type.
5421
+ for (var i = 1, prev = types[0]; i < len - 1; ++i) {
5422
+ var type = types[i];
5423
+ if (type == "+" && prev == "1" && types[i+1] == "1") types[i] = "1";
5424
+ else if (type == "," && prev == types[i+1] &&
5425
+ (prev == "1" || prev == "n")) types[i] = prev;
5426
+ prev = type;
5427
+ }
5428
+
5429
+ // W5. A sequence of European terminators adjacent to European
5430
+ // numbers changes to all European numbers.
5431
+ // W6. Otherwise, separators and terminators change to Other
5432
+ // Neutral.
5433
+ for (var i = 0; i < len; ++i) {
5434
+ var type = types[i];
5435
+ if (type == ",") types[i] = "N";
5436
+ else if (type == "%") {
5437
+ for (var end = i + 1; end < len && types[end] == "%"; ++end) {}
5438
+ var replace = (i && types[i-1] == "!") || (end < len - 1 && types[end] == "1") ? "1" : "N";
5439
+ for (var j = i; j < end; ++j) types[j] = replace;
5440
+ i = end - 1;
5441
+ }
5442
+ }
5443
+
5444
+ // W7. Search backwards from each instance of a European number
5445
+ // until the first strong type (R, L, or sor) is found. If an L is
5446
+ // found, then change the type of the European number to L.
5447
+ for (var i = 0, cur = outerType; i < len; ++i) {
5448
+ var type = types[i];
5449
+ if (cur == "L" && type == "1") types[i] = "L";
5450
+ else if (isStrong.test(type)) cur = type;
5451
+ }
5452
+
5453
+ // N1. A sequence of neutrals takes the direction of the
5454
+ // surrounding strong text if the text on both sides has the same
5455
+ // direction. European and Arabic numbers act as if they were R in
5456
+ // terms of their influence on neutrals. Start-of-level-run (sor)
5457
+ // and end-of-level-run (eor) are used at level run boundaries.
5458
+ // N2. Any remaining neutrals take the embedding direction.
5459
+ for (var i = 0; i < len; ++i) {
5460
+ if (isNeutral.test(types[i])) {
5461
+ for (var end = i + 1; end < len && isNeutral.test(types[end]); ++end) {}
5462
+ var before = (i ? types[i-1] : outerType) == "L";
5463
+ var after = (end < len - 1 ? types[end] : outerType) == "L";
5464
+ var replace = before || after ? "L" : "R";
5465
+ for (var j = i; j < end; ++j) types[j] = replace;
5466
+ i = end - 1;
5467
+ }
5468
+ }
5469
+
5470
+ // Here we depart from the documented algorithm, in order to avoid
5471
+ // building up an actual levels array. Since there are only three
5472
+ // levels (0, 1, 2) in an implementation that doesn't take
5473
+ // explicit embedding into account, we can build up the order on
5474
+ // the fly, without following the level-based algorithm.
5475
+ var order = [], m;
5476
+ for (var i = 0; i < len;) {
5477
+ if (countsAsLeft.test(types[i])) {
5478
+ var start = i;
5479
+ for (++i; i < len && countsAsLeft.test(types[i]); ++i) {}
5480
+ order.push({from: start, to: i, level: 0});
5481
+ } else {
5482
+ var pos = i, at = order.length;
5483
+ for (++i; i < len && types[i] != "L"; ++i) {}
5484
+ for (var j = pos; j < i;) {
5485
+ if (countsAsNum.test(types[j])) {
5486
+ if (pos < j) order.splice(at, 0, {from: pos, to: j, level: 1});
5487
+ var nstart = j;
5488
+ for (++j; j < i && countsAsNum.test(types[j]); ++j) {}
5489
+ order.splice(at, 0, {from: nstart, to: j, level: 2});
5490
+ pos = j;
5491
+ } else ++j;
5492
+ }
5493
+ if (pos < i) order.splice(at, 0, {from: pos, to: i, level: 1});
5494
+ }
5495
+ }
5496
+ if (order[0].level == 1 && (m = str.match(/^\s+/))) {
5497
+ order[0].from = m[0].length;
5498
+ order.unshift({from: 0, to: m[0].length, level: 0});
5499
+ }
5500
+ if (lst(order).level == 1 && (m = str.match(/\s+$/))) {
5501
+ lst(order).to -= m[0].length;
5502
+ order.push({from: len - m[0].length, to: len, level: 0});
5503
+ }
5504
+ if (order[0].level != lst(order).level)
5505
+ order.push({from: len, to: len, level: order[0].level});
5506
+
5507
+ return order;
5508
+ };
5509
+ })();
5510
+
5511
+ // THE END
5512
+
5513
+ CodeMirror.version = "3.11";
5514
+
5515
+ return CodeMirror;
5516
+ })();
{assets → vendor}/codemirror/mode/clike.js RENAMED
@@ -1,302 +1,302 @@
1
- CodeMirror.defineMode("clike", function(config, parserConfig) {
2
- var indentUnit = config.indentUnit,
3
- statementIndentUnit = parserConfig.statementIndentUnit || indentUnit,
4
- dontAlignCalls = parserConfig.dontAlignCalls,
5
- keywords = parserConfig.keywords || {},
6
- builtin = parserConfig.builtin || {},
7
- blockKeywords = parserConfig.blockKeywords || {},
8
- atoms = parserConfig.atoms || {},
9
- hooks = parserConfig.hooks || {},
10
- multiLineStrings = parserConfig.multiLineStrings;
11
- var isOperatorChar = /[+\-*&%=<>!?|\/]/;
12
-
13
- var curPunc;
14
-
15
- function tokenBase(stream, state) {
16
- var ch = stream.next();
17
- if (hooks[ch]) {
18
- var result = hooks[ch](stream, state);
19
- if (result !== false) return result;
20
- }
21
- if (ch == '"' || ch == "'") {
22
- state.tokenize = tokenString(ch);
23
- return state.tokenize(stream, state);
24
- }
25
- if (/[\[\]{}\(\),;\:\.]/.test(ch)) {
26
- curPunc = ch;
27
- return null;
28
- }
29
- if (/\d/.test(ch)) {
30
- stream.eatWhile(/[\w\.]/);
31
- return "number";
32
- }
33
- if (ch == "/") {
34
- if (stream.eat("*")) {
35
- state.tokenize = tokenComment;
36
- return tokenComment(stream, state);
37
- }
38
- if (stream.eat("/")) {
39
- stream.skipToEnd();
40
- return "comment";
41
- }
42
- }
43
- if (isOperatorChar.test(ch)) {
44
- stream.eatWhile(isOperatorChar);
45
- return "operator";
46
- }
47
- stream.eatWhile(/[\w\$_]/);
48
- var cur = stream.current();
49
- if (keywords.propertyIsEnumerable(cur)) {
50
- if (blockKeywords.propertyIsEnumerable(cur)) curPunc = "newstatement";
51
- return "keyword";
52
- }
53
- if (builtin.propertyIsEnumerable(cur)) {
54
- if (blockKeywords.propertyIsEnumerable(cur)) curPunc = "newstatement";
55
- return "builtin";
56
- }
57
- if (atoms.propertyIsEnumerable(cur)) return "atom";
58
- return "variable";
59
- }
60
-
61
- function tokenString(quote) {
62
- return function(stream, state) {
63
- var escaped = false, next, end = false;
64
- while ((next = stream.next()) != null) {
65
- if (next == quote && !escaped) {end = true; break;}
66
- escaped = !escaped && next == "\\";
67
- }
68
- if (end || !(escaped || multiLineStrings))
69
- state.tokenize = null;
70
- return "string";
71
- };
72
- }
73
-
74
- function tokenComment(stream, state) {
75
- var maybeEnd = false, ch;
76
- while (ch = stream.next()) {
77
- if (ch == "/" && maybeEnd) {
78
- state.tokenize = null;
79
- break;
80
- }
81
- maybeEnd = (ch == "*");
82
- }
83
- return "comment";
84
- }
85
-
86
- function Context(indented, column, type, align, prev) {
87
- this.indented = indented;
88
- this.column = column;
89
- this.type = type;
90
- this.align = align;
91
- this.prev = prev;
92
- }
93
- function pushContext(state, col, type) {
94
- var indent = state.indented;
95
- if (state.context && state.context.type == "statement")
96
- indent = state.context.indented;
97
- return state.context = new Context(indent, col, type, null, state.context);
98
- }
99
- function popContext(state) {
100
- var t = state.context.type;
101
- if (t == ")" || t == "]" || t == "}")
102
- state.indented = state.context.indented;
103
- return state.context = state.context.prev;
104
- }
105
-
106
- // Interface
107
-
108
- return {
109
- startState: function(basecolumn) {
110
- return {
111
- tokenize: null,
112
- context: new Context((basecolumn || 0) - indentUnit, 0, "top", false),
113
- indented: 0,
114
- startOfLine: true
115
- };
116
- },
117
-
118
- token: function(stream, state) {
119
- var ctx = state.context;
120
- if (stream.sol()) {
121
- if (ctx.align == null) ctx.align = false;
122
- state.indented = stream.indentation();
123
- state.startOfLine = true;
124
- }
125
- if (stream.eatSpace()) return null;
126
- curPunc = null;
127
- var style = (state.tokenize || tokenBase)(stream, state);
128
- if (style == "comment" || style == "meta") return style;
129
- if (ctx.align == null) ctx.align = true;
130
-
131
- if ((curPunc == ";" || curPunc == ":" || curPunc == ",") && ctx.type == "statement") popContext(state);
132
- else if (curPunc == "{") pushContext(state, stream.column(), "}");
133
- else if (curPunc == "[") pushContext(state, stream.column(), "]");
134
- else if (curPunc == "(") pushContext(state, stream.column(), ")");
135
- else if (curPunc == "}") {
136
- while (ctx.type == "statement") ctx = popContext(state);
137
- if (ctx.type == "}") ctx = popContext(state);
138
- while (ctx.type == "statement") ctx = popContext(state);
139
- }
140
- else if (curPunc == ctx.type) popContext(state);
141
- else if (((ctx.type == "}" || ctx.type == "top") && curPunc != ';') || (ctx.type == "statement" && curPunc == "newstatement"))
142
- pushContext(state, stream.column(), "statement");
143
- state.startOfLine = false;
144
- return style;
145
- },
146
-
147
- indent: function(state, textAfter) {
148
- if (state.tokenize != tokenBase && state.tokenize != null) return CodeMirror.Pass;
149
- var ctx = state.context, firstChar = textAfter && textAfter.charAt(0);
150
- if (ctx.type == "statement" && firstChar == "}") ctx = ctx.prev;
151
- var closing = firstChar == ctx.type;
152
- if (ctx.type == "statement") return ctx.indented + (firstChar == "{" ? 0 : statementIndentUnit);
153
- else if (dontAlignCalls && ctx.type == ")" && !closing) return ctx.indented + statementIndentUnit;
154
- else if (ctx.align) return ctx.column + (closing ? 0 : 1);
155
- else return ctx.indented + (closing ? 0 : indentUnit);
156
- },
157
-
158
- electricChars: "{}"
159
- };
160
- });
161
-
162
- (function() {
163
- function words(str) {
164
- var obj = {}, words = str.split(" ");
165
- for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
166
- return obj;
167
- }
168
- var cKeywords = "auto if break int case long char register continue return default short do sizeof " +
169
- "double static else struct entry switch extern typedef float union for unsigned " +
170
- "goto while enum void const signed volatile";
171
-
172
- function cppHook(stream, state) {
173
- if (!state.startOfLine) return false;
174
- for (;;) {
175
- if (stream.skipTo("\\")) {
176
- stream.next();
177
- if (stream.eol()) {
178
- state.tokenize = cppHook;
179
- break;
180
- }
181
- } else {
182
- stream.skipToEnd();
183
- state.tokenize = null;
184
- break;
185
- }
186
- }
187
- return "meta";
188
- }
189
-
190
- // C#-style strings where "" escapes a quote.
191
- function tokenAtString(stream, state) {
192
- var next;
193
- while ((next = stream.next()) != null) {
194
- if (next == '"' && !stream.eat('"')) {
195
- state.tokenize = null;
196
- break;
197
- }
198
- }
199
- return "string";
200
- }
201
-
202
- function mimes(ms, mode) {
203
- for (var i = 0; i < ms.length; ++i) CodeMirror.defineMIME(ms[i], mode);
204
- }
205
-
206
- mimes(["text/x-csrc", "text/x-c", "text/x-chdr"], {
207
- name: "clike",
208
- keywords: words(cKeywords),
209
- blockKeywords: words("case do else for if switch while struct"),
210
- atoms: words("null"),
211
- hooks: {"#": cppHook}
212
- });
213
- mimes(["text/x-c++src", "text/x-c++hdr"], {
214
- name: "clike",
215
- keywords: words(cKeywords + " asm dynamic_cast namespace reinterpret_cast try bool explicit new " +
216
- "static_cast typeid catch operator template typename class friend private " +
217
- "this using const_cast inline public throw virtual delete mutable protected " +
218
- "wchar_t"),
219
- blockKeywords: words("catch class do else finally for if struct switch try while"),
220
- atoms: words("true false null"),
221
- hooks: {"#": cppHook}
222
- });
223
- CodeMirror.defineMIME("text/x-java", {
224
- name: "clike",
225
- keywords: words("abstract assert boolean break byte case catch char class const continue default " +
226
- "do double else enum extends final finally float for goto if implements import " +
227
- "instanceof int interface long native new package private protected public " +
228
- "return short static strictfp super switch synchronized this throw throws transient " +
229
- "try void volatile while"),
230
- blockKeywords: words("catch class do else finally for if switch try while"),
231
- atoms: words("true false null"),
232
- hooks: {
233
- "@": function(stream) {
234
- stream.eatWhile(/[\w\$_]/);
235
- return "meta";
236
- }
237
- }
238
- });
239
- CodeMirror.defineMIME("text/x-csharp", {
240
- name: "clike",
241
- keywords: words("abstract as base break case catch checked class const continue" +
242
- " default delegate do else enum event explicit extern finally fixed for" +
243
- " foreach goto if implicit in interface internal is lock namespace new" +
244
- " operator out override params private protected public readonly ref return sealed" +
245
- " sizeof stackalloc static struct switch this throw try typeof unchecked" +
246
- " unsafe using virtual void volatile while add alias ascending descending dynamic from get" +
247
- " global group into join let orderby partial remove select set value var yield"),
248
- blockKeywords: words("catch class do else finally for foreach if struct switch try while"),
249
- builtin: words("Boolean Byte Char DateTime DateTimeOffset Decimal Double" +
250
- " Guid Int16 Int32 Int64 Object SByte Single String TimeSpan UInt16 UInt32" +
251
- " UInt64 bool byte char decimal double short int long object" +
252
- " sbyte float string ushort uint ulong"),
253
- atoms: words("true false null"),
254
- hooks: {
255
- "@": function(stream, state) {
256
- if (stream.eat('"')) {
257
- state.tokenize = tokenAtString;
258
- return tokenAtString(stream, state);
259
- }
260
- stream.eatWhile(/[\w\$_]/);
261
- return "meta";
262
- }
263
- }
264
- });
265
- CodeMirror.defineMIME("text/x-scala", {
266
- name: "clike",
267
- keywords: words(
268
-
269
- /* scala */
270
- "abstract case catch class def do else extends false final finally for forSome if " +
271
- "implicit import lazy match new null object override package private protected return " +
272
- "sealed super this throw trait try trye type val var while with yield _ : = => <- <: " +
273
- "<% >: # @ " +
274
-
275
- /* package scala */
276
- "assert assume require print println printf readLine readBoolean readByte readShort " +
277
- "readChar readInt readLong readFloat readDouble " +
278
-
279
- "AnyVal App Application Array BufferedIterator BigDecimal BigInt Char Console Either " +
280
- "Enumeration Equiv Error Exception Fractional Function IndexedSeq Integral Iterable " +
281
- "Iterator List Map Numeric Nil NotNull Option Ordered Ordering PartialFunction PartialOrdering " +
282
- "Product Proxy Range Responder Seq Serializable Set Specializable Stream StringBuilder " +
283
- "StringContext Symbol Throwable Traversable TraversableOnce Tuple Unit Vector :: #:: " +
284
-
285
- /* package java.lang */
286
- "Boolean Byte Character CharSequence Class ClassLoader Cloneable Comparable " +
287
- "Compiler Double Exception Float Integer Long Math Number Object Package Pair Process " +
288
- "Runtime Runnable SecurityManager Short StackTraceElement StrictMath String " +
289
- "StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void"
290
-
291
-
292
- ),
293
- blockKeywords: words("catch class do else finally for forSome if match switch try while"),
294
- atoms: words("true false null"),
295
- hooks: {
296
- "@": function(stream) {
297
- stream.eatWhile(/[\w\$_]/);
298
- return "meta";
299
- }
300
- }
301
- });
302
- }());
1
+ CodeMirror.defineMode("clike", function(config, parserConfig) {
2
+ var indentUnit = config.indentUnit,
3
+ statementIndentUnit = parserConfig.statementIndentUnit || indentUnit,
4
+ dontAlignCalls = parserConfig.dontAlignCalls,
5
+ keywords = parserConfig.keywords || {},
6
+ builtin = parserConfig.builtin || {},
7
+ blockKeywords = parserConfig.blockKeywords || {},
8
+ atoms = parserConfig.atoms || {},
9
+ hooks = parserConfig.hooks || {},
10
+ multiLineStrings = parserConfig.multiLineStrings;
11
+ var isOperatorChar = /[+\-*&%=<>!?|\/]/;
12
+
13
+ var curPunc;
14
+
15
+ function tokenBase(stream, state) {
16
+ var ch = stream.next();
17
+ if (hooks[ch]) {
18
+ var result = hooks[ch](stream, state);
19
+ if (result !== false) return result;
20
+ }
21
+ if (ch == '"' || ch == "'") {
22
+ state.tokenize = tokenString(ch);
23
+ return state.tokenize(stream, state);
24
+ }
25
+ if (/[\[\]{}\(\),;\:\.]/.test(ch)) {
26
+ curPunc = ch;
27
+ return null;
28
+ }
29
+ if (/\d/.test(ch)) {
30
+ stream.eatWhile(/[\w\.]/);
31
+ return "number";
32
+ }
33
+ if (ch == "/") {
34
+ if (stream.eat("*")) {
35
+ state.tokenize = tokenComment;
36
+ return tokenComment(stream, state);
37
+ }
38
+ if (stream.eat("/")) {
39
+ stream.skipToEnd();
40
+ return "comment";
41
+ }
42
+ }
43
+ if (isOperatorChar.test(ch)) {
44
+ stream.eatWhile(isOperatorChar);
45
+ return "operator";
46
+ }
47
+ stream.eatWhile(/[\w\$_]/);
48
+ var cur = stream.current();
49
+ if (keywords.propertyIsEnumerable(cur)) {
50
+ if (blockKeywords.propertyIsEnumerable(cur)) curPunc = "newstatement";
51
+ return "keyword";
52
+ }
53
+ if (builtin.propertyIsEnumerable(cur)) {
54
+ if (blockKeywords.propertyIsEnumerable(cur)) curPunc = "newstatement";
55
+ return "builtin";
56
+ }
57
+ if (atoms.propertyIsEnumerable(cur)) return "atom";
58
+ return "variable";
59
+ }
60
+
61
+ function tokenString(quote) {
62
+ return function(stream, state) {
63
+ var escaped = false, next, end = false;
64
+ while ((next = stream.next()) != null) {
65
+ if (next == quote && !escaped) {end = true; break;}
66
+ escaped = !escaped && next == "\\";
67
+ }
68
+ if (end || !(escaped || multiLineStrings))
69
+ state.tokenize = null;
70
+ return "string";
71
+ };
72
+ }
73
+
74
+ function tokenComment(stream, state) {
75
+ var maybeEnd = false, ch;
76
+ while (ch = stream.next()) {
77
+ if (ch == "/" && maybeEnd) {
78
+ state.tokenize = null;
79
+ break;
80
+ }
81
+ maybeEnd = (ch == "*");
82
+ }
83
+ return "comment";
84
+ }
85
+
86
+ function Context(indented, column, type, align, prev) {
87
+ this.indented = indented;
88
+ this.column = column;
89
+ this.type = type;
90
+ this.align = align;
91
+ this.prev = prev;
92
+ }
93
+ function pushContext(state, col, type) {
94
+ var indent = state.indented;
95
+ if (state.context && state.context.type == "statement")
96
+ indent = state.context.indented;
97
+ return state.context = new Context(indent, col, type, null, state.context);
98
+ }
99
+ function popContext(state) {
100
+ var t = state.context.type;
101
+ if (t == ")" || t == "]" || t == "}")
102
+ state.indented = state.context.indented;
103
+ return state.context = state.context.prev;
104
+ }
105
+
106
+ // Interface
107
+
108
+ return {
109
+ startState: function(basecolumn) {
110
+ return {
111
+ tokenize: null,
112
+ context: new Context((basecolumn || 0) - indentUnit, 0, "top", false),
113
+ indented: 0,
114
+ startOfLine: true
115
+ };
116
+ },
117
+
118
+ token: function(stream, state) {
119
+ var ctx = state.context;
120
+ if (stream.sol()) {
121
+ if (ctx.align == null) ctx.align = false;
122
+ state.indented = stream.indentation();
123
+ state.startOfLine = true;
124
+ }
125
+ if (stream.eatSpace()) return null;
126
+ curPunc = null;
127
+ var style = (state.tokenize || tokenBase)(stream, state);
128
+ if (style == "comment" || style == "meta") return style;
129
+ if (ctx.align == null) ctx.align = true;
130
+
131
+ if ((curPunc == ";" || curPunc == ":" || curPunc == ",") && ctx.type == "statement") popContext(state);
132
+ else if (curPunc == "{") pushContext(state, stream.column(), "}");
133
+ else if (curPunc == "[") pushContext(state, stream.column(), "]");
134
+ else if (curPunc == "(") pushContext(state, stream.column(), ")");
135
+ else if (curPunc == "}") {
136
+ while (ctx.type == "statement") ctx = popContext(state);
137
+ if (ctx.type == "}") ctx = popContext(state);
138
+ while (ctx.type == "statement") ctx = popContext(state);
139
+ }
140
+ else if (curPunc == ctx.type) popContext(state);
141
+ else if (((ctx.type == "}" || ctx.type == "top") && curPunc != ';') || (ctx.type == "statement" && curPunc == "newstatement"))
142
+ pushContext(state, stream.column(), "statement");
143
+ state.startOfLine = false;
144
+ return style;
145
+ },
146
+
147
+ indent: function(state, textAfter) {
148
+ if (state.tokenize != tokenBase && state.tokenize != null) return CodeMirror.Pass;
149
+ var ctx = state.context, firstChar = textAfter && textAfter.charAt(0);
150
+ if (ctx.type == "statement" && firstChar == "}") ctx = ctx.prev;
151
+ var closing = firstChar == ctx.type;
152
+ if (ctx.type == "statement") return ctx.indented + (firstChar == "{" ? 0 : statementIndentUnit);
153
+ else if (dontAlignCalls && ctx.type == ")" && !closing) return ctx.indented + statementIndentUnit;
154
+ else if (ctx.align) return ctx.column + (closing ? 0 : 1);
155
+ else return ctx.indented + (closing ? 0 : indentUnit);
156
+ },
157
+
158
+ electricChars: "{}"
159
+ };
160
+ });
161
+
162
+ (function() {
163
+ function words(str) {
164
+ var obj = {}, words = str.split(" ");
165
+ for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
166
+ return obj;
167
+ }
168
+ var cKeywords = "auto if break int case long char register continue return default short do sizeof " +
169
+ "double static else struct entry switch extern typedef float union for unsigned " +
170
+ "goto while enum void const signed volatile";
171
+
172
+ function cppHook(stream, state) {
173
+ if (!state.startOfLine) return false;
174
+ for (;;) {
175
+ if (stream.skipTo("\\")) {
176
+ stream.next();
177
+ if (stream.eol()) {
178
+ state.tokenize = cppHook;
179
+ break;
180
+ }
181
+ } else {
182
+ stream.skipToEnd();
183
+ state.tokenize = null;
184
+ break;
185
+ }
186
+ }
187
+ return "meta";
188
+ }
189
+
190
+ // C#-style strings where "" escapes a quote.
191
+ function tokenAtString(stream, state) {
192
+ var next;
193
+ while ((next = stream.next()) != null) {
194
+ if (next == '"' && !stream.eat('"')) {
195
+ state.tokenize = null;
196
+ break;
197
+ }
198
+ }
199
+ return "string";
200
+ }
201
+
202
+ function mimes(ms, mode) {
203
+ for (var i = 0; i < ms.length; ++i) CodeMirror.defineMIME(ms[i], mode);
204
+ }
205
+
206
+ mimes(["text/x-csrc", "text/x-c", "text/x-chdr"], {
207
+ name: "clike",
208
+ keywords: words(cKeywords),
209
+ blockKeywords: words("case do else for if switch while struct"),
210
+ atoms: words("null"),
211
+ hooks: {"#": cppHook}
212
+ });
213
+ mimes(["text/x-c++src", "text/x-c++hdr"], {
214
+ name: "clike",
215
+ keywords: words(cKeywords + " asm dynamic_cast namespace reinterpret_cast try bool explicit new " +
216
+ "static_cast typeid catch operator template typename class friend private " +
217
+ "this using const_cast inline public throw virtual delete mutable protected " +
218
+ "wchar_t"),
219
+ blockKeywords: words("catch class do else finally for if struct switch try while"),
220
+ atoms: words("true false null"),
221
+ hooks: {"#": cppHook}
222
+ });
223
+ CodeMirror.defineMIME("text/x-java", {
224
+ name: "clike",
225
+ keywords: words("abstract assert boolean break byte case catch char class const continue default " +
226
+ "do double else enum extends final finally float for goto if implements import " +
227
+ "instanceof int interface long native new package private protected public " +
228
+ "return short static strictfp super switch synchronized this throw throws transient " +
229
+ "try void volatile while"),
230
+ blockKeywords: words("catch class do else finally for if switch try while"),
231
+ atoms: words("true false null"),
232
+ hooks: {
233
+ "@": function(stream) {
234
+ stream.eatWhile(/[\w\$_]/);
235
+ return "meta";
236
+ }
237
+ }
238
+ });
239
+ CodeMirror.defineMIME("text/x-csharp", {
240
+ name: "clike",
241
+ keywords: words("abstract as base break case catch checked class const continue" +
242
+ " default delegate do else enum event explicit extern finally fixed for" +
243
+ " foreach goto if implicit in interface internal is lock namespace new" +
244
+ " operator out override params private protected public readonly ref return sealed" +
245
+ " sizeof stackalloc static struct switch this throw try typeof unchecked" +
246
+ " unsafe using virtual void volatile while add alias ascending descending dynamic from get" +
247
+ " global group into join let orderby partial remove select set value var yield"),
248
+ blockKeywords: words("catch class do else finally for foreach if struct switch try while"),
249
+ builtin: words("Boolean Byte Char DateTime DateTimeOffset Decimal Double" +
250
+ " Guid Int16 Int32 Int64 Object SByte Single String TimeSpan UInt16 UInt32" +
251
+ " UInt64 bool byte char decimal double short int long object" +
252
+ " sbyte float string ushort uint ulong"),
253
+ atoms: words("true false null"),
254
+ hooks: {
255
+ "@": function(stream, state) {
256
+ if (stream.eat('"')) {
257
+ state.tokenize = tokenAtString;
258
+ return tokenAtString(stream, state);
259
+ }
260
+ stream.eatWhile(/[\w\$_]/);
261
+ return "meta";
262
+ }
263
+ }
264
+ });
265
+ CodeMirror.defineMIME("text/x-scala", {
266
+ name: "clike",
267
+ keywords: words(
268
+
269
+ /* scala */
270
+ "abstract case catch class def do else extends false final finally for forSome if " +
271
+ "implicit import lazy match new null object override package private protected return " +
272
+ "sealed super this throw trait try trye type val var while with yield _ : = => <- <: " +
273
+ "<% >: # @ " +
274
+
275
+ /* package scala */
276
+ "assert assume require print println printf readLine readBoolean readByte readShort " +
277
+ "readChar readInt readLong readFloat readDouble " +
278
+
279
+ "AnyVal App Application Array BufferedIterator BigDecimal BigInt Char Console Either " +
280
+ "Enumeration Equiv Error Exception Fractional Function IndexedSeq Integral Iterable " +
281
+ "Iterator List Map Numeric Nil NotNull Option Ordered Ordering PartialFunction PartialOrdering " +
282
+ "Product Proxy Range Responder Seq Serializable Set Specializable Stream StringBuilder " +
283
+ "StringContext Symbol Throwable Traversable TraversableOnce Tuple Unit Vector :: #:: " +
284
+
285
+ /* package java.lang */
286
+ "Boolean Byte Character CharSequence Class ClassLoader Cloneable Comparable " +
287
+ "Compiler Double Exception Float Integer Long Math Number Object Package Pair Process " +
288
+ "Runtime Runnable SecurityManager Short StackTraceElement StrictMath String " +
289
+ "StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void"
290
+
291
+
292
+ ),
293
+ blockKeywords: words("catch class do else finally for forSome if match switch try while"),
294
+ atoms: words("true false null"),
295
+ hooks: {
296
+ "@": function(stream) {
297
+ stream.eatWhile(/[\w\$_]/);
298
+ return "meta";
299
+ }
300
+ }
301
+ });
302
+ }());
{assets → vendor}/codemirror/mode/php.js RENAMED
@@ -1,129 +1,129 @@
1
- (function() {
2
- function keywords(str) {
3
- var obj = {}, words = str.split(" ");
4
- for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
5
- return obj;
6
- }
7
- function heredoc(delim) {
8
- return function(stream, state) {
9
- if (stream.match(delim)) state.tokenize = null;
10
- else stream.skipToEnd();
11
- return "string";
12
- };
13
- }
14
- var phpConfig = {
15
- name: "clike",
16
- keywords: keywords("abstract and array as break case catch class clone const continue declare default " +
17
- "do else elseif enddeclare endfor endforeach endif endswitch endwhile extends final " +
18
- "for foreach function global goto if implements interface instanceof namespace " +
19
- "new or private protected public static switch throw trait try use var while xor " +
20
- "die echo empty exit eval include include_once isset list require require_once return " +
21
- "print unset __halt_compiler self static parent"),
22
- blockKeywords: keywords("catch do else elseif for foreach if switch try while"),
23
- atoms: keywords("true false null TRUE FALSE NULL __CLASS__ __DIR__ __FILE__ __LINE__ __METHOD__ __FUNCTION__ __NAMESPACE__"),
24
- builtin: keywords("func_num_args func_get_arg func_get_args strlen strcmp strncmp strcasecmp strncasecmp each error_reporting define defined trigger_error user_error set_error_handler restore_error_handler get_declared_classes get_loaded_extensions extension_loaded get_extension_funcs debug_backtrace constant bin2hex sleep usleep time mktime gmmktime strftime gmstrftime strtotime date gmdate getdate localtime checkdate flush wordwrap htmlspecialchars htmlentities html_entity_decode md5 md5_file crc32 getimagesize image_type_to_mime_type phpinfo phpversion phpcredits strnatcmp strnatcasecmp substr_count strspn strcspn strtok strtoupper strtolower strpos strrpos strrev hebrev hebrevc nl2br basename dirname pathinfo stripslashes stripcslashes strstr stristr strrchr str_shuffle str_word_count strcoll substr substr_replace quotemeta ucfirst ucwords strtr addslashes addcslashes rtrim str_replace str_repeat count_chars chunk_split trim ltrim strip_tags similar_text explode implode setlocale localeconv parse_str str_pad chop strchr sprintf printf vprintf vsprintf sscanf fscanf parse_url urlencode urldecode rawurlencode rawurldecode readlink linkinfo link unlink exec system escapeshellcmd escapeshellarg passthru shell_exec proc_open proc_close rand srand getrandmax mt_rand mt_srand mt_getrandmax base64_decode base64_encode abs ceil floor round is_finite is_nan is_infinite bindec hexdec octdec decbin decoct dechex base_convert number_format fmod ip2long long2ip getenv putenv getopt microtime gettimeofday getrusage uniqid quoted_printable_decode set_time_limit get_cfg_var magic_quotes_runtime set_magic_quotes_runtime get_magic_quotes_gpc get_magic_quotes_runtime import_request_variables error_log serialize unserialize memory_get_usage var_dump var_export debug_zval_dump print_r highlight_file show_source highlight_string ini_get ini_get_all ini_set ini_alter ini_restore get_include_path set_include_path restore_include_path setcookie header headers_sent connection_aborted connection_status ignore_user_abort parse_ini_file is_uploaded_file move_uploaded_file intval floatval doubleval strval gettype settype is_null is_resource is_bool is_long is_float is_int is_integer is_double is_real is_numeric is_string is_array is_object is_scalar ereg ereg_replace eregi eregi_replace split spliti join sql_regcase dl pclose popen readfile rewind rmdir umask fclose feof fgetc fgets fgetss fread fopen fpassthru ftruncate fstat fseek ftell fflush fwrite fputs mkdir rename copy tempnam tmpfile file file_get_contents stream_select stream_context_create stream_context_set_params stream_context_set_option stream_context_get_options stream_filter_prepend stream_filter_append fgetcsv flock get_meta_tags stream_set_write_buffer set_file_buffer set_socket_blocking stream_set_blocking socket_set_blocking stream_get_meta_data stream_register_wrapper stream_wrapper_register stream_set_timeout socket_set_timeout socket_get_status realpath fnmatch fsockopen pfsockopen pack unpack get_browser crypt opendir closedir chdir getcwd rewinddir readdir dir glob fileatime filectime filegroup fileinode filemtime fileowner fileperms filesize filetype file_exists is_writable is_writeable is_readable is_executable is_file is_dir is_link stat lstat chown touch clearstatcache mail ob_start ob_flush ob_clean ob_end_flush ob_end_clean ob_get_flush ob_get_clean ob_get_length ob_get_level ob_get_status ob_get_contents ob_implicit_flush ob_list_handlers ksort krsort natsort natcasesort asort arsort sort rsort usort uasort uksort shuffle array_walk count end prev next reset current key min max in_array array_search extract compact array_fill range array_multisort array_push array_pop array_shift array_unshift array_splice array_slice array_merge array_merge_recursive array_keys array_values array_count_values array_reverse array_reduce array_pad array_flip array_change_key_case array_rand array_unique array_intersect array_intersect_assoc array_diff array_diff_assoc array_sum array_filter array_map array_chunk array_key_exists pos sizeof key_exists assert assert_options version_compare ftok str_rot13 aggregate session_name session_module_name session_save_path session_id session_regenerate_id session_decode session_register session_unregister session_is_registered session_encode session_start session_destroy session_unset session_set_save_handler session_cache_limiter session_cache_expire session_set_cookie_params session_get_cookie_params session_write_close preg_match preg_match_all preg_replace preg_replace_callback preg_split preg_quote preg_grep overload ctype_alnum ctype_alpha ctype_cntrl ctype_digit ctype_lower ctype_graph ctype_print ctype_punct ctype_space ctype_upper ctype_xdigit virtual apache_request_headers apache_note apache_lookup_uri apache_child_terminate apache_setenv apache_response_headers apache_get_version getallheaders mysql_connect mysql_pconnect mysql_close mysql_select_db mysql_create_db mysql_drop_db mysql_query mysql_unbuffered_query mysql_db_query mysql_list_dbs mysql_list_tables mysql_list_fields mysql_list_processes mysql_error mysql_errno mysql_affected_rows mysql_insert_id mysql_result mysql_num_rows mysql_num_fields mysql_fetch_row mysql_fetch_array mysql_fetch_assoc mysql_fetch_object mysql_data_seek mysql_fetch_lengths mysql_fetch_field mysql_field_seek mysql_free_result mysql_field_name mysql_field_table mysql_field_len mysql_field_type mysql_field_flags mysql_escape_string mysql_real_escape_string mysql_stat mysql_thread_id mysql_client_encoding mysql_get_client_info mysql_get_host_info mysql_get_proto_info mysql_get_server_info mysql_info mysql mysql_fieldname mysql_fieldtable mysql_fieldlen mysql_fieldtype mysql_fieldflags mysql_selectdb mysql_createdb mysql_dropdb mysql_freeresult mysql_numfields mysql_numrows mysql_listdbs mysql_listtables mysql_listfields mysql_db_name mysql_dbname mysql_tablename mysql_table_name pg_connect pg_pconnect pg_close pg_connection_status pg_connection_busy pg_connection_reset pg_host pg_dbname pg_port pg_tty pg_options pg_ping pg_query pg_send_query pg_cancel_query pg_fetch_result pg_fetch_row pg_fetch_assoc pg_fetch_array pg_fetch_object pg_fetch_all pg_affected_rows pg_get_result pg_result_seek pg_result_status pg_free_result pg_last_oid pg_num_rows pg_num_fields pg_field_name pg_field_num pg_field_size pg_field_type pg_field_prtlen pg_field_is_null pg_get_notify pg_get_pid pg_result_error pg_last_error pg_last_notice pg_put_line pg_end_copy pg_copy_to pg_copy_from pg_trace pg_untrace pg_lo_create pg_lo_unlink pg_lo_open pg_lo_close pg_lo_read pg_lo_write pg_lo_read_all pg_lo_import pg_lo_export pg_lo_seek pg_lo_tell pg_escape_string pg_escape_bytea pg_unescape_bytea pg_client_encoding pg_set_client_encoding pg_meta_data pg_convert pg_insert pg_update pg_delete pg_select pg_exec pg_getlastoid pg_cmdtuples pg_errormessage pg_numrows pg_numfields pg_fieldname pg_fieldsize pg_fieldtype pg_fieldnum pg_fieldprtlen pg_fieldisnull pg_freeresult pg_result pg_loreadall pg_locreate pg_lounlink pg_loopen pg_loclose pg_loread pg_lowrite pg_loimport pg_loexport echo print global static exit array empty eval isset unset die include require include_once require_once"),
25
- multiLineStrings: true,
26
- hooks: {
27
- "$": function(stream) {
28
- stream.eatWhile(/[\w\$_]/);
29
- return "variable-2";
30
- },
31
- "<": function(stream, state) {
32
- if (stream.match(/<</)) {
33
- stream.eatWhile(/[\w\.]/);
34
- state.tokenize = heredoc(stream.current().slice(3));
35
- return state.tokenize(stream, state);
36
- }
37
- return false;
38
- },
39
- "#": function(stream) {
40
- while (!stream.eol() && !stream.match("?>", false)) stream.next();
41
- return "comment";
42
- },
43
- "/": function(stream) {
44
- if (stream.eat("/")) {
45
- while (!stream.eol() && !stream.match("?>", false)) stream.next();
46
- return "comment";
47
- }
48
- return false;
49
- }
50
- }
51
- };
52
-
53
- CodeMirror.defineMode("php", function(config, parserConfig) {
54
- var htmlMode = CodeMirror.getMode(config, "text/html");
55
- var phpMode = CodeMirror.getMode(config, phpConfig);
56
-
57
- function dispatch(stream, state) {
58
- var isPHP = state.curMode == phpMode;
59
- if (stream.sol() && state.pending != '"') state.pending = null;
60
- if (!isPHP) {
61
- if (stream.match(/^<\?\w*/)) {
62
- state.curMode = phpMode;
63
- state.curState = state.php;
64
- return "meta";
65
- }
66
- if (state.pending == '"') {
67
- while (!stream.eol() && stream.next() != '"') {}
68
- var style = "string";
69
- } else if (state.pending && stream.pos < state.pending.end) {
70
- stream.pos = state.pending.end;
71
- var style = state.pending.style;
72
- } else {
73
- var style = htmlMode.token(stream, state.curState);
74
- }
75
- state.pending = null;
76
- var cur = stream.current(), openPHP = cur.search(/<\?/);
77
- if (openPHP != -1) {
78
- if (style == "string" && /\"$/.test(cur) && !/\?>/.test(cur)) state.pending = '"';
79
- else state.pending = {end: stream.pos, style: style};
80
- stream.backUp(cur.length - openPHP);
81
- }
82
- return style;
83
- } else if (isPHP && state.php.tokenize == null && stream.match("?>")) {
84
- state.curMode = htmlMode;
85
- state.curState = state.html;
86
- return "meta";
87
- } else {
88
- return phpMode.token(stream, state.curState);
89
- }
90
- }
91
-
92
- return {
93
- startState: function() {
94
- var html = CodeMirror.startState(htmlMode), php = CodeMirror.startState(phpMode);
95
- return {html: html,
96
- php: php,
97
- curMode: parserConfig.startOpen ? phpMode : htmlMode,
98
- curState: parserConfig.startOpen ? php : html,
99
- pending: null};
100
- },
101
-
102
- copyState: function(state) {
103
- var html = state.html, htmlNew = CodeMirror.copyState(htmlMode, html),
104
- php = state.php, phpNew = CodeMirror.copyState(phpMode, php), cur;
105
- if (state.curMode == htmlMode) cur = htmlNew;
106
- else cur = phpNew;
107
- return {html: htmlNew, php: phpNew, curMode: state.curMode, curState: cur,
108
- pending: state.pending};
109
- },
110
-
111
- token: dispatch,
112
-
113
- indent: function(state, textAfter) {
114
- if ((state.curMode != phpMode && /^\s*<\//.test(textAfter)) ||
115
- (state.curMode == phpMode && /^\?>/.test(textAfter)))
116
- return htmlMode.indent(state.html, textAfter);
117
- return state.curMode.indent(state.curState, textAfter);
118
- },
119
-
120
- electricChars: "/{}:",
121
-
122
- innerMode: function(state) { return {state: state.curState, mode: state.curMode}; }
123
- };
124
- }, "htmlmixed", "clike");
125
-
126
- CodeMirror.defineMIME("application/x-httpd-php", "php");
127
- CodeMirror.defineMIME("application/x-httpd-php-open", {name: "php", startOpen: true});
128
- CodeMirror.defineMIME("text/x-php", phpConfig);
129
- })();
1
+ (function() {
2
+ function keywords(str) {
3
+ var obj = {}, words = str.split(" ");
4
+ for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
5
+ return obj;
6
+ }
7
+ function heredoc(delim) {
8
+ return function(stream, state) {
9
+ if (stream.match(delim)) state.tokenize = null;
10
+ else stream.skipToEnd();
11
+ return "string";
12
+ };
13
+ }
14
+ var phpConfig = {
15
+ name: "clike",
16
+ keywords: keywords("abstract and array as break case catch class clone const continue declare default " +
17
+ "do else elseif enddeclare endfor endforeach endif endswitch endwhile extends final " +
18
+ "for foreach function global goto if implements interface instanceof namespace " +
19
+ "new or private protected public static switch throw trait try use var while xor " +
20
+ "die echo empty exit eval include include_once isset list require require_once return " +
21
+ "print unset __halt_compiler self static parent"),
22
+ blockKeywords: keywords("catch do else elseif for foreach if switch try while"),
23
+ atoms: keywords("true false null TRUE FALSE NULL __CLASS__ __DIR__ __FILE__ __LINE__ __METHOD__ __FUNCTION__ __NAMESPACE__"),
24
+ builtin: keywords("func_num_args func_get_arg func_get_args strlen strcmp strncmp strcasecmp strncasecmp each error_reporting define defined trigger_error user_error set_error_handler restore_error_handler get_declared_classes get_loaded_extensions extension_loaded get_extension_funcs debug_backtrace constant bin2hex sleep usleep time mktime gmmktime strftime gmstrftime strtotime date gmdate getdate localtime checkdate flush wordwrap htmlspecialchars htmlentities html_entity_decode md5 md5_file crc32 getimagesize image_type_to_mime_type phpinfo phpversion phpcredits strnatcmp strnatcasecmp substr_count strspn strcspn strtok strtoupper strtolower strpos strrpos strrev hebrev hebrevc nl2br basename dirname pathinfo stripslashes stripcslashes strstr stristr strrchr str_shuffle str_word_count strcoll substr substr_replace quotemeta ucfirst ucwords strtr addslashes addcslashes rtrim str_replace str_repeat count_chars chunk_split trim ltrim strip_tags similar_text explode implode setlocale localeconv parse_str str_pad chop strchr sprintf printf vprintf vsprintf sscanf fscanf parse_url urlencode urldecode rawurlencode rawurldecode readlink linkinfo link unlink exec system escapeshellcmd escapeshellarg passthru shell_exec proc_open proc_close rand srand getrandmax mt_rand mt_srand mt_getrandmax base64_decode base64_encode abs ceil floor round is_finite is_nan is_infinite bindec hexdec octdec decbin decoct dechex base_convert number_format fmod ip2long long2ip getenv putenv getopt microtime gettimeofday getrusage uniqid quoted_printable_decode set_time_limit get_cfg_var magic_quotes_runtime set_magic_quotes_runtime get_magic_quotes_gpc get_magic_quotes_runtime import_request_variables error_log serialize unserialize memory_get_usage var_dump var_export debug_zval_dump print_r highlight_file show_source highlight_string ini_get ini_get_all ini_set ini_alter ini_restore get_include_path set_include_path restore_include_path setcookie header headers_sent connection_aborted connection_status ignore_user_abort parse_ini_file is_uploaded_file move_uploaded_file intval floatval doubleval strval gettype settype is_null is_resource is_bool is_long is_float is_int is_integer is_double is_real is_numeric is_string is_array is_object is_scalar ereg ereg_replace eregi eregi_replace split spliti join sql_regcase dl pclose popen readfile rewind rmdir umask fclose feof fgetc fgets fgetss fread fopen fpassthru ftruncate fstat fseek ftell fflush fwrite fputs mkdir rename copy tempnam tmpfile file file_get_contents stream_select stream_context_create stream_context_set_params stream_context_set_option stream_context_get_options stream_filter_prepend stream_filter_append fgetcsv flock get_meta_tags stream_set_write_buffer set_file_buffer set_socket_blocking stream_set_blocking socket_set_blocking stream_get_meta_data stream_register_wrapper stream_wrapper_register stream_set_timeout socket_set_timeout socket_get_status realpath fnmatch fsockopen pfsockopen pack unpack get_browser crypt opendir closedir chdir getcwd rewinddir readdir dir glob fileatime filectime filegroup fileinode filemtime fileowner fileperms filesize filetype file_exists is_writable is_writeable is_readable is_executable is_file is_dir is_link stat lstat chown touch clearstatcache mail ob_start ob_flush ob_clean ob_end_flush ob_end_clean ob_get_flush ob_get_clean ob_get_length ob_get_level ob_get_status ob_get_contents ob_implicit_flush ob_list_handlers ksort krsort natsort natcasesort asort arsort sort rsort usort uasort uksort shuffle array_walk count end prev next reset current key min max in_array array_search extract compact array_fill range array_multisort array_push array_pop array_shift array_unshift array_splice array_slice array_merge array_merge_recursive array_keys array_values array_count_values array_reverse array_reduce array_pad array_flip array_change_key_case array_rand array_unique array_intersect array_intersect_assoc array_diff array_diff_assoc array_sum array_filter array_map array_chunk array_key_exists pos sizeof key_exists assert assert_options version_compare ftok str_rot13 aggregate session_name session_module_name session_save_path session_id session_regenerate_id session_decode session_register session_unregister session_is_registered session_encode session_start session_destroy session_unset session_set_save_handler session_cache_limiter session_cache_expire session_set_cookie_params session_get_cookie_params session_write_close preg_match preg_match_all preg_replace preg_replace_callback preg_split preg_quote preg_grep overload ctype_alnum ctype_alpha ctype_cntrl ctype_digit ctype_lower ctype_graph ctype_print ctype_punct ctype_space ctype_upper ctype_xdigit virtual apache_request_headers apache_note apache_lookup_uri apache_child_terminate apache_setenv apache_response_headers apache_get_version getallheaders mysql_connect mysql_pconnect mysql_close mysql_select_db mysql_create_db mysql_drop_db mysql_query mysql_unbuffered_query mysql_db_query mysql_list_dbs mysql_list_tables mysql_list_fields mysql_list_processes mysql_error mysql_errno mysql_affected_rows mysql_insert_id mysql_result mysql_num_rows mysql_num_fields mysql_fetch_row mysql_fetch_array mysql_fetch_assoc mysql_fetch_object mysql_data_seek mysql_fetch_lengths mysql_fetch_field mysql_field_seek mysql_free_result mysql_field_name mysql_field_table mysql_field_len mysql_field_type mysql_field_flags mysql_escape_string mysql_real_escape_string mysql_stat mysql_thread_id mysql_client_encoding mysql_get_client_info mysql_get_host_info mysql_get_proto_info mysql_get_server_info mysql_info mysql mysql_fieldname mysql_fieldtable mysql_fieldlen mysql_fieldtype mysql_fieldflags mysql_selectdb mysql_createdb mysql_dropdb mysql_freeresult mysql_numfields mysql_numrows mysql_listdbs mysql_listtables mysql_listfields mysql_db_name mysql_dbname mysql_tablename mysql_table_name pg_connect pg_pconnect pg_close pg_connection_status pg_connection_busy pg_connection_reset pg_host pg_dbname pg_port pg_tty pg_options pg_ping pg_query pg_send_query pg_cancel_query pg_fetch_result pg_fetch_row pg_fetch_assoc pg_fetch_array pg_fetch_object pg_fetch_all pg_affected_rows pg_get_result pg_result_seek pg_result_status pg_free_result pg_last_oid pg_num_rows pg_num_fields pg_field_name pg_field_num pg_field_size pg_field_type pg_field_prtlen pg_field_is_null pg_get_notify pg_get_pid pg_result_error pg_last_error pg_last_notice pg_put_line pg_end_copy pg_copy_to pg_copy_from pg_trace pg_untrace pg_lo_create pg_lo_unlink pg_lo_open pg_lo_close pg_lo_read pg_lo_write pg_lo_read_all pg_lo_import pg_lo_export pg_lo_seek pg_lo_tell pg_escape_string pg_escape_bytea pg_unescape_bytea pg_client_encoding pg_set_client_encoding pg_meta_data pg_convert pg_insert pg_update pg_delete pg_select pg_exec pg_getlastoid pg_cmdtuples pg_errormessage pg_numrows pg_numfields pg_fieldname pg_fieldsize pg_fieldtype pg_fieldnum pg_fieldprtlen pg_fieldisnull pg_freeresult pg_result pg_loreadall pg_locreate pg_lounlink pg_loopen pg_loclose pg_loread pg_lowrite pg_loimport pg_loexport echo print global static exit array empty eval isset unset die include require include_once require_once"),
25
+ multiLineStrings: true,
26
+ hooks: {
27
+ "$": function(stream) {
28
+ stream.eatWhile(/[\w\$_]/);
29
+ return "variable-2";
30
+ },
31
+ "<": function(stream, state) {
32
+ if (stream.match(/<</)) {
33
+ stream.eatWhile(/[\w\.]/);
34
+ state.tokenize = heredoc(stream.current().slice(3));
35
+ return state.tokenize(stream, state);
36
+ }
37
+ return false;
38
+ },
39
+ "#": function(stream) {
40
+ while (!stream.eol() && !stream.match("?>", false)) stream.next();
41
+ return "comment";
42
+ },
43
+ "/": function(stream) {
44
+ if (stream.eat("/")) {
45
+ while (!stream.eol() && !stream.match("?>", false)) stream.next();
46
+ return "comment";
47
+ }
48
+ return false;
49
+ }
50
+ }
51
+ };
52
+
53
+ CodeMirror.defineMode("php", function(config, parserConfig) {
54
+ var htmlMode = CodeMirror.getMode(config, "text/html");
55
+ var phpMode = CodeMirror.getMode(config, phpConfig);
56
+
57
+ function dispatch(stream, state) {
58
+ var isPHP = state.curMode == phpMode;
59
+ if (stream.sol() && state.pending != '"') state.pending = null;
60
+ if (!isPHP) {
61
+ if (stream.match(/^<\?\w*/)) {
62
+ state.curMode = phpMode;
63
+ state.curState = state.php;
64
+ return "meta";
65
+ }
66
+ if (state.pending == '"') {
67
+ while (!stream.eol() && stream.next() != '"') {}
68
+ var style = "string";
69
+ } else if (state.pending && stream.pos < state.pending.end) {
70
+ stream.pos = state.pending.end;
71
+ var style = state.pending.style;
72
+ } else {
73
+ var style = htmlMode.token(stream, state.curState);
74
+ }
75
+ state.pending = null;
76
+ var cur = stream.current(), openPHP = cur.search(/<\?/);
77
+ if (openPHP != -1) {
78
+ if (style == "string" && /\"$/.test(cur) && !/\?>/.test(cur)) state.pending = '"';
79
+ else state.pending = {end: stream.pos, style: style};
80
+ stream.backUp(cur.length - openPHP);
81
+ }
82
+ return style;
83
+ } else if (isPHP && state.php.tokenize == null && stream.match("?>")) {
84
+ state.curMode = htmlMode;
85
+ state.curState = state.html;
86
+ return "meta";
87
+ } else {
88
+ return phpMode.token(stream, state.curState);
89
+ }
90
+ }
91
+
92
+ return {
93
+ startState: function() {
94
+ var html = CodeMirror.startState(htmlMode), php = CodeMirror.startState(phpMode);
95
+ return {html: html,
96
+ php: php,
97
+ curMode: parserConfig.startOpen ? phpMode : htmlMode,
98
+ curState: parserConfig.startOpen ? php : html,
99
+ pending: null};
100
+ },
101
+
102
+ copyState: function(state) {
103
+ var html = state.html, htmlNew = CodeMirror.copyState(htmlMode, html),
104
+ php = state.php, phpNew = CodeMirror.copyState(phpMode, php), cur;
105
+ if (state.curMode == htmlMode) cur = htmlNew;
106
+ else cur = phpNew;
107
+ return {html: htmlNew, php: phpNew, curMode: state.curMode, curState: cur,
108
+ pending: state.pending};
109
+ },
110
+
111
+ token: dispatch,
112
+
113
+ indent: function(state, textAfter) {
114
+ if ((state.curMode != phpMode && /^\s*<\//.test(textAfter)) ||
115
+ (state.curMode == phpMode && /^\?>/.test(textAfter)))
116
+ return htmlMode.indent(state.html, textAfter);
117
+ return state.curMode.indent(state.curState, textAfter);
118
+ },
119
+
120
+ electricChars: "/{}:",
121
+
122
+ innerMode: function(state) { return {state: state.curState, mode: state.curMode}; }
123
+ };
124
+ }, "htmlmixed", "clike");
125
+
126
+ CodeMirror.defineMIME("application/x-httpd-php", "php");
127
+ CodeMirror.defineMIME("application/x-httpd-php-open", {name: "php", startOpen: true});
128
+ CodeMirror.defineMIME("text/x-php", phpConfig);
129
+ })();