WP Google Maps - Version 8.1.18

Version Description

Please update to 8.1.18 or above to ensure you are using the latest security enhancements.

Download this release

Release Info

Developer DylanAuty
Plugin Icon 128x128 WP Google Maps
Version 8.1.18
Comparing to
See all releases

Code changes from version 8.1.17 to 8.1.18

base/classes/widget_module.class.php CHANGED
@@ -35,20 +35,16 @@ class wpgmza_widget extends WP_Widget {
35
  $title = apply_filters( 'widget_title', $instance['title'] );
36
 
37
  echo $args['before_widget'];
38
- if ( ! empty( $title ) )
39
- echo $args['before_title'] . $title . $args['after_title'];
 
40
 
41
- if(!isset($instance['selection']))
42
- {
43
  global $wpdb;
44
  $instance['selection'] = $wpdb->get_var("SELECT id FROM {$wpdb->prefix}wpgmza_maps ORDER BY id DESC LIMIT 1");
45
  }
46
 
47
- echo do_shortcode("[wpgmza id='".$instance['selection']."']");
48
-
49
-
50
-
51
-
52
 
53
  echo $args['after_widget'];
54
  }
35
  $title = apply_filters( 'widget_title', $instance['title'] );
36
 
37
  echo $args['before_widget'];
38
+ if (!empty($title)){
39
+ echo $args['before_title'] . esc_html($title) . $args['after_title'];
40
+ }
41
 
42
+ if(!isset($instance['selection'])){
 
43
  global $wpdb;
44
  $instance['selection'] = $wpdb->get_var("SELECT id FROM {$wpdb->prefix}wpgmza_maps ORDER BY id DESC LIMIT 1");
45
  }
46
 
47
+ echo do_shortcode("[wpgmza id='".intval($instance['selection'])."']");
 
 
 
 
48
 
49
  echo $args['after_widget'];
50
  }
html/map-edit-page/map-edit-page.html.php CHANGED
@@ -153,7 +153,7 @@
153
  _e("copy this into your post or page to display the map", "wp-google-maps");
154
 
155
  // NB: I recommend adding a unique ID or class to this link and using the DOM to set the href rather than mixing content with logic here. - Perry
156
- echo ". ".__(sprintf("Or <a href='%s' target='BLANK'>click here to automatically create a Map Page now</a>.","admin.php?page=wp-google-maps-menu&amp;action=create-map-page&amp;map_id=".$_REQUEST['map_id']),"wp-google-maps");
157
 
158
  ?>
159
  </i>
@@ -1297,7 +1297,7 @@
1297
  </div>
1298
  </fieldset>
1299
 
1300
- <fieldset class="wpgmza-pro-feature">
1301
  <legend>
1302
  <?php
1303
  _e("Enable Polygon Labels", "wp-google-maps");
153
  _e("copy this into your post or page to display the map", "wp-google-maps");
154
 
155
  // NB: I recommend adding a unique ID or class to this link and using the DOM to set the href rather than mixing content with logic here. - Perry
156
+ echo ". ".__(sprintf("Or <a href='%s' target='BLANK'>click here to automatically create a Map Page now</a>.","admin.php?page=wp-google-maps-menu&amp;action=create-map-page&amp;map_id=".intval($_REQUEST['map_id'])),"wp-google-maps");
157
 
158
  ?>
159
  </i>
1297
  </div>
1298
  </fieldset>
1299
 
1300
+ <fieldset class="wpgmza-pro-feature" data-wpgmza-require-engine="google-maps">
1301
  <legend>
1302
  <?php
1303
  _e("Enable Polygon Labels", "wp-google-maps");
html/map-list-page.html.php CHANGED
@@ -30,8 +30,8 @@
30
  <div class='wpgmza-review-nag'>
31
  <?php
32
  echo sprintf( __( '<h3>We need your love!</h3><p>If you are enjoying our plugin, please consider <a href="%1$s" target="_blank" class="button-border button-border__green">reviewing WP Google Maps</a>. It would mean the world to us! If you are experiencing issues with the plugin, please <a href="%2$s" target="_blank" class="button-border button-border__green">contact us</a> and we will help you as soon as humanly possible!</p>', 'wp-google-maps' ),
33
- 'https://wordpress.org/support/view/plugin-reviews/wp-google-maps?filter=5',
34
- 'http://www.wpgmaps.com/contact-us/'
35
  );
36
  ?>
37
 
30
  <div class='wpgmza-review-nag'>
31
  <?php
32
  echo sprintf( __( '<h3>We need your love!</h3><p>If you are enjoying our plugin, please consider <a href="%1$s" target="_blank" class="button-border button-border__green">reviewing WP Google Maps</a>. It would mean the world to us! If you are experiencing issues with the plugin, please <a href="%2$s" target="_blank" class="button-border button-border__green">contact us</a> and we will help you as soon as humanly possible!</p>', 'wp-google-maps' ),
33
+ esc_url('https://wordpress.org/support/view/plugin-reviews/wp-google-maps?filter=5'),
34
+ esc_url('http://www.wpgmaps.com/contact-us/')
35
  );
36
  ?>
37
 
includes/class.maps-engine-dialog.php CHANGED
@@ -29,7 +29,7 @@ class MapsEngineDialog
29
 
30
  $settings = get_option('WPGMZA_OTHER_SETTINGS');
31
 
32
- $settings['wpgmza_maps_engine'] = $_POST['engine'];
33
  $settings['wpgmza_maps_engine_dialog_done'] = true;
34
 
35
  update_option('WPGMZA_OTHER_SETTINGS', $settings);
29
 
30
  $settings = get_option('WPGMZA_OTHER_SETTINGS');
31
 
32
+ $settings['wpgmza_maps_engine'] = sanitize_text_field($_POST['engine']);
33
  $settings['wpgmza_maps_engine_dialog_done'] = true;
34
 
35
  update_option('WPGMZA_OTHER_SETTINGS', $settings);
includes/class.script-loader.php CHANGED
@@ -590,19 +590,19 @@ class ScriptLoader
590
  switch($_GET['action'])
591
  {
592
  case "add_poly":
593
- wpgmaps_b_admin_add_poly_javascript($_GET['map_id']);
594
  break;
595
 
596
  case "edit_poly":
597
- wpgmaps_b_admin_edit_poly_javascript($_GET['map_id'], $_GET['poly_id']);
598
  break;
599
 
600
  case "add_polyline":
601
- wpgmaps_b_admin_add_polyline_javascript($_GET['map_id']);
602
  break;
603
 
604
  case "edit_polyline":
605
- wpgmaps_b_admin_edit_polyline_javascript($_GET['map_id'], $_GET['poly_id']);
606
  break;
607
 
608
  default:
590
  switch($_GET['action'])
591
  {
592
  case "add_poly":
593
+ wpgmaps_b_admin_add_poly_javascript(intval($_GET['map_id']));
594
  break;
595
 
596
  case "edit_poly":
597
+ wpgmaps_b_admin_edit_poly_javascript(intval($_GET['map_id']), intval($_GET['poly_id']));
598
  break;
599
 
600
  case "add_polyline":
601
+ wpgmaps_b_admin_add_polyline_javascript(intval($_GET['map_id']));
602
  break;
603
 
604
  case "edit_polyline":
605
+ wpgmaps_b_admin_edit_polyline_javascript(intval($_GET['map_id']), intval($_GET['poly_id']));
606
  break;
607
 
608
  default:
includes/class.selector-to-xpath.php CHANGED
@@ -15,7 +15,7 @@ if(!defined('ABSPATH'))
15
  */
16
  function trace($str)
17
  {
18
- echo $str . "\r\n";
19
  }
20
 
21
  /**
15
  */
16
  function trace($str)
17
  {
18
+ echo esc_html($str) . "\r\n";
19
  }
20
 
21
  /**
includes/class.theme-panel.php CHANGED
@@ -18,9 +18,14 @@ class ThemePanel extends DOMDocument
18
  wp_enqueue_style('owl-carousel', $base . 'lib/owl.carousel.css', array(), $wpgmza->getBasicVersion());
19
  wp_enqueue_style('owl-carousel_theme', $base . 'lib/owl.theme.css', array(), $wpgmza->getBasicVersion());
20
 
21
- wp_enqueue_script('codemirror', $base . 'lib/codemirror.js', array(), $wpgmza->getBasicVersion());
22
- wp_enqueue_style('codemirror', $base . 'lib/codemirror.css', array(), $wpgmza->getBasicVersion());
23
- wp_enqueue_script('codemirror-javascript', $base . 'lib/mode/javascript.js', array(), $wpgmza->getBasicVersion());
 
 
 
 
 
24
 
25
  if($map)
26
  $this->populate($map);
18
  wp_enqueue_style('owl-carousel', $base . 'lib/owl.carousel.css', array(), $wpgmza->getBasicVersion());
19
  wp_enqueue_style('owl-carousel_theme', $base . 'lib/owl.theme.css', array(), $wpgmza->getBasicVersion());
20
 
21
+
22
+ /*
23
+ * Deprecating code mirror enqueue, we don't actually use this with V8
24
+ *
25
+ * This will be reintroduced with V9, but we will need to use the included version from WordPress core
26
+ *
27
+ * Since 8.1.18, code removed, comment left as placeholder
28
+ */
29
 
30
  if($map)
31
  $this->populate($map);
includes/compat/backwards_compat_v6.php CHANGED
@@ -51,7 +51,7 @@ add_action("wpgmza_check_map_editor_backwards_compat", "wpgmza_check_map_editor_
51
  */
52
  function wpgmza_check_map_editor_backwards_compat_v6()
53
  {
54
- $map_id = isset($_GET['map_id']) ? (int)$_GET['map_id'] : null;
55
 
56
  if(isset($_GET['action'])){
57
  if ($_GET['action'] == "edit" && isset($_GET['map_id']) && wpgmza_check_pro_compat_required_v6()) {
51
  */
52
  function wpgmza_check_map_editor_backwards_compat_v6()
53
  {
54
+ $map_id = isset($_GET['map_id']) ? (int) intval($_GET['map_id']) : null;
55
 
56
  if(isset($_GET['action'])){
57
  if ($_GET['action'] == "edit" && isset($_GET['map_id']) && wpgmza_check_pro_compat_required_v6()) {
includes/compat/class.pro-below-8.1-compatibility.php CHANGED
@@ -172,7 +172,7 @@ function wpgmza_b_pro_edit_poly($mid) {
172
  <h2>".__("Edit Polygon","wp-google-maps")."</h2>
173
  <form action='?page=wp-google-maps-menu&action=edit&map_id=".esc_attr($mid)."' method='post' id='wpgmaps_edit_poly_form'>
174
  <input type='hidden' name='wpgmaps_map_id' id='wpgmaps_map_id' value='".esc_attr($mid)."' />
175
- <input type='hidden' name='wpgmaps_poly_id' id='wpgmaps_poly_id' value='".esc_attr($_GET['poly_id'])."' />
176
  <input type='hidden' name='wpgmaps_polygon-nonce' id='wpgmaps_b_nonce' value='".wp_create_nonce( 'wpgmaps_polygon-nonce' )."' />
177
 
178
  <table class='wpgmza-listing-comp' style='width:30%;float:left; height:400px;'>
@@ -237,7 +237,7 @@ function wpgmza_b_pro_edit_poly($mid) {
237
 
238
  <div class='clear'></div>
239
  <p style='clear: both;' >Polygon data:<br /><textarea name=\"wpgmza_polygon\" id=\"poly_line_list\" style=\"height:100px; background-color:#FFF; padding:5px; overflow:auto;\"></textarea>
240
- <!-- <p style='clear: both;' >Polygon data (inner):<br /><textarea name=\"wpgmza_polygon_inner\" id=\"poly_line_list_inner\" style=\"width:90%; height:100px; border:1px solid #ccc; background-color:#FFF; padding:5px; overflow:auto;\">".$pol->innerpolydata."</textarea> -->
241
  <p class='submit'><a href='javascript:history.back();' class='button button-secondary' title='".__("Cancel")."'>".__("Cancel")."</a> <input type='submit' name='wpgmza_edit_poly' class='button-primary' value='".__("Save Polygon","wp-google-maps")." &raquo;' /></p>
242
 
243
  </form>
@@ -513,12 +513,12 @@ function wpgmza_b_return_polygon_list($map_id,$admin = true,$width = "100%") {
513
  if (isset($result->polyname) && $result->polyname != "") { $polygon_name = $result->polyname; } else { $polygon_name = "Polygon".$result->id; }
514
 
515
  $wpgmza_tmp .= "
516
- <tr id=\"wpgmza_poly_tr_".$result->id."\">
517
- <td height=\"40\">".$result->id."</td>
518
  <td height=\"40\">".esc_attr(stripslashes($polygon_name))."</td>
519
  <td width='170' align='left'>
520
- <a href=\"".get_option('siteurl')."/wp-admin/admin.php?page=wp-google-maps-menu&action=edit_poly&map_id=".$map_id."&poly_id=".$result->id."\" title=\"".__("Edit","wp-google-maps")."\" class=\"wpgmza_edit_poly_btn button\" id=\"".$result->id."\"><i class=\"fa fa-edit\"> </i></a>
521
- <a href=\"javascript:void(0);\" title=\"".__("Delete this polygon","wp-google-maps")."\" class=\"wpgmza_poly_del_btn button\" id=\"".$result->id."\"><i class=\"fa fa-times\"> </i></a>
522
  </td>
523
  </tr>";
524
 
@@ -705,6 +705,8 @@ function wpgmza_b_pro_edit_polyline($mid) {
705
  $res = wpgmza_get_map_data($mid);
706
  $pol = wpgmza_b_return_polyline_options(sanitize_text_field($_GET['poly_id']));
707
 
 
 
708
  echo "
709
 
710
 
@@ -715,7 +717,7 @@ function wpgmza_b_pro_edit_polyline($mid) {
715
  <h2>".__("Edit Polyline","wp-google-maps")."</h2>
716
  <form action='?page=wp-google-maps-menu&action=edit&map_id=".esc_attr($mid)."' method='post' id='wpgmaps_edit_poly_form'>
717
  <input type='hidden' name='wpgmaps_map_id' id='wpgmaps_map_id' value='".esc_attr($mid)."' />
718
- <input type='hidden' name='wpgmaps_poly_id' id='wpgmaps_poly_id' value='".esc_attr($_GET['poly_id'])."' />
719
  <input type='hidden' name='wpgmaps_polyline-nonce' id='wpgmaps_b_nonce' value='".wp_create_nonce( 'wpgmaps_polyline-nonce' )."' />
720
  <table class='wpgmza-listing-comp' style='width:30%;float:left;'>
721
  <tr>
@@ -1025,12 +1027,12 @@ function wpgmza_b_return_polyline_list($map_id,$admin = true,$width = "100%") {
1025
  if (isset($result->polyname) && $result->polyname != "") { $poly_name = $result->polyname; } else { $poly_name = "Polyline".$result->id; }
1026
 
1027
  $wpgmza_tmp .= "
1028
- <tr id=\"wpgmza_poly_tr_".$result->id."\">
1029
- <td height=\"40\">".$result->id."</td>
1030
  <td height=\"40\">".esc_attr(stripslashes($poly_name))."</td>
1031
  <td width='170' align='left'>
1032
- <a href=\"".get_option('siteurl')."/wp-admin/admin.php?page=wp-google-maps-menu&action=edit_polyline&map_id=".$map_id."&poly_id=".$result->id."\" title=\"".__("Edit","wp-google-maps")."\" class=\"wpgmza_edit_poly_btn button\" id=\"".$result->id."\"><i class=\"fa fa-edit\"> </i></a>
1033
- <a href=\"javascript:void(0);\" title=\"".__("Delete this polyline","wp-google-maps")."\" class=\"wpgmza_polyline_del_btn button\" id=\"".$result->id."\"><i class=\"fa fa-times\"> </i></a>
1034
  </td>
1035
  </tr>";
1036
 
@@ -1164,7 +1166,7 @@ function wpgmaps_admin_menu() {
1164
 
1165
  function wpgmaps_menu_layout() {
1166
 
1167
- $map_id = isset($_GET['map_id']) ? (int)$_GET['map_id'] : null;
1168
  $nonce = wp_create_nonce('wpgmza_menu_layout_nonce');
1169
 
1170
  $handle = 'avia-google-maps-api';
@@ -1195,7 +1197,7 @@ function wpgmaps_menu_layout() {
1195
 
1196
  wpgmaps_trash_map($map_id);
1197
 
1198
- $url = get_option('siteurl') . "/wp-admin/admin.php?page=wp-google-maps-menu";
1199
 
1200
  echo "<script>window.location = \"$url\";</script>";
1201
 
@@ -1203,7 +1205,7 @@ function wpgmaps_menu_layout() {
1203
 
1204
  } else {
1205
  $res = wpgmza_get_map_data($map_id);
1206
- echo "<h2>".__("Delete your map","wp-google-maps")."</h2><p>".__("Are you sure you want to delete the map","wp-google-maps")." <strong>\"".$res->map_title."?\"</strong> <br /><a href='?page=wp-google-maps-menu&s=1&action=trash&map_id=".$map_id."&s=1&nonce=$nonce'>".__("Yes","wp-google-maps")."</a> | <a href='?page=wp-google-maps-menu'>".__("No","wp-google-maps")."</a></p>";
1207
  return;
1208
  }
1209
  }
@@ -1346,14 +1348,14 @@ function wpgmaps_list_maps() {
1346
  } else {
1347
  $trashlink = "";
1348
  }
1349
- echo "<tr id=\"record_".$result->id."\">";
1350
- echo "<td class='id column-id'>".$result->id."</td>";
1351
- echo "<td class='map_title column-map_title'><strong><big><a href=\"?page=wp-google-maps-menu&action=edit&map_id=".$result->id."\" title=\"".__("Edit","wp-google-maps")."\">".stripslashes($result->map_title)."</a></big></strong><br /></td>";
1352
- echo "<td class='map_width column-map_width'>".$result->map_width."".stripslashes($result->map_width_type)."</td>";
1353
- echo "<td class='map_width column-map_height'>".$result->map_height."".stripslashes($result->map_height_type)."</td>";
1354
  echo "<td class='type column-type'>".$map_type."</td>";
1355
- echo "<td class='type column-type'><a class='page-title-action' href=\"?page=wp-google-maps-menu&action=edit&map_id=".$result->id."\" title=\"".__("Edit","wp-google-maps")."\">".__("Edit","wp-google-maps")."</a> $trashlink</td>";
1356
- echo "<td class='type column-type'><input class='wpgmza_copy_shortcode' type='text' readonly value='[wpgmza id=\"".$result->id."\"]'/></td>";
1357
  echo "</tr>";
1358
 
1359
 
@@ -1956,7 +1958,7 @@ function wpgmaps_head() {
1956
  }
1957
 
1958
  $marker = \WPGMZA\Marker::createInstance($_POST['wpgmaps_marker_id']);
1959
- $latlng = new \WPGMZA\LatLng($_POST['wpgmaps_marker_lat'], $_POST['wpgmaps_marker_lng']);
1960
 
1961
  if(preg_match(\WPGMZA\LatLng::REGEXP, $marker->address))
1962
  {
@@ -1973,8 +1975,8 @@ function wpgmaps_head() {
1973
  }
1974
  }
1975
 
1976
- $lat = $_POST['wpgmaps_marker_lat'];
1977
- $lng = $_POST['wpgmaps_marker_lng'];
1978
 
1979
  $marker->lat = $lat;
1980
  $marker->lng = $lng;
@@ -2419,10 +2421,10 @@ function wpgmza_settings_page_post()
2419
 
2420
  if (isset($_POST['wpgmza_api_version'])) { $wpgmza->settings['wpgmza_api_version'] = sanitize_text_field($_POST['wpgmza_api_version']); }
2421
  if (isset($_POST['wpgmza_custom_css'])) { $wpgmza->settings['wpgmza_custom_css'] = sanitize_text_field($_POST['wpgmza_custom_css']); }
2422
- if (isset($_POST['wpgmza_custom_js'])) { $wpgmza->settings['wpgmza_custom_js'] = $_POST['wpgmza_custom_js']; }
2423
 
2424
  if (isset($_POST['user_interface_style']))
2425
- $wpgmza->settings['user_interface_style'] = esc_attr($_POST['user_interface_style']);
2426
 
2427
  if (isset($_POST['wpgmza_marker_xml_location'])) { update_option("wpgmza_xml_location",sanitize_text_field($_POST['wpgmza_marker_xml_location'])); }
2428
  if (isset($_POST['wpgmza_marker_xml_url'])) { update_option("wpgmza_xml_url",sanitize_text_field($_POST['wpgmza_marker_xml_url'])); }
172
  <h2>".__("Edit Polygon","wp-google-maps")."</h2>
173
  <form action='?page=wp-google-maps-menu&action=edit&map_id=".esc_attr($mid)."' method='post' id='wpgmaps_edit_poly_form'>
174
  <input type='hidden' name='wpgmaps_map_id' id='wpgmaps_map_id' value='".esc_attr($mid)."' />
175
+ <input type='hidden' name='wpgmaps_poly_id' id='wpgmaps_poly_id' value='".esc_attr(intval($_GET['poly_id']))."' />
176
  <input type='hidden' name='wpgmaps_polygon-nonce' id='wpgmaps_b_nonce' value='".wp_create_nonce( 'wpgmaps_polygon-nonce' )."' />
177
 
178
  <table class='wpgmza-listing-comp' style='width:30%;float:left; height:400px;'>
237
 
238
  <div class='clear'></div>
239
  <p style='clear: both;' >Polygon data:<br /><textarea name=\"wpgmza_polygon\" id=\"poly_line_list\" style=\"height:100px; background-color:#FFF; padding:5px; overflow:auto;\"></textarea>
240
+ <!-- <p style='clear: both;' >Polygon data (inner):<br /><textarea name=\"wpgmza_polygon_inner\" id=\"poly_line_list_inner\" style=\"width:90%; height:100px; border:1px solid #ccc; background-color:#FFF; padding:5px; overflow:auto;\">".esc_textarea($pol->innerpolydata)."</textarea> -->
241
  <p class='submit'><a href='javascript:history.back();' class='button button-secondary' title='".__("Cancel")."'>".__("Cancel")."</a> <input type='submit' name='wpgmza_edit_poly' class='button-primary' value='".__("Save Polygon","wp-google-maps")." &raquo;' /></p>
242
 
243
  </form>
513
  if (isset($result->polyname) && $result->polyname != "") { $polygon_name = $result->polyname; } else { $polygon_name = "Polygon".$result->id; }
514
 
515
  $wpgmza_tmp .= "
516
+ <tr id=\"wpgmza_poly_tr_".intval($result->id)."\">
517
+ <td height=\"40\">".intval($result->id)."</td>
518
  <td height=\"40\">".esc_attr(stripslashes($polygon_name))."</td>
519
  <td width='170' align='left'>
520
+ <a href=\"".esc_url(get_option('siteurl'))."/wp-admin/admin.php?page=wp-google-maps-menu&action=edit_poly&map_id=".intval($map_id)."&poly_id=".intval($result->id)."\" title=\"".__("Edit","wp-google-maps")."\" class=\"wpgmza_edit_poly_btn button\" id=\"".intval($result->id)."\"><i class=\"fa fa-edit\"> </i></a>
521
+ <a href=\"javascript:void(0);\" title=\"".__("Delete this polygon","wp-google-maps")."\" class=\"wpgmza_poly_del_btn button\" id=\"".intval($result->id)."\"><i class=\"fa fa-times\"> </i></a>
522
  </td>
523
  </tr>";
524
 
705
  $res = wpgmza_get_map_data($mid);
706
  $pol = wpgmza_b_return_polyline_options(sanitize_text_field($_GET['poly_id']));
707
 
708
+ $pol->polydata = esc_textarea($pol->polydata);
709
+
710
  echo "
711
 
712
 
717
  <h2>".__("Edit Polyline","wp-google-maps")."</h2>
718
  <form action='?page=wp-google-maps-menu&action=edit&map_id=".esc_attr($mid)."' method='post' id='wpgmaps_edit_poly_form'>
719
  <input type='hidden' name='wpgmaps_map_id' id='wpgmaps_map_id' value='".esc_attr($mid)."' />
720
+ <input type='hidden' name='wpgmaps_poly_id' id='wpgmaps_poly_id' value='".esc_attr(intval($_GET['poly_id']))."' />
721
  <input type='hidden' name='wpgmaps_polyline-nonce' id='wpgmaps_b_nonce' value='".wp_create_nonce( 'wpgmaps_polyline-nonce' )."' />
722
  <table class='wpgmza-listing-comp' style='width:30%;float:left;'>
723
  <tr>
1027
  if (isset($result->polyname) && $result->polyname != "") { $poly_name = $result->polyname; } else { $poly_name = "Polyline".$result->id; }
1028
 
1029
  $wpgmza_tmp .= "
1030
+ <tr id=\"wpgmza_poly_tr_".intval($result->id)."\">
1031
+ <td height=\"40\">".intval($result->id)."</td>
1032
  <td height=\"40\">".esc_attr(stripslashes($poly_name))."</td>
1033
  <td width='170' align='left'>
1034
+ <a href=\"".esc_url(get_option('siteurl'))."/wp-admin/admin.php?page=wp-google-maps-menu&action=edit_polyline&map_id=".intval($map_id)."&poly_id=".intval($result->id)."\" title=\"".__("Edit","wp-google-maps")."\" class=\"wpgmza_edit_poly_btn button\" id=\"".intval($result->id)."\"><i class=\"fa fa-edit\"> </i></a>
1035
+ <a href=\"javascript:void(0);\" title=\"".__("Delete this polyline","wp-google-maps")."\" class=\"wpgmza_polyline_del_btn button\" id=\"".intval($result->id)."\"><i class=\"fa fa-times\"> </i></a>
1036
  </td>
1037
  </tr>";
1038
 
1166
 
1167
  function wpgmaps_menu_layout() {
1168
 
1169
+ $map_id = isset($_GET['map_id']) ? (int) intval($_GET['map_id']) : null;
1170
  $nonce = wp_create_nonce('wpgmza_menu_layout_nonce');
1171
 
1172
  $handle = 'avia-google-maps-api';
1197
 
1198
  wpgmaps_trash_map($map_id);
1199
 
1200
+ $url = esc_url(get_option('siteurl') . "/wp-admin/admin.php?page=wp-google-maps-menu");
1201
 
1202
  echo "<script>window.location = \"$url\";</script>";
1203
 
1205
 
1206
  } else {
1207
  $res = wpgmza_get_map_data($map_id);
1208
+ echo "<h2>".__("Delete your map","wp-google-maps")."</h2><p>".__("Are you sure you want to delete the map","wp-google-maps")." <strong>\"".strip_tags($res->map_title)."?\"</strong> <br /><a href='?page=wp-google-maps-menu&s=1&action=trash&map_id=".$map_id."&s=1&nonce=$nonce'>".__("Yes","wp-google-maps")."</a> | <a href='?page=wp-google-maps-menu'>".__("No","wp-google-maps")."</a></p>";
1209
  return;
1210
  }
1211
  }
1348
  } else {
1349
  $trashlink = "";
1350
  }
1351
+ echo "<tr id=\"record_".intval($result->id)."\">";
1352
+ echo "<td class='id column-id'>".intval($result->id)."</td>";
1353
+ echo "<td class='map_title column-map_title'><strong><big><a href=\"?page=wp-google-maps-menu&action=edit&map_id=".intval($result->id)."\" title=\"".__("Edit","wp-google-maps")."\">".stripslashes(strip_tags($result->map_title))."</a></big></strong><br /></td>";
1354
+ echo "<td class='map_width column-map_width'>".esc_html($result->map_width)."".stripslashes(esc_html($result->map_width_type))."</td>";
1355
+ echo "<td class='map_width column-map_height'>".esc_html($result->map_height)."".stripslashes(esc_html($result->map_height_type))."</td>";
1356
  echo "<td class='type column-type'>".$map_type."</td>";
1357
+ echo "<td class='type column-type'><a class='page-title-action' href=\"?page=wp-google-maps-menu&action=edit&map_id=".intval($result->id)."\" title=\"".__("Edit","wp-google-maps")."\">".__("Edit","wp-google-maps")."</a> $trashlink</td>";
1358
+ echo "<td class='type column-type'><input class='wpgmza_copy_shortcode' type='text' readonly value='[wpgmza id=\"".intval($result->id)."\"]'/></td>";
1359
  echo "</tr>";
1360
 
1361
 
1958
  }
1959
 
1960
  $marker = \WPGMZA\Marker::createInstance($_POST['wpgmaps_marker_id']);
1961
+ $latlng = new \WPGMZA\LatLng(floatval($_POST['wpgmaps_marker_lat']), floatval($_POST['wpgmaps_marker_lng']));
1962
 
1963
  if(preg_match(\WPGMZA\LatLng::REGEXP, $marker->address))
1964
  {
1975
  }
1976
  }
1977
 
1978
+ $lat = floatval($_POST['wpgmaps_marker_lat']);
1979
+ $lng = floatval($_POST['wpgmaps_marker_lng']);
1980
 
1981
  $marker->lat = $lat;
1982
  $marker->lng = $lng;
2421
 
2422
  if (isset($_POST['wpgmza_api_version'])) { $wpgmza->settings['wpgmza_api_version'] = sanitize_text_field($_POST['wpgmza_api_version']); }
2423
  if (isset($_POST['wpgmza_custom_css'])) { $wpgmza->settings['wpgmza_custom_css'] = sanitize_text_field($_POST['wpgmza_custom_css']); }
2424
+ if (isset($_POST['wpgmza_custom_js'])) { $wpgmza->settings['wpgmza_custom_js'] = sanitize_text_field($_POST['wpgmza_custom_js']); }
2425
 
2426
  if (isset($_POST['user_interface_style']))
2427
+ $wpgmza->settings['user_interface_style'] = esc_attr(sanitize_text_field($_POST['user_interface_style']));
2428
 
2429
  if (isset($_POST['wpgmza_marker_xml_location'])) { update_option("wpgmza_xml_location",sanitize_text_field($_POST['wpgmza_marker_xml_location'])); }
2430
  if (isset($_POST['wpgmza_marker_xml_url'])) { update_option("wpgmza_xml_url",sanitize_text_field($_POST['wpgmza_marker_xml_url'])); }
includes/legacy/functions.circle.php CHANGED
@@ -27,6 +27,7 @@ function wpgmza_b_add_circle($mid)
27
  wpgmaps_b_admin_add_circle_javascript();
28
 
29
  if ($_GET['action'] == "add_circle" && isset($mid)) {
 
30
  $res = wpgmza_get_map_data($mid);
31
  echo "
32
 
@@ -120,8 +121,9 @@ function wpgmza_b_edit_circle($mid)
120
  wpgmaps_b_admin_add_circle_javascript();
121
 
122
  if ($_GET['action'] == "edit_circle" && isset($mid)) {
 
123
  $res = wpgmza_get_map_data($mid);
124
- $circle_id = (int)$_GET['circle_id'];
125
 
126
  $results = $wpdb->get_results("SELECT *, {$wpgmza->spatialFunctionPrefix}AsText(center) AS center FROM $wpgmza_tblname_circles WHERE id = $circle_id");
127
 
@@ -278,6 +280,8 @@ if(!function_exists('wpgmza_get_circles_table'))
278
  {
279
  global $wpdb;
280
  global $wpgmza_tblname_circles;
 
 
281
 
282
  $circles_table = "
283
  <table>
@@ -295,12 +299,14 @@ if(!function_exists('wpgmza_get_circles_table'))
295
  $circles = $wpdb->get_results($stmt);
296
  foreach($circles as $circle)
297
  {
 
 
298
  $circles_table .= "
299
  <tr>
300
  <td>{$circle->id}</td>
301
  <td>{$circle->name}</td>
302
  <td width='170' align='left'>
303
- <a href='" . get_option('siteurl') . "/wp-admin/admin.php?page=wp-google-maps-menu&amp;action=edit_circle&amp;map_id={$map_id}&amp;circle_id={$circle->id}'
304
  title='" . __('Edit', 'wp-google-maps') . "'
305
  class='wpgmza_edit_circle_btn button'
306
  id='{$circle->id}'>
27
  wpgmaps_b_admin_add_circle_javascript();
28
 
29
  if ($_GET['action'] == "add_circle" && isset($mid)) {
30
+ $mid = intval($mid);
31
  $res = wpgmza_get_map_data($mid);
32
  echo "
33
 
121
  wpgmaps_b_admin_add_circle_javascript();
122
 
123
  if ($_GET['action'] == "edit_circle" && isset($mid)) {
124
+ $mid = intval($mid);
125
  $res = wpgmza_get_map_data($mid);
126
+ $circle_id = (int) intval($_GET['circle_id']);
127
 
128
  $results = $wpdb->get_results("SELECT *, {$wpgmza->spatialFunctionPrefix}AsText(center) AS center FROM $wpgmza_tblname_circles WHERE id = $circle_id");
129
 
280
  {
281
  global $wpdb;
282
  global $wpgmza_tblname_circles;
283
+
284
+ $map_id = intval($map_id);
285
 
286
  $circles_table = "
287
  <table>
299
  $circles = $wpdb->get_results($stmt);
300
  foreach($circles as $circle)
301
  {
302
+ $circle->id = intval($circle->id);
303
+
304
  $circles_table .= "
305
  <tr>
306
  <td>{$circle->id}</td>
307
  <td>{$circle->name}</td>
308
  <td width='170' align='left'>
309
+ <a href='" . esc_url(get_option('siteurl')) . "/wp-admin/admin.php?page=wp-google-maps-menu&amp;action=edit_circle&amp;map_id={$map_id}&amp;circle_id={$circle->id}'
310
  title='" . __('Edit', 'wp-google-maps') . "'
311
  class='wpgmza_edit_circle_btn button'
312
  id='{$circle->id}'>
includes/legacy/functions.rectangle.php CHANGED
@@ -27,6 +27,7 @@ function wpgmza_b_add_rectangle($mid)
27
  wpgmaps_b_admin_add_rectangle_javascript();
28
 
29
  if ($_GET['action'] == "add_rectangle" && isset($mid)) {
 
30
  $res = wpgmza_get_map_data($mid);
31
  echo "
32
 
@@ -105,8 +106,9 @@ function wpgmza_b_edit_rectangle($mid)
105
  wpgmaps_b_admin_add_rectangle_javascript();
106
 
107
  if ($_GET['action'] == "edit_rectangle" && isset($mid)) {
 
108
  $res = wpgmza_get_map_data($mid);
109
- $rectangle_id = (int)$_GET['rectangle_id'];
110
 
111
  $results = $wpdb->get_results("SELECT *, {$wpgmza->spatialFunctionPrefix}AsText(cornerA) AS cornerA, {$wpgmza->spatialFunctionPrefix}AsText(cornerB) AS cornerB FROM $wpgmza_tblname_rectangles WHERE id = $rectangle_id");
112
 
@@ -247,6 +249,8 @@ if(!function_exists('wpgmza_get_rectangles_table'))
247
  {
248
  global $wpdb;
249
  global $wpgmza_tblname_rectangles;
 
 
250
 
251
  $rectangles_table = "
252
  <table>
@@ -263,7 +267,9 @@ if(!function_exists('wpgmza_get_rectangles_table'))
263
  $stmt = $wpdb->prepare("SELECT * FROM $wpgmza_tblname_rectangles WHERE map_id = %d", array($map_id));
264
  $rectangles = $wpdb->get_results($stmt);
265
  foreach($rectangles as $rectangle)
266
- {
 
 
267
  $rectangles_table .= "
268
  <tr>
269
  <td>{$rectangle->id}</td>
27
  wpgmaps_b_admin_add_rectangle_javascript();
28
 
29
  if ($_GET['action'] == "add_rectangle" && isset($mid)) {
30
+ $mid = intval($mid);
31
  $res = wpgmza_get_map_data($mid);
32
  echo "
33
 
106
  wpgmaps_b_admin_add_rectangle_javascript();
107
 
108
  if ($_GET['action'] == "edit_rectangle" && isset($mid)) {
109
+ $mid = intval($mid);
110
  $res = wpgmza_get_map_data($mid);
111
+ $rectangle_id = (int) intval($_GET['rectangle_id']);
112
 
113
  $results = $wpdb->get_results("SELECT *, {$wpgmza->spatialFunctionPrefix}AsText(cornerA) AS cornerA, {$wpgmza->spatialFunctionPrefix}AsText(cornerB) AS cornerB FROM $wpgmza_tblname_rectangles WHERE id = $rectangle_id");
114
 
249
  {
250
  global $wpdb;
251
  global $wpgmza_tblname_rectangles;
252
+
253
+ $map_id = intval($map_id);
254
 
255
  $rectangles_table = "
256
  <table>
267
  $stmt = $wpdb->prepare("SELECT * FROM $wpgmza_tblname_rectangles WHERE map_id = %d", array($map_id));
268
  $rectangles = $wpdb->get_results($stmt);
269
  foreach($rectangles as $rectangle)
270
+ {
271
+ $rectangle->id = intval($rectangle->id);
272
+
273
  $rectangles_table .= "
274
  <tr>
275
  <td>{$rectangle->id}</td>
includes/map-edit-page/class.map-edit-page.php CHANGED
@@ -12,7 +12,7 @@ class MapEditPage extends Page
12
  global $wpgmza_pro_version;
13
 
14
  if($map_id === null)
15
- $map_id = $_REQUEST['map_id'];
16
 
17
 
18
 
@@ -182,7 +182,7 @@ class MapEditPage extends Page
182
  if (isset($_GET) && $_GET['action'] == 'create-map-page' && isset($_GET['map_id'])) {
183
 
184
  // NB: Suggest using $this->map->id, global functions should be dropped
185
- $res = wpgmza_get_map_data($_GET['map_id']);
186
 
187
  // Set the post ID to -1. This sets to no action at moment
188
  $post_id = -1;
@@ -219,7 +219,7 @@ class MapEditPage extends Page
219
  'post_type' => 'page'
220
  )
221
  );
222
- echo '<script>window.location.href = "post.php?post='.$post_id.'&action=edit";</script>';
223
  return;
224
  } else {
225
  $loop_posts->the_post();
@@ -238,7 +238,7 @@ class MapEditPage extends Page
238
  )
239
  );
240
 
241
- echo '<script>window.location.href = "post.php?post='.$post_id.'&action=edit";</script>';
242
  return;
243
  }
244
  } else {
@@ -328,7 +328,7 @@ class MapEditPage extends Page
328
  $this->map->set($data);
329
 
330
  // Done! Redirect to the specified URL
331
- wp_redirect($_POST['redirect_to']);
332
  }
333
 
334
  }
12
  global $wpgmza_pro_version;
13
 
14
  if($map_id === null)
15
+ $map_id = intval($_REQUEST['map_id']);
16
 
17
 
18
 
182
  if (isset($_GET) && $_GET['action'] == 'create-map-page' && isset($_GET['map_id'])) {
183
 
184
  // NB: Suggest using $this->map->id, global functions should be dropped
185
+ $res = wpgmza_get_map_data(intval($_GET['map_id']));
186
 
187
  // Set the post ID to -1. This sets to no action at moment
188
  $post_id = -1;
219
  'post_type' => 'page'
220
  )
221
  );
222
+ echo '<script>window.location.href = "post.php?post='.intval($post_id).'&action=edit";</script>';
223
  return;
224
  } else {
225
  $loop_posts->the_post();
238
  )
239
  );
240
 
241
+ echo '<script>window.location.href = "post.php?post='.intval($post_id).'&action=edit";</script>';
242
  return;
243
  }
244
  } else {
328
  $this->map->set($data);
329
 
330
  // Done! Redirect to the specified URL
331
+ wp_redirect(strip_tags($_POST['redirect_to']));
332
  }
333
 
334
  }
includes/open-layers/class.nominatim-geocode-cache.php CHANGED
@@ -95,7 +95,7 @@ class NominatimGeocodeCache
95
  function query_nominatim_cache()
96
  {
97
  $cache = new NominatimGeocodeCache();
98
- $record = $cache->get($_GET['query']);
99
 
100
  if(!$record)
101
  $record = array();
@@ -111,7 +111,7 @@ function query_nominatim_cache()
111
  function store_nominatim_cache()
112
  {
113
  $cache = new NominatimGeocodeCache();
114
- $cache->set($_POST['query'], $_POST['response']);
115
 
116
  wp_send_json(array(
117
  'success' => 1
95
  function query_nominatim_cache()
96
  {
97
  $cache = new NominatimGeocodeCache();
98
+ $record = $cache->get(sanitize_text_field($_GET['query']));
99
 
100
  if(!$record)
101
  $record = array();
111
  function store_nominatim_cache()
112
  {
113
  $cache = new NominatimGeocodeCache();
114
+ $cache->set(sanitize_text_field($_POST['query']), $_POST['response']);
115
 
116
  wp_send_json(array(
117
  'success' => 1
legacy-core.php CHANGED
@@ -215,7 +215,7 @@ function wpgmaps_folder_check() {
215
  function wpgmaps_folder_warning() {
216
  $file = get_option("wpgmza_xml_location");
217
  echo '
218
- <div class="error"><p>'.__('<strong>WP Google Maps cannot find the directory it uses to save marker data to. Please confirm that <em>', 'wp-google-maps').' '.$file.' '.__('</em>exists. Please also ensure that you assign file permissions of 755 (or 777) to this directory.','wp-google-maps').'</strong></p></div>
219
  ';
220
  }
221
 
@@ -498,7 +498,7 @@ function wpgmaps_action_callback_basic() {
498
  exit;
499
  }
500
 
501
- $map_id = (isset($_POST['map_id']) ? (int)$_POST['map_id'] : null);
502
 
503
  if ($_POST['action'] == "add_marker") {
504
 
@@ -557,7 +557,7 @@ function wpgmaps_action_callback_basic() {
557
 
558
  }
559
  if ($_POST['action'] == "edit_marker") {
560
- $cur_id = (int)$_POST['edit_id'];
561
 
562
  $lat = floatval($_POST['lat']);
563
  $lng = floatval($_POST['lng']);
@@ -578,8 +578,8 @@ function wpgmaps_action_callback_basic() {
578
  $lat,
579
  $lng,
580
  "POINT($lat $lng)",
581
- (int)$_POST['anim'],
582
- (int)$_POST['infoopen'],
583
  (int)$cur_id
584
  );
585
 
@@ -626,7 +626,7 @@ function wpgmaps_action_callback_basic() {
626
 
627
  if($_POST['action'] == "delete_circle") {
628
  global $wpgmza_tblname_circles;
629
- $stmt = $wpdb->prepare("DELETE FROM $wpgmza_tblname_circles WHERE id=%d", array((int)$_POST['circle_id']));
630
  $wpdb->query($stmt);
631
 
632
  echo wpgmza_get_circles_table($map_id);
@@ -634,7 +634,7 @@ function wpgmaps_action_callback_basic() {
634
 
635
  if($_POST['action'] == "delete_rectangle") {
636
  global $wpgmza_tblname_rectangles;
637
- $stmt = $wpdb->prepare("DELETE FROM $wpgmza_tblname_rectangles WHERE id=%d", array((int)$_POST['rectangle_id']));
638
  $wpdb->query($stmt);
639
 
640
  echo wpgmza_get_rectangles_table($map_id);
@@ -685,7 +685,7 @@ function wpgmaps_tag_basic( $atts )
685
 
686
  $res = wpgmza_get_map_data($wpgmza_current_map_id);
687
 
688
- if (!isset($res)) { echo __("Error: The map ID","wp-google-maps")." (".$wpgmza_current_map_id.") ".__("does not exist","wp-google-maps"); return; }
689
 
690
  $user_api_key = get_option( 'wpgmza_google_maps_api_key' );
691
 
@@ -904,6 +904,11 @@ function wpgmaps_get_plugin_url() {
904
  return plugin_dir_url( __FILE__ );
905
  }
906
 
 
 
 
 
 
907
  function wpgmaps_menu_marker_layout() {
908
 
909
  if (!$_GET['action']) {
@@ -917,14 +922,14 @@ function wpgmaps_menu_marker_layout() {
917
  if ($_GET['action'] == "trash" && isset($_GET['marker_id'])) {
918
 
919
  if ($_GET['s'] == "1") {
920
- if (wpgmaps_trash_marker((int)$_GET['marker_id'])) {
921
  wp_add_inline_script('wpgmza', "window.location = \"".get_option('siteurl')."/wp-admin/admin.php?page=wp-google-maps-marker-menu\"");
922
  } else {
923
  _e("There was a problem deleting the marker.");;
924
  }
925
  } else {
926
- $res = wpgmza_get_marker_data((int)$_GET['map_id']);
927
- echo "<h2>".__("Delete Marker","wp-google-maps")."</h2><p>".__("Are you sure you want to delete this marker:","wp-google-maps")." <strong>\"".$res->address."?\"</strong> <br /><a href='?page=wp-google-maps-marker-menu&action=trash&marker_id=".(int)$_GET['marker_id']."&s=1'>".__("Yes","wp-google-maps")."</a> | <a href='?page=wp-google-maps-marker-menu'>".__("No","wp-google-maps")."</a></p>";
928
  }
929
 
930
 
@@ -1009,8 +1014,8 @@ function wpgmza_review_nag() {
1009
 
1010
  if ($days_diff >= 10) {
1011
  $rate_text = sprintf( __( '<h3>We need your love!</h3><p>If you are enjoying our plugin, please consider <a href="%1$s" target="_blank" class="button-border button-border__green">reviewing WP Google Maps</a>. It would mean the world to us! If you are experiencing issues with the plugin, please <a href="%2$s" target="_blank" class="button-border button-border__green">contact us</a> and we will help you as soon as humanly possible!</p>', 'wp-google-maps' ),
1012
- 'https://wordpress.org/support/view/plugin-reviews/wp-google-maps?filter=5',
1013
- 'http://www.wpgmaps.com/contact-us/'
1014
  );
1015
  echo "<style>.wpgmza_upgrade_nag { display:none; }</style>";
1016
  echo "<div class='updated wpgmza_nag_review_div'>".$rate_text."<p><a href='admin.php?page=wp-google-maps-menu&action2=close_review' class='wpgmza_close_review_nag button-border button-border__green' title='".__("We will not nag you again, promise!","wp-google-maps")."'>".__("Close","wp-google-maps")."</a></p></div>";
@@ -1088,6 +1093,11 @@ function wpgmza_map_page() {
1088
  echo"<br /><div style='float:right;'><a href='http://www.wpgmaps.com/documentation/troubleshooting/' target='_BLANK' title='WP Google Maps Troubleshooting Section'>".__("Problems with the plugin? See the troubleshooting manual.","wp-google-maps")."</a></div>";
1089
  }
1090
 
 
 
 
 
 
1091
  function wpgmza_marker_page() {
1092
  echo"<div class=\"wrap\"><div id=\"icon-edit\" class=\"icon32 icon32-posts-post\"><br></div><h2>".__("My Markers","wp-google-maps")." <a href=\"admin.php?page=wp-google-maps-marker-menu&action=new\" class=\"add-new-h2\">".__("Add New","wp-google-maps")."</a></h2>";
1093
  wpgmaps_list_markers();
@@ -1096,6 +1106,11 @@ function wpgmza_marker_page() {
1096
 
1097
  }
1098
 
 
 
 
 
 
1099
  function wpgmaps_list_markers() {
1100
  global $wpdb;
1101
  global $wpgmza_tblname;
@@ -1121,15 +1136,15 @@ function wpgmaps_list_markers() {
1121
  <tbody id=\"the-list\" class='list:wp_list_text_link'>
1122
  ";
1123
  foreach ( $results as $result ) {
1124
- echo "<tr id=\"record_".$result->id."\">";
1125
- echo "<td class='id column-id'>".$result->id."</td>";
1126
- echo "<td class='id column-id'>".$result->icon."</td>";
1127
- echo "<td class='id column-id'>".$result->map_id."</td>";
1128
- echo "<td class='id column-id'>".$result->title."</td>";
1129
- echo "<td class='id column-id'>".$result->address."</td>";
1130
- echo "<td class='id column-id'>".$result->lat.",".$result->lng."</td>";
1131
- echo "<td class='id column-id'>".$result->pic."</td>";
1132
- echo "<td class='id column-id'>".$result->link."</td>";
1133
  echo "</tr>";
1134
 
1135
 
@@ -1161,21 +1176,21 @@ function wpgmza_edit_marker($mid) {
1161
  <div class='wide'>
1162
 
1163
  <h2>".__("Edit Marker Location","wp-google-maps")." ".__("ID","wp-google-maps")."#$mid</h2>
1164
- <form action='?page=wp-google-maps-menu&action=edit&map_id=".$res->map_id."' method='post' id='wpgmaps_edit_marker'>
1165
  <p></p>
1166
 
1167
  <input type='hidden' name='wpgmaps_marker-nonce' id='wpgmaps_b_nonce' value='".wp_create_nonce( 'wpgmaps_marker-nonce' )."' />
1168
- <input type='hidden' name='wpgmaps_marker_id' id='wpgmaps_marker_id' value='".$mid."' />
1169
  <div id=\"wpgmaps_status\"></div>
1170
  <table>
1171
 
1172
  <tr>
1173
  <td>".__("Marker Latitude","wp-google-maps").":</td>
1174
- <td><input id='wpgmaps_marker_lat' name='wpgmaps_marker_lat' type='text' size='15' maxlength='100' value='".$res->lat."' /></td>
1175
  </tr>
1176
  <tr>
1177
  <td>".__("Marker Longitude","wp-google-maps").":</td>
1178
- <td><input id='wpgmaps_marker_lng' name='wpgmaps_marker_lng' type='text' size='15' maxlength='100' value='".$res->lng."' /></td>
1179
  </tr>
1180
 
1181
  </table>
@@ -1420,19 +1435,14 @@ function wpgmaps_debugger($section) {
1420
 
1421
  }
1422
 
1423
- function wpgmaps_load_jquery() {
1424
- if (!is_admin()) {
1425
- $wpgmza_settings = get_option("WPGMZA_OTHER_SETTINGS");
1426
- if (isset($wpgmza_settings['wpgmza_settings_force_jquery'])) {
1427
- if ($wpgmza_settings['wpgmza_settings_force_jquery'] == "yes") {
1428
- wp_deregister_script('jquery');
1429
- wp_register_script('jquery', '//code.jquery.com/jquery-1.11.3.min.js', false, "1.11.3");
1430
- }
1431
- }
1432
- wp_enqueue_script('jquery');
1433
- }
1434
- }
1435
- add_action('wp_enqueue_scripts', 'wpgmaps_load_jquery', 9999);
1436
 
1437
  function wpgmza_get_category_data($cat_id) {
1438
  global $wpgmza;
@@ -1487,7 +1497,7 @@ function wpgmza_get_category_icon($cat_id) {
1487
 
1488
 
1489
  function wpgmza_return_error($data) {
1490
- echo "<div id=\"message\" class=\"error\"><p><strong>".$data->get_error_message()."</strong><blockquote>".$data->get_error_data()."</blockquote></p></div>";
1491
 
1492
  }
1493
 
@@ -1629,7 +1639,7 @@ function google_maps_api_key_warning(){
1629
 
1630
  echo "<p>" . __('<strong>Alternatively, please switch to the OpenLayers map engine</strong> on the maps settings page', 'wp-google-maps') . "</p>";
1631
 
1632
- echo sprintf( __('Need help? %s or %s.', 'wp-google-maps'), $video, $documentation )."</p>";
1633
  echo "</div>";
1634
  }
1635
  }
@@ -1712,13 +1722,13 @@ function wpgm_pro_link($link) {
1712
  if (defined('wpgm_aff')) {
1713
  $id = sanitize_text_field(wpgm_aff);
1714
  if ($id && $id !== "") {
1715
- return "http://affiliatetracker.io/?aff=".$id."&affuri=".base64_encode($link);
1716
  } else {
1717
- return $link;
1718
  }
1719
 
1720
  } else {
1721
- return $link;
1722
  }
1723
  }
1724
 
215
  function wpgmaps_folder_warning() {
216
  $file = get_option("wpgmza_xml_location");
217
  echo '
218
+ <div class="error"><p>'.__('<strong>WP Google Maps cannot find the directory it uses to save marker data to. Please confirm that <em>', 'wp-google-maps').' '.esc_url($file).' '.__('</em>exists. Please also ensure that you assign file permissions of 755 (or 777) to this directory.','wp-google-maps').'</strong></p></div>
219
  ';
220
  }
221
 
498
  exit;
499
  }
500
 
501
+ $map_id = (isset($_POST['map_id']) ? (int) intval($_POST['map_id']) : null);
502
 
503
  if ($_POST['action'] == "add_marker") {
504
 
557
 
558
  }
559
  if ($_POST['action'] == "edit_marker") {
560
+ $cur_id = (int) intval($_POST['edit_id']);
561
 
562
  $lat = floatval($_POST['lat']);
563
  $lng = floatval($_POST['lng']);
578
  $lat,
579
  $lng,
580
  "POINT($lat $lng)",
581
+ (int) intval($_POST['anim']),
582
+ (int) intval($_POST['infoopen']),
583
  (int)$cur_id
584
  );
585
 
626
 
627
  if($_POST['action'] == "delete_circle") {
628
  global $wpgmza_tblname_circles;
629
+ $stmt = $wpdb->prepare("DELETE FROM $wpgmza_tblname_circles WHERE id=%d", array((int) intval($_POST['circle_id'])));
630
  $wpdb->query($stmt);
631
 
632
  echo wpgmza_get_circles_table($map_id);
634
 
635
  if($_POST['action'] == "delete_rectangle") {
636
  global $wpgmza_tblname_rectangles;
637
+ $stmt = $wpdb->prepare("DELETE FROM $wpgmza_tblname_rectangles WHERE id=%d", array((int) intval($_POST['rectangle_id'])));
638
  $wpdb->query($stmt);
639
 
640
  echo wpgmza_get_rectangles_table($map_id);
685
 
686
  $res = wpgmza_get_map_data($wpgmza_current_map_id);
687
 
688
+ if (!isset($res)) { echo __("Error: The map ID","wp-google-maps")." (". intval($wpgmza_current_map_id) .") ".__("does not exist","wp-google-maps"); return; }
689
 
690
  $user_api_key = get_option( 'wpgmza_google_maps_api_key' );
691
 
904
  return plugin_dir_url( __FILE__ );
905
  }
906
 
907
+ /**
908
+ * Not called at all
909
+ *
910
+ * @deprecated since V7
911
+ */
912
  function wpgmaps_menu_marker_layout() {
913
 
914
  if (!$_GET['action']) {
922
  if ($_GET['action'] == "trash" && isset($_GET['marker_id'])) {
923
 
924
  if ($_GET['s'] == "1") {
925
+ if (wpgmaps_trash_marker((int) intval($_GET['marker_id']) )) {
926
  wp_add_inline_script('wpgmza', "window.location = \"".get_option('siteurl')."/wp-admin/admin.php?page=wp-google-maps-marker-menu\"");
927
  } else {
928
  _e("There was a problem deleting the marker.");;
929
  }
930
  } else {
931
+ $res = wpgmza_get_marker_data((int) intval($_GET['map_id']) );
932
+ echo "<h2>".__("Delete Marker","wp-google-maps")."</h2><p>".__("Are you sure you want to delete this marker:","wp-google-maps")." <strong>\"".strip_tags($res->address)."?\"</strong> <br /><a href='?page=wp-google-maps-marker-menu&action=trash&marker_id=".(int) intval($_GET['marker_id']) ."&s=1'>".__("Yes","wp-google-maps")."</a> | <a href='?page=wp-google-maps-marker-menu'>".__("No","wp-google-maps")."</a></p>";
933
  }
934
 
935
 
1014
 
1015
  if ($days_diff >= 10) {
1016
  $rate_text = sprintf( __( '<h3>We need your love!</h3><p>If you are enjoying our plugin, please consider <a href="%1$s" target="_blank" class="button-border button-border__green">reviewing WP Google Maps</a>. It would mean the world to us! If you are experiencing issues with the plugin, please <a href="%2$s" target="_blank" class="button-border button-border__green">contact us</a> and we will help you as soon as humanly possible!</p>', 'wp-google-maps' ),
1017
+ esc_attr('https://wordpress.org/support/view/plugin-reviews/wp-google-maps?filter=5'),
1018
+ esc_attr('http://www.wpgmaps.com/contact-us/')
1019
  );
1020
  echo "<style>.wpgmza_upgrade_nag { display:none; }</style>";
1021
  echo "<div class='updated wpgmza_nag_review_div'>".$rate_text."<p><a href='admin.php?page=wp-google-maps-menu&action2=close_review' class='wpgmza_close_review_nag button-border button-border__green' title='".__("We will not nag you again, promise!","wp-google-maps")."'>".__("Close","wp-google-maps")."</a></p></div>";
1093
  echo"<br /><div style='float:right;'><a href='http://www.wpgmaps.com/documentation/troubleshooting/' target='_BLANK' title='WP Google Maps Troubleshooting Section'>".__("Problems with the plugin? See the troubleshooting manual.","wp-google-maps")."</a></div>";
1094
  }
1095
 
1096
+ /**
1097
+ * Deprecated
1098
+ *
1099
+ * @deprecated since V7
1100
+ */
1101
  function wpgmza_marker_page() {
1102
  echo"<div class=\"wrap\"><div id=\"icon-edit\" class=\"icon32 icon32-posts-post\"><br></div><h2>".__("My Markers","wp-google-maps")." <a href=\"admin.php?page=wp-google-maps-marker-menu&action=new\" class=\"add-new-h2\">".__("Add New","wp-google-maps")."</a></h2>";
1103
  wpgmaps_list_markers();
1106
 
1107
  }
1108
 
1109
+ /**
1110
+ * Deprecated
1111
+ *
1112
+ * @deprecated since V7
1113
+ */
1114
  function wpgmaps_list_markers() {
1115
  global $wpdb;
1116
  global $wpgmza_tblname;
1136
  <tbody id=\"the-list\" class='list:wp_list_text_link'>
1137
  ";
1138
  foreach ( $results as $result ) {
1139
+ echo "<tr id=\"record_".intval($result->id)."\">";
1140
+ echo "<td class='id column-id'>".intval($result->id)."</td>";
1141
+ echo "<td class='id column-id'>".esc_html($result->icon)."</td>";
1142
+ echo "<td class='id column-id'>".intval($result->map_id)."</td>";
1143
+ echo "<td class='id column-id'>".esc_html($result->title)."</td>";
1144
+ echo "<td class='id column-id'>".esc_html($result->address)."</td>";
1145
+ echo "<td class='id column-id'>".floatval($result->lat).",".floatval($result->lng)."</td>";
1146
+ echo "<td class='id column-id'>".esc_html($result->pic)."</td>";
1147
+ echo "<td class='id column-id'>".esc_url($result->link)."</td>";
1148
  echo "</tr>";
1149
 
1150
 
1176
  <div class='wide'>
1177
 
1178
  <h2>".__("Edit Marker Location","wp-google-maps")." ".__("ID","wp-google-maps")."#$mid</h2>
1179
+ <form action='?page=wp-google-maps-menu&action=edit&map_id=".intval($res->map_id)."' method='post' id='wpgmaps_edit_marker'>
1180
  <p></p>
1181
 
1182
  <input type='hidden' name='wpgmaps_marker-nonce' id='wpgmaps_b_nonce' value='".wp_create_nonce( 'wpgmaps_marker-nonce' )."' />
1183
+ <input type='hidden' name='wpgmaps_marker_id' id='wpgmaps_marker_id' value='".intval($mid)."' />
1184
  <div id=\"wpgmaps_status\"></div>
1185
  <table>
1186
 
1187
  <tr>
1188
  <td>".__("Marker Latitude","wp-google-maps").":</td>
1189
+ <td><input id='wpgmaps_marker_lat' name='wpgmaps_marker_lat' type='text' size='15' maxlength='100' value='".floatval($res->lat)."' /></td>
1190
  </tr>
1191
  <tr>
1192
  <td>".__("Marker Longitude","wp-google-maps").":</td>
1193
+ <td><input id='wpgmaps_marker_lng' name='wpgmaps_marker_lng' type='text' size='15' maxlength='100' value='".floatval($res->lng)."' /></td>
1194
  </tr>
1195
 
1196
  </table>
1435
 
1436
  }
1437
 
1438
+ /**
1439
+ * This fuction remain in place, however, does execute any code
1440
+ *
1441
+ * It should be noted this code has technically been disabled for an extensive amount of time, the setting that controls this function was removed pre V8
1442
+ *
1443
+ * @deprecated since 8.1.18
1444
+ */
1445
+ function wpgmaps_load_jquery() {}
 
 
 
 
 
1446
 
1447
  function wpgmza_get_category_data($cat_id) {
1448
  global $wpgmza;
1497
 
1498
 
1499
  function wpgmza_return_error($data) {
1500
+ echo "<div id=\"message\" class=\"error\"><p><strong>".strip_tags($data->get_error_message())."</strong><blockquote>".strip_tags($data->get_error_data())."</blockquote></p></div>";
1501
 
1502
  }
1503
 
1639
 
1640
  echo "<p>" . __('<strong>Alternatively, please switch to the OpenLayers map engine</strong> on the maps settings page', 'wp-google-maps') . "</p>";
1641
 
1642
+ echo sprintf( __('Need help? %s or %s.', 'wp-google-maps'), esc_url($video), esc_url($documentation) )."</p>";
1643
  echo "</div>";
1644
  }
1645
  }
1722
  if (defined('wpgm_aff')) {
1723
  $id = sanitize_text_field(wpgm_aff);
1724
  if ($id && $id !== "") {
1725
+ return esc_attr("http://affiliatetracker.io/?aff=".$id."&affuri=".base64_encode($link));
1726
  } else {
1727
+ return esc_attr($link);
1728
  }
1729
 
1730
  } else {
1731
+ return esc_attr($link);
1732
  }
1733
  }
1734
 
lib/codemirror.css DELETED
@@ -1,346 +0,0 @@
1
- /* BASICS */
2
-
3
- .CodeMirror {
4
- /* Set height, width, borders, and global font properties here */
5
- font-family: monospace;
6
- height: 300px;
7
- color: black;
8
- direction: ltr;
9
- }
10
-
11
- /* PADDING */
12
-
13
- .CodeMirror-lines {
14
- padding: 4px 0; /* Vertical padding around content */
15
- }
16
- .CodeMirror pre {
17
- padding: 0 4px; /* Horizontal padding of content */
18
- }
19
-
20
- .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
21
- background-color: white; /* The little square between H and V scrollbars */
22
- }
23
-
24
- /* GUTTER */
25
-
26
- .CodeMirror-gutters {
27
- border-right: 1px solid #ddd;
28
- background-color: #f7f7f7;
29
- white-space: nowrap;
30
- }
31
- .CodeMirror-linenumbers {}
32
- .CodeMirror-linenumber {
33
- padding: 0 3px 0 5px;
34
- min-width: 20px;
35
- text-align: right;
36
- color: #999;
37
- white-space: nowrap;
38
- }
39
-
40
- .CodeMirror-guttermarker { color: black; }
41
- .CodeMirror-guttermarker-subtle { color: #999; }
42
-
43
- /* CURSOR */
44
-
45
- .CodeMirror-cursor {
46
- border-left: 1px solid black;
47
- border-right: none;
48
- width: 0;
49
- }
50
- /* Shown when moving in bi-directional text */
51
- .CodeMirror div.CodeMirror-secondarycursor {
52
- border-left: 1px solid silver;
53
- }
54
- .cm-fat-cursor .CodeMirror-cursor {
55
- width: auto;
56
- border: 0 !important;
57
- background: #7e7;
58
- }
59
- .cm-fat-cursor div.CodeMirror-cursors {
60
- z-index: 1;
61
- }
62
- .cm-fat-cursor-mark {
63
- background-color: rgba(20, 255, 20, 0.5);
64
- -webkit-animation: blink 1.06s steps(1) infinite;
65
- -moz-animation: blink 1.06s steps(1) infinite;
66
- animation: blink 1.06s steps(1) infinite;
67
- }
68
- .cm-animate-fat-cursor {
69
- width: auto;
70
- border: 0;
71
- -webkit-animation: blink 1.06s steps(1) infinite;
72
- -moz-animation: blink 1.06s steps(1) infinite;
73
- animation: blink 1.06s steps(1) infinite;
74
- background-color: #7e7;
75
- }
76
- @-moz-keyframes blink {
77
- 0% {}
78
- 50% { background-color: transparent; }
79
- 100% {}
80
- }
81
- @-webkit-keyframes blink {
82
- 0% {}
83
- 50% { background-color: transparent; }
84
- 100% {}
85
- }
86
- @keyframes blink {
87
- 0% {}
88
- 50% { background-color: transparent; }
89
- 100% {}
90
- }
91
-
92
- /* Can style cursor different in overwrite (non-insert) mode */
93
- .CodeMirror-overwrite .CodeMirror-cursor {}
94
-
95
- .cm-tab { display: inline-block; text-decoration: inherit; }
96
-
97
- .CodeMirror-rulers {
98
- position: absolute;
99
- left: 0; right: 0; top: -50px; bottom: -20px;
100
- overflow: hidden;
101
- }
102
- .CodeMirror-ruler {
103
- border-left: 1px solid #ccc;
104
- top: 0; bottom: 0;
105
- position: absolute;
106
- }
107
-
108
- /* DEFAULT THEME */
109
-
110
- .cm-s-default .cm-header {color: blue;}
111
- .cm-s-default .cm-quote {color: #090;}
112
- .cm-negative {color: #d44;}
113
- .cm-positive {color: #292;}
114
- .cm-header, .cm-strong {font-weight: bold;}
115
- .cm-em {font-style: italic;}
116
- .cm-link {text-decoration: underline;}
117
- .cm-strikethrough {text-decoration: line-through;}
118
-
119
- .cm-s-default .cm-keyword {color: #708;}
120
- .cm-s-default .cm-atom {color: #219;}
121
- .cm-s-default .cm-number {color: #164;}
122
- .cm-s-default .cm-def {color: #00f;}
123
- .cm-s-default .cm-variable,
124
- .cm-s-default .cm-punctuation,
125
- .cm-s-default .cm-property,
126
- .cm-s-default .cm-operator {}
127
- .cm-s-default .cm-variable-2 {color: #05a;}
128
- .cm-s-default .cm-variable-3, .cm-s-default .cm-type {color: #085;}
129
- .cm-s-default .cm-comment {color: #a50;}
130
- .cm-s-default .cm-string {color: #a11;}
131
- .cm-s-default .cm-string-2 {color: #f50;}
132
- .cm-s-default .cm-meta {color: #555;}
133
- .cm-s-default .cm-qualifier {color: #555;}
134
- .cm-s-default .cm-builtin {color: #30a;}
135
- .cm-s-default .cm-bracket {color: #997;}
136
- .cm-s-default .cm-tag {color: #170;}
137
- .cm-s-default .cm-attribute {color: #00c;}
138
- .cm-s-default .cm-hr {color: #999;}
139
- .cm-s-default .cm-link {color: #00c;}
140
-
141
- .cm-s-default .cm-error {color: #f00;}
142
- .cm-invalidchar {color: #f00;}
143
-
144
- .CodeMirror-composing { border-bottom: 2px solid; }
145
-
146
- /* Default styles for common addons */
147
-
148
- div.CodeMirror span.CodeMirror-matchingbracket {color: #0b0;}
149
- div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;}
150
- .CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); }
151
- .CodeMirror-activeline-background {background: #e8f2ff;}
152
-
153
- /* STOP */
154
-
155
- /* The rest of this file contains styles related to the mechanics of
156
- the editor. You probably shouldn't touch them. */
157
-
158
- .CodeMirror {
159
- position: relative;
160
- overflow: hidden;
161
- background: white;
162
- }
163
-
164
- .CodeMirror-scroll {
165
- overflow: scroll !important; /* Things will break if this is overridden */
166
- /* 30px is the magic margin used to hide the element's real scrollbars */
167
- /* See overflow: hidden in .CodeMirror */
168
- margin-bottom: -30px; margin-right: -30px;
169
- padding-bottom: 30px;
170
- height: 100%;
171
- outline: none; /* Prevent dragging from highlighting the element */
172
- position: relative;
173
- }
174
- .CodeMirror-sizer {
175
- position: relative;
176
- border-right: 30px solid transparent;
177
- }
178
-
179
- /* The fake, visible scrollbars. Used to force redraw during scrolling
180
- before actual scrolling happens, thus preventing shaking and
181
- flickering artifacts. */
182
- .CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
183
- position: absolute;
184
- z-index: 6;
185
- display: none;
186
- }
187
- .CodeMirror-vscrollbar {
188
- right: 0; top: 0;
189
- overflow-x: hidden;
190
- overflow-y: scroll;
191
- }
192
- .CodeMirror-hscrollbar {
193
- bottom: 0; left: 0;
194
- overflow-y: hidden;
195
- overflow-x: scroll;
196
- }
197
- .CodeMirror-scrollbar-filler {
198
- right: 0; bottom: 0;
199
- }
200
- .CodeMirror-gutter-filler {
201
- left: 0; bottom: 0;
202
- }
203
-
204
- .CodeMirror-gutters {
205
- position: absolute; left: 0; top: 0;
206
- min-height: 100%;
207
- z-index: 3;
208
- }
209
- .CodeMirror-gutter {
210
- white-space: normal;
211
- height: 100%;
212
- display: inline-block;
213
- vertical-align: top;
214
- margin-bottom: -30px;
215
- }
216
- .CodeMirror-gutter-wrapper {
217
- position: absolute;
218
- z-index: 4;
219
- background: none !important;
220
- border: none !important;
221
- }
222
- .CodeMirror-gutter-background {
223
- position: absolute;
224
- top: 0; bottom: 0;
225
- z-index: 4;
226
- }
227
- .CodeMirror-gutter-elt {
228
- position: absolute;
229
- cursor: default;
230
- z-index: 4;
231
- }
232
- .CodeMirror-gutter-wrapper ::selection { background-color: transparent }
233
- .CodeMirror-gutter-wrapper ::-moz-selection { background-color: transparent }
234
-
235
- .CodeMirror-lines {
236
- cursor: text;
237
- min-height: 1px; /* prevents collapsing before first draw */
238
- }
239
- .CodeMirror pre {
240
- /* Reset some styles that the rest of the page might have set */
241
- -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0;
242
- border-width: 0;
243
- background: transparent;
244
- font-family: inherit;
245
- font-size: inherit;
246
- margin: 0;
247
- white-space: pre;
248
- word-wrap: normal;
249
- line-height: inherit;
250
- color: inherit;
251
- z-index: 2;
252
- position: relative;
253
- overflow: visible;
254
- -webkit-tap-highlight-color: transparent;
255
- -webkit-font-variant-ligatures: contextual;
256
- font-variant-ligatures: contextual;
257
- }
258
- .CodeMirror-wrap pre {
259
- word-wrap: break-word;
260
- white-space: pre-wrap;
261
- word-break: normal;
262
- }
263
-
264
- .CodeMirror-linebackground {
265
- position: absolute;
266
- left: 0; right: 0; top: 0; bottom: 0;
267
- z-index: 0;
268
- }
269
-
270
- .CodeMirror-linewidget {
271
- position: relative;
272
- z-index: 2;
273
- padding: 0.1px; /* Force widget margins to stay inside of the container */
274
- }
275
-
276
- .CodeMirror-widget {}
277
-
278
- .CodeMirror-rtl pre { direction: rtl; }
279
-
280
- .CodeMirror-code {
281
- outline: none;
282
- }
283
-
284
- /* Force content-box sizing for the elements where we expect it */
285
- .CodeMirror-scroll,
286
- .CodeMirror-sizer,
287
- .CodeMirror-gutter,
288
- .CodeMirror-gutters,
289
- .CodeMirror-linenumber {
290
- -moz-box-sizing: content-box;
291
- box-sizing: content-box;
292
- }
293
-
294
- .CodeMirror-measure {
295
- position: absolute;
296
- width: 100%;
297
- height: 0;
298
- overflow: hidden;
299
- visibility: hidden;
300
- }
301
-
302
- .CodeMirror-cursor {
303
- position: absolute;
304
- pointer-events: none;
305
- }
306
- .CodeMirror-measure pre { position: static; }
307
-
308
- div.CodeMirror-cursors {
309
- visibility: hidden;
310
- position: relative;
311
- z-index: 3;
312
- }
313
- div.CodeMirror-dragcursors {
314
- visibility: visible;
315
- }
316
-
317
- .CodeMirror-focused div.CodeMirror-cursors {
318
- visibility: visible;
319
- }
320
-
321
- .CodeMirror-selected { background: #d9d9d9; }
322
- .CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
323
- .CodeMirror-crosshair { cursor: crosshair; }
324
- .CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; }
325
- .CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; }
326
-
327
- .cm-searching {
328
- background-color: #ffa;
329
- background-color: rgba(255, 255, 0, .4);
330
- }
331
-
332
- /* Used to force a border model for a node */
333
- .cm-force-border { padding-right: .1px; }
334
-
335
- @media print {
336
- /* Hide the cursor when printing */
337
- .CodeMirror div.CodeMirror-cursors {
338
- visibility: hidden;
339
- }
340
- }
341
-
342
- /* See issue #2901 */
343
- .cm-tab-wrap-hack:after { content: ''; }
344
-
345
- /* Help users use markselection to safely style text background */
346
- span.CodeMirror-selectedtext { background: none; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/codemirror.js DELETED
@@ -1,9762 +0,0 @@
1
- // CodeMirror, copyright (c) by Marijn Haverbeke and others
2
- // Distributed under an MIT license: https://codemirror.net/LICENSE
3
-
4
- // This is CodeMirror (https://codemirror.net), a code editor
5
- // implemented in JavaScript on top of the browser's DOM.
6
- //
7
- // You can find some technical background for some of the code below
8
- // at http://marijnhaverbeke.nl/blog/#cm-internals .
9
-
10
- (function (global, factory) {
11
- typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
12
- typeof define === 'function' && define.amd ? define(factory) :
13
- (global.CodeMirror = factory());
14
- }(this, (function () { 'use strict';
15
-
16
- // Kludges for bugs and behavior differences that can't be feature
17
- // detected are enabled based on userAgent etc sniffing.
18
- var userAgent = navigator.userAgent;
19
- var platform = navigator.platform;
20
-
21
- var gecko = /gecko\/\d/i.test(userAgent);
22
- var ie_upto10 = /MSIE \d/.test(userAgent);
23
- var ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(userAgent);
24
- var edge = /Edge\/(\d+)/.exec(userAgent);
25
- var ie = ie_upto10 || ie_11up || edge;
26
- var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : +(edge || ie_11up)[1]);
27
- var webkit = !edge && /WebKit\//.test(userAgent);
28
- var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(userAgent);
29
- var chrome = !edge && /Chrome\//.test(userAgent);
30
- var presto = /Opera\//.test(userAgent);
31
- var safari = /Apple Computer/.test(navigator.vendor);
32
- var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(userAgent);
33
- var phantom = /PhantomJS/.test(userAgent);
34
-
35
- var ios = !edge && /AppleWebKit/.test(userAgent) && /Mobile\/\w+/.test(userAgent);
36
- var android = /Android/.test(userAgent);
37
- // This is woefully incomplete. Suggestions for alternative methods welcome.
38
- var mobile = ios || android || /webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(userAgent);
39
- var mac = ios || /Mac/.test(platform);
40
- var chromeOS = /\bCrOS\b/.test(userAgent);
41
- var windows = /win/i.test(platform);
42
-
43
- var presto_version = presto && userAgent.match(/Version\/(\d*\.\d*)/);
44
- if (presto_version) { presto_version = Number(presto_version[1]); }
45
- if (presto_version && presto_version >= 15) { presto = false; webkit = true; }
46
- // Some browsers use the wrong event properties to signal cmd/ctrl on OS X
47
- var flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11));
48
- var captureRightClick = gecko || (ie && ie_version >= 9);
49
-
50
- function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*") }
51
-
52
- var rmClass = function(node, cls) {
53
- var current = node.className;
54
- var match = classTest(cls).exec(current);
55
- if (match) {
56
- var after = current.slice(match.index + match[0].length);
57
- node.className = current.slice(0, match.index) + (after ? match[1] + after : "");
58
- }
59
- };
60
-
61
- function removeChildren(e) {
62
- for (var count = e.childNodes.length; count > 0; --count)
63
- { e.removeChild(e.firstChild); }
64
- return e
65
- }
66
-
67
- function removeChildrenAndAdd(parent, e) {
68
- return removeChildren(parent).appendChild(e)
69
- }
70
-
71
- function elt(tag, content, className, style) {
72
- var e = document.createElement(tag);
73
- if (className) { e.className = className; }
74
- if (style) { e.style.cssText = style; }
75
- if (typeof content == "string") { e.appendChild(document.createTextNode(content)); }
76
- else if (content) { for (var i = 0; i < content.length; ++i) { e.appendChild(content[i]); } }
77
- return e
78
- }
79
- // wrapper for elt, which removes the elt from the accessibility tree
80
- function eltP(tag, content, className, style) {
81
- var e = elt(tag, content, className, style);
82
- e.setAttribute("role", "presentation");
83
- return e
84
- }
85
-
86
- var range;
87
- if (document.createRange) { range = function(node, start, end, endNode) {
88
- var r = document.createRange();
89
- r.setEnd(endNode || node, end);
90
- r.setStart(node, start);
91
- return r
92
- }; }
93
- else { range = function(node, start, end) {
94
- var r = document.body.createTextRange();
95
- try { r.moveToElementText(node.parentNode); }
96
- catch(e) { return r }
97
- r.collapse(true);
98
- r.moveEnd("character", end);
99
- r.moveStart("character", start);
100
- return r
101
- }; }
102
-
103
- function contains(parent, child) {
104
- if (child.nodeType == 3) // Android browser always returns false when child is a textnode
105
- { child = child.parentNode; }
106
- if (parent.contains)
107
- { return parent.contains(child) }
108
- do {
109
- if (child.nodeType == 11) { child = child.host; }
110
- if (child == parent) { return true }
111
- } while (child = child.parentNode)
112
- }
113
-
114
- function activeElt() {
115
- // IE and Edge may throw an "Unspecified Error" when accessing document.activeElement.
116
- // IE < 10 will throw when accessed while the page is loading or in an iframe.
117
- // IE > 9 and Edge will throw when accessed in an iframe if document.body is unavailable.
118
- var activeElement;
119
- try {
120
- activeElement = document.activeElement;
121
- } catch(e) {
122
- activeElement = document.body || null;
123
- }
124
- while (activeElement && activeElement.shadowRoot && activeElement.shadowRoot.activeElement)
125
- { activeElement = activeElement.shadowRoot.activeElement; }
126
- return activeElement
127
- }
128
-
129
- function addClass(node, cls) {
130
- var current = node.className;
131
- if (!classTest(cls).test(current)) { node.className += (current ? " " : "") + cls; }
132
- }
133
- function joinClasses(a, b) {
134
- var as = a.split(" ");
135
- for (var i = 0; i < as.length; i++)
136
- { if (as[i] && !classTest(as[i]).test(b)) { b += " " + as[i]; } }
137
- return b
138
- }
139
-
140
- var selectInput = function(node) { node.select(); };
141
- if (ios) // Mobile Safari apparently has a bug where select() is broken.
142
- { selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length; }; }
143
- else if (ie) // Suppress mysterious IE10 errors
144
- { selectInput = function(node) { try { node.select(); } catch(_e) {} }; }
145
-
146
- function bind(f) {
147
- var args = Array.prototype.slice.call(arguments, 1);
148
- return function(){return f.apply(null, args)}
149
- }
150
-
151
- function copyObj(obj, target, overwrite) {
152
- if (!target) { target = {}; }
153
- for (var prop in obj)
154
- { if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop)))
155
- { target[prop] = obj[prop]; } }
156
- return target
157
- }
158
-
159
- // Counts the column offset in a string, taking tabs into account.
160
- // Used mostly to find indentation.
161
- function countColumn(string, end, tabSize, startIndex, startValue) {
162
- if (end == null) {
163
- end = string.search(/[^\s\u00a0]/);
164
- if (end == -1) { end = string.length; }
165
- }
166
- for (var i = startIndex || 0, n = startValue || 0;;) {
167
- var nextTab = string.indexOf("\t", i);
168
- if (nextTab < 0 || nextTab >= end)
169
- { return n + (end - i) }
170
- n += nextTab - i;
171
- n += tabSize - (n % tabSize);
172
- i = nextTab + 1;
173
- }
174
- }
175
-
176
- var Delayed = function() {this.id = null;};
177
- Delayed.prototype.set = function (ms, f) {
178
- clearTimeout(this.id);
179
- this.id = setTimeout(f, ms);
180
- };
181
-
182
- function indexOf(array, elt) {
183
- for (var i = 0; i < array.length; ++i)
184
- { if (array[i] == elt) { return i } }
185
- return -1
186
- }
187
-
188
- // Number of pixels added to scroller and sizer to hide scrollbar
189
- var scrollerGap = 30;
190
-
191
- // Returned or thrown by various protocols to signal 'I'm not
192
- // handling this'.
193
- var Pass = {toString: function(){return "CodeMirror.Pass"}};
194
-
195
- // Reused option objects for setSelection & friends
196
- var sel_dontScroll = {scroll: false}, sel_mouse = {origin: "*mouse"}, sel_move = {origin: "+move"};
197
-
198
- // The inverse of countColumn -- find the offset that corresponds to
199
- // a particular column.
200
- function findColumn(string, goal, tabSize) {
201
- for (var pos = 0, col = 0;;) {
202
- var nextTab = string.indexOf("\t", pos);
203
- if (nextTab == -1) { nextTab = string.length; }
204
- var skipped = nextTab - pos;
205
- if (nextTab == string.length || col + skipped >= goal)
206
- { return pos + Math.min(skipped, goal - col) }
207
- col += nextTab - pos;
208
- col += tabSize - (col % tabSize);
209
- pos = nextTab + 1;
210
- if (col >= goal) { return pos }
211
- }
212
- }
213
-
214
- var spaceStrs = [""];
215
- function spaceStr(n) {
216
- while (spaceStrs.length <= n)
217
- { spaceStrs.push(lst(spaceStrs) + " "); }
218
- return spaceStrs[n]
219
- }
220
-
221
- function lst(arr) { return arr[arr.length-1] }
222
-
223
- function map(array, f) {
224
- var out = [];
225
- for (var i = 0; i < array.length; i++) { out[i] = f(array[i], i); }
226
- return out
227
- }
228
-
229
- function insertSorted(array, value, score) {
230
- var pos = 0, priority = score(value);
231
- while (pos < array.length && score(array[pos]) <= priority) { pos++; }
232
- array.splice(pos, 0, value);
233
- }
234
-
235
- function nothing() {}
236
-
237
- function createObj(base, props) {
238
- var inst;
239
- if (Object.create) {
240
- inst = Object.create(base);
241
- } else {
242
- nothing.prototype = base;
243
- inst = new nothing();
244
- }
245
- if (props) { copyObj(props, inst); }
246
- return inst
247
- }
248
-
249
- var nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/;
250
- function isWordCharBasic(ch) {
251
- return /\w/.test(ch) || ch > "\x80" &&
252
- (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch))
253
- }
254
- function isWordChar(ch, helper) {
255
- if (!helper) { return isWordCharBasic(ch) }
256
- if (helper.source.indexOf("\\w") > -1 && isWordCharBasic(ch)) { return true }
257
- return helper.test(ch)
258
- }
259
-
260
- function isEmpty(obj) {
261
- for (var n in obj) { if (obj.hasOwnProperty(n) && obj[n]) { return false } }
262
- return true
263
- }
264
-
265
- // Extending unicode characters. A series of a non-extending char +
266
- // any number of extending chars is treated as a single unit as far
267
- // as editing and measuring is concerned. This is not fully correct,
268
- // since some scripts/fonts/browsers also treat other configurations
269
- // of code points as a group.
270
- var extendingChars = /[\u0300-\u036f\u0483-\u0489\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u065e\u0670\u06d6-\u06dc\u06de-\u06e4\u06e7\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0900-\u0902\u093c\u0941-\u0948\u094d\u0951-\u0955\u0962\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2\u09e3\u0a01\u0a02\u0a3c\u0a41\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a70\u0a71\u0a75\u0a81\u0a82\u0abc\u0ac1-\u0ac5\u0ac7\u0ac8\u0acd\u0ae2\u0ae3\u0b01\u0b3c\u0b3e\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b82\u0bbe\u0bc0\u0bcd\u0bd7\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0cbc\u0cbf\u0cc2\u0cc6\u0ccc\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0d3e\u0d41-\u0d44\u0d4d\u0d57\u0d62\u0d63\u0dca\u0dcf\u0dd2-\u0dd4\u0dd6\u0ddf\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86\u0f87\u0f90-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039\u103a\u103d\u103e\u1058\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085\u1086\u108d\u109d\u135f\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193b\u1a17\u1a18\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80\u1b81\u1ba2-\u1ba5\u1ba8\u1ba9\u1c2c-\u1c33\u1c36\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1dc0-\u1de6\u1dfd-\u1dff\u200c\u200d\u20d0-\u20f0\u2cef-\u2cf1\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua66f-\ua672\ua67c\ua67d\ua6f0\ua6f1\ua802\ua806\ua80b\ua825\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\uaa29-\uaa2e\uaa31\uaa32\uaa35\uaa36\uaa43\uaa4c\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uabe5\uabe8\uabed\udc00-\udfff\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\uff9e\uff9f]/;
271
- function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch) }
272
-
273
- // Returns a number from the range [`0`; `str.length`] unless `pos` is outside that range.
274
- function skipExtendingChars(str, pos, dir) {
275
- while ((dir < 0 ? pos > 0 : pos < str.length) && isExtendingChar(str.charAt(pos))) { pos += dir; }
276
- return pos
277
- }
278
-
279
- // Returns the value from the range [`from`; `to`] that satisfies
280
- // `pred` and is closest to `from`. Assumes that at least `to`
281
- // satisfies `pred`. Supports `from` being greater than `to`.
282
- function findFirst(pred, from, to) {
283
- // At any point we are certain `to` satisfies `pred`, don't know
284
- // whether `from` does.
285
- var dir = from > to ? -1 : 1;
286
- for (;;) {
287
- if (from == to) { return from }
288
- var midF = (from + to) / 2, mid = dir < 0 ? Math.ceil(midF) : Math.floor(midF);
289
- if (mid == from) { return pred(mid) ? from : to }
290
- if (pred(mid)) { to = mid; }
291
- else { from = mid + dir; }
292
- }
293
- }
294
-
295
- // BIDI HELPERS
296
-
297
- function iterateBidiSections(order, from, to, f) {
298
- if (!order) { return f(from, to, "ltr", 0) }
299
- var found = false;
300
- for (var i = 0; i < order.length; ++i) {
301
- var part = order[i];
302
- if (part.from < to && part.to > from || from == to && part.to == from) {
303
- f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr", i);
304
- found = true;
305
- }
306
- }
307
- if (!found) { f(from, to, "ltr"); }
308
- }
309
-
310
- var bidiOther = null;
311
- function getBidiPartAt(order, ch, sticky) {
312
- var found;
313
- bidiOther = null;
314
- for (var i = 0; i < order.length; ++i) {
315
- var cur = order[i];
316
- if (cur.from < ch && cur.to > ch) { return i }
317
- if (cur.to == ch) {
318
- if (cur.from != cur.to && sticky == "before") { found = i; }
319
- else { bidiOther = i; }
320
- }
321
- if (cur.from == ch) {
322
- if (cur.from != cur.to && sticky != "before") { found = i; }
323
- else { bidiOther = i; }
324
- }
325
- }
326
- return found != null ? found : bidiOther
327
- }
328
-
329
- // Bidirectional ordering algorithm
330
- // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm
331
- // that this (partially) implements.
332
-
333
- // One-char codes used for character types:
334
- // L (L): Left-to-Right
335
- // R (R): Right-to-Left
336
- // r (AL): Right-to-Left Arabic
337
- // 1 (EN): European Number
338
- // + (ES): European Number Separator
339
- // % (ET): European Number Terminator
340
- // n (AN): Arabic Number
341
- // , (CS): Common Number Separator
342
- // m (NSM): Non-Spacing Mark
343
- // b (BN): Boundary Neutral
344
- // s (B): Paragraph Separator
345
- // t (S): Segment Separator
346
- // w (WS): Whitespace
347
- // N (ON): Other Neutrals
348
-
349
- // Returns null if characters are ordered as they appear
350
- // (left-to-right), or an array of sections ({from, to, level}
351
- // objects) in the order in which they occur visually.
352
- var bidiOrdering = (function() {
353
- // Character types for codepoints 0 to 0xff
354
- var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN";
355
- // Character types for codepoints 0x600 to 0x6f9
356
- var arabicTypes = "nnnnnnNNr%%r,rNNmmmmmmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmnNmmmmmmrrmmNmmmmrr1111111111";
357
- function charType(code) {
358
- if (code <= 0xf7) { return lowTypes.charAt(code) }
359
- else if (0x590 <= code && code <= 0x5f4) { return "R" }
360
- else if (0x600 <= code && code <= 0x6f9) { return arabicTypes.charAt(code - 0x600) }
361
- else if (0x6ee <= code && code <= 0x8ac) { return "r" }
362
- else if (0x2000 <= code && code <= 0x200b) { return "w" }
363
- else if (code == 0x200c) { return "b" }
364
- else { return "L" }
365
- }
366
-
367
- var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;
368
- var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/;
369
-
370
- function BidiSpan(level, from, to) {
371
- this.level = level;
372
- this.from = from; this.to = to;
373
- }
374
-
375
- return function(str, direction) {
376
- var outerType = direction == "ltr" ? "L" : "R";
377
-
378
- if (str.length == 0 || direction == "ltr" && !bidiRE.test(str)) { return false }
379
- var len = str.length, types = [];
380
- for (var i = 0; i < len; ++i)
381
- { types.push(charType(str.charCodeAt(i))); }
382
-
383
- // W1. Examine each non-spacing mark (NSM) in the level run, and
384
- // change the type of the NSM to the type of the previous
385
- // character. If the NSM is at the start of the level run, it will
386
- // get the type of sor.
387
- for (var i$1 = 0, prev = outerType; i$1 < len; ++i$1) {
388
- var type = types[i$1];
389
- if (type == "m") { types[i$1] = prev; }
390
- else { prev = type; }
391
- }
392
-
393
- // W2. Search backwards from each instance of a European number
394
- // until the first strong type (R, L, AL, or sor) is found. If an
395
- // AL is found, change the type of the European number to Arabic
396
- // number.
397
- // W3. Change all ALs to R.
398
- for (var i$2 = 0, cur = outerType; i$2 < len; ++i$2) {
399
- var type$1 = types[i$2];
400
- if (type$1 == "1" && cur == "r") { types[i$2] = "n"; }
401
- else if (isStrong.test(type$1)) { cur = type$1; if (type$1 == "r") { types[i$2] = "R"; } }
402
- }
403
-
404
- // W4. A single European separator between two European numbers
405
- // changes to a European number. A single common separator between
406
- // two numbers of the same type changes to that type.
407
- for (var i$3 = 1, prev$1 = types[0]; i$3 < len - 1; ++i$3) {
408
- var type$2 = types[i$3];
409
- if (type$2 == "+" && prev$1 == "1" && types[i$3+1] == "1") { types[i$3] = "1"; }
410
- else if (type$2 == "," && prev$1 == types[i$3+1] &&
411
- (prev$1 == "1" || prev$1 == "n")) { types[i$3] = prev$1; }
412
- prev$1 = type$2;
413
- }
414
-
415
- // W5. A sequence of European terminators adjacent to European
416
- // numbers changes to all European numbers.
417
- // W6. Otherwise, separators and terminators change to Other
418
- // Neutral.
419
- for (var i$4 = 0; i$4 < len; ++i$4) {
420
- var type$3 = types[i$4];
421
- if (type$3 == ",") { types[i$4] = "N"; }
422
- else if (type$3 == "%") {
423
- var end = (void 0);
424
- for (end = i$4 + 1; end < len && types[end] == "%"; ++end) {}
425
- var replace = (i$4 && types[i$4-1] == "!") || (end < len && types[end] == "1") ? "1" : "N";
426
- for (var j = i$4; j < end; ++j) { types[j] = replace; }
427
- i$4 = end - 1;
428
- }
429
- }
430
-
431
- // W7. Search backwards from each instance of a European number
432
- // until the first strong type (R, L, or sor) is found. If an L is
433
- // found, then change the type of the European number to L.
434
- for (var i$5 = 0, cur$1 = outerType; i$5 < len; ++i$5) {
435
- var type$4 = types[i$5];
436
- if (cur$1 == "L" && type$4 == "1") { types[i$5] = "L"; }
437
- else if (isStrong.test(type$4)) { cur$1 = type$4; }
438
- }
439
-
440
- // N1. A sequence of neutrals takes the direction of the
441
- // surrounding strong text if the text on both sides has the same
442
- // direction. European and Arabic numbers act as if they were R in
443
- // terms of their influence on neutrals. Start-of-level-run (sor)
444
- // and end-of-level-run (eor) are used at level run boundaries.
445
- // N2. Any remaining neutrals take the embedding direction.
446
- for (var i$6 = 0; i$6 < len; ++i$6) {
447
- if (isNeutral.test(types[i$6])) {
448
- var end$1 = (void 0);
449
- for (end$1 = i$6 + 1; end$1 < len && isNeutral.test(types[end$1]); ++end$1) {}
450
- var before = (i$6 ? types[i$6-1] : outerType) == "L";
451
- var after = (end$1 < len ? types[end$1] : outerType) == "L";
452
- var replace$1 = before == after ? (before ? "L" : "R") : outerType;
453
- for (var j$1 = i$6; j$1 < end$1; ++j$1) { types[j$1] = replace$1; }
454
- i$6 = end$1 - 1;
455
- }
456
- }
457
-
458
- // Here we depart from the documented algorithm, in order to avoid
459
- // building up an actual levels array. Since there are only three
460
- // levels (0, 1, 2) in an implementation that doesn't take
461
- // explicit embedding into account, we can build up the order on
462
- // the fly, without following the level-based algorithm.
463
- var order = [], m;
464
- for (var i$7 = 0; i$7 < len;) {
465
- if (countsAsLeft.test(types[i$7])) {
466
- var start = i$7;
467
- for (++i$7; i$7 < len && countsAsLeft.test(types[i$7]); ++i$7) {}
468
- order.push(new BidiSpan(0, start, i$7));
469
- } else {
470
- var pos = i$7, at = order.length;
471
- for (++i$7; i$7 < len && types[i$7] != "L"; ++i$7) {}
472
- for (var j$2 = pos; j$2 < i$7;) {
473
- if (countsAsNum.test(types[j$2])) {
474
- if (pos < j$2) { order.splice(at, 0, new BidiSpan(1, pos, j$2)); }
475
- var nstart = j$2;
476
- for (++j$2; j$2 < i$7 && countsAsNum.test(types[j$2]); ++j$2) {}
477
- order.splice(at, 0, new BidiSpan(2, nstart, j$2));
478
- pos = j$2;
479
- } else { ++j$2; }
480
- }
481
- if (pos < i$7) { order.splice(at, 0, new BidiSpan(1, pos, i$7)); }
482
- }
483
- }
484
- if (direction == "ltr") {
485
- if (order[0].level == 1 && (m = str.match(/^\s+/))) {
486
- order[0].from = m[0].length;
487
- order.unshift(new BidiSpan(0, 0, m[0].length));
488
- }
489
- if (lst(order).level == 1 && (m = str.match(/\s+$/))) {
490
- lst(order).to -= m[0].length;
491
- order.push(new BidiSpan(0, len - m[0].length, len));
492
- }
493
- }
494
-
495
- return direction == "rtl" ? order.reverse() : order
496
- }
497
- })();
498
-
499
- // Get the bidi ordering for the given line (and cache it). Returns
500
- // false for lines that are fully left-to-right, and an array of
501
- // BidiSpan objects otherwise.
502
- function getOrder(line, direction) {
503
- var order = line.order;
504
- if (order == null) { order = line.order = bidiOrdering(line.text, direction); }
505
- return order
506
- }
507
-
508
- // EVENT HANDLING
509
-
510
- // Lightweight event framework. on/off also work on DOM nodes,
511
- // registering native DOM handlers.
512
-
513
- var noHandlers = [];
514
-
515
- var on = function(emitter, type, f) {
516
- if (emitter.addEventListener) {
517
- emitter.addEventListener(type, f, false);
518
- } else if (emitter.attachEvent) {
519
- emitter.attachEvent("on" + type, f);
520
- } else {
521
- var map$$1 = emitter._handlers || (emitter._handlers = {});
522
- map$$1[type] = (map$$1[type] || noHandlers).concat(f);
523
- }
524
- };
525
-
526
- function getHandlers(emitter, type) {
527
- return emitter._handlers && emitter._handlers[type] || noHandlers
528
- }
529
-
530
- function off(emitter, type, f) {
531
- if (emitter.removeEventListener) {
532
- emitter.removeEventListener(type, f, false);
533
- } else if (emitter.detachEvent) {
534
- emitter.detachEvent("on" + type, f);
535
- } else {
536
- var map$$1 = emitter._handlers, arr = map$$1 && map$$1[type];
537
- if (arr) {
538
- var index = indexOf(arr, f);
539
- if (index > -1)
540
- { map$$1[type] = arr.slice(0, index).concat(arr.slice(index + 1)); }
541
- }
542
- }
543
- }
544
-
545
- function signal(emitter, type /*, values...*/) {
546
- var handlers = getHandlers(emitter, type);
547
- if (!handlers.length) { return }
548
- var args = Array.prototype.slice.call(arguments, 2);
549
- for (var i = 0; i < handlers.length; ++i) { handlers[i].apply(null, args); }
550
- }
551
-
552
- // The DOM events that CodeMirror handles can be overridden by
553
- // registering a (non-DOM) handler on the editor for the event name,
554
- // and preventDefault-ing the event in that handler.
555
- function signalDOMEvent(cm, e, override) {
556
- if (typeof e == "string")
557
- { e = {type: e, preventDefault: function() { this.defaultPrevented = true; }}; }
558
- signal(cm, override || e.type, cm, e);
559
- return e_defaultPrevented(e) || e.codemirrorIgnore
560
- }
561
-
562
- function signalCursorActivity(cm) {
563
- var arr = cm._handlers && cm._handlers.cursorActivity;
564
- if (!arr) { return }
565
- var set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = []);
566
- for (var i = 0; i < arr.length; ++i) { if (indexOf(set, arr[i]) == -1)
567
- { set.push(arr[i]); } }
568
- }
569
-
570
- function hasHandler(emitter, type) {
571
- return getHandlers(emitter, type).length > 0
572
- }
573
-
574
- // Add on and off methods to a constructor's prototype, to make
575
- // registering events on such objects more convenient.
576
- function eventMixin(ctor) {
577
- ctor.prototype.on = function(type, f) {on(this, type, f);};
578
- ctor.prototype.off = function(type, f) {off(this, type, f);};
579
- }
580
-
581
- // Due to the fact that we still support jurassic IE versions, some
582
- // compatibility wrappers are needed.
583
-
584
- function e_preventDefault(e) {
585
- if (e.preventDefault) { e.preventDefault(); }
586
- else { e.returnValue = false; }
587
- }
588
- function e_stopPropagation(e) {
589
- if (e.stopPropagation) { e.stopPropagation(); }
590
- else { e.cancelBubble = true; }
591
- }
592
- function e_defaultPrevented(e) {
593
- return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false
594
- }
595
- function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);}
596
-
597
- function e_target(e) {return e.target || e.srcElement}
598
- function e_button(e) {
599
- var b = e.which;
600
- if (b == null) {
601
- if (e.button & 1) { b = 1; }
602
- else if (e.button & 2) { b = 3; }
603
- else if (e.button & 4) { b = 2; }
604
- }
605
- if (mac && e.ctrlKey && b == 1) { b = 3; }
606
- return b
607
- }
608
-
609
- // Detect drag-and-drop
610
- var dragAndDrop = function() {
611
- // There is *some* kind of drag-and-drop support in IE6-8, but I
612
- // couldn't get it to work yet.
613
- if (ie && ie_version < 9) { return false }
614
- var div = elt('div');
615
- return "draggable" in div || "dragDrop" in div
616
- }();
617
-
618
- var zwspSupported;
619
- function zeroWidthElement(measure) {
620
- if (zwspSupported == null) {
621
- var test = elt("span", "\u200b");
622
- removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")]));
623
- if (measure.firstChild.offsetHeight != 0)
624
- { zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie && ie_version < 8); }
625
- }
626
- var node = zwspSupported ? elt("span", "\u200b") :
627
- elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px");
628
- node.setAttribute("cm-text", "");
629
- return node
630
- }
631
-
632
- // Feature-detect IE's crummy client rect reporting for bidi text
633
- var badBidiRects;
634
- function hasBadBidiRects(measure) {
635
- if (badBidiRects != null) { return badBidiRects }
636
- var txt = removeChildrenAndAdd(measure, document.createTextNode("A\u062eA"));
637
- var r0 = range(txt, 0, 1).getBoundingClientRect();
638
- var r1 = range(txt, 1, 2).getBoundingClientRect();
639
- removeChildren(measure);
640
- if (!r0 || r0.left == r0.right) { return false } // Safari returns null in some cases (#2780)
641
- return badBidiRects = (r1.right - r0.right < 3)
642
- }
643
-
644
- // See if "".split is the broken IE version, if so, provide an
645
- // alternative way to split lines.
646
- var splitLinesAuto = "\n\nb".split(/\n/).length != 3 ? function (string) {
647
- var pos = 0, result = [], l = string.length;
648
- while (pos <= l) {
649
- var nl = string.indexOf("\n", pos);
650
- if (nl == -1) { nl = string.length; }
651
- var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl);
652
- var rt = line.indexOf("\r");
653
- if (rt != -1) {
654
- result.push(line.slice(0, rt));
655
- pos += rt + 1;
656
- } else {
657
- result.push(line);
658
- pos = nl + 1;
659
- }
660
- }
661
- return result
662
- } : function (string) { return string.split(/\r\n?|\n/); };
663
-
664
- var hasSelection = window.getSelection ? function (te) {
665
- try { return te.selectionStart != te.selectionEnd }
666
- catch(e) { return false }
667
- } : function (te) {
668
- var range$$1;
669
- try {range$$1 = te.ownerDocument.selection.createRange();}
670
- catch(e) {}
671
- if (!range$$1 || range$$1.parentElement() != te) { return false }
672
- return range$$1.compareEndPoints("StartToEnd", range$$1) != 0
673
- };
674
-
675
- var hasCopyEvent = (function () {
676
- var e = elt("div");
677
- if ("oncopy" in e) { return true }
678
- e.setAttribute("oncopy", "return;");
679
- return typeof e.oncopy == "function"
680
- })();
681
-
682
- var badZoomedRects = null;
683
- function hasBadZoomedRects(measure) {
684
- if (badZoomedRects != null) { return badZoomedRects }
685
- var node = removeChildrenAndAdd(measure, elt("span", "x"));
686
- var normal = node.getBoundingClientRect();
687
- var fromRange = range(node, 0, 1).getBoundingClientRect();
688
- return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1
689
- }
690
-
691
- // Known modes, by name and by MIME
692
- var modes = {}, mimeModes = {};
693
-
694
- // Extra arguments are stored as the mode's dependencies, which is
695
- // used by (legacy) mechanisms like loadmode.js to automatically
696
- // load a mode. (Preferred mechanism is the require/define calls.)
697
- function defineMode(name, mode) {
698
- if (arguments.length > 2)
699
- { mode.dependencies = Array.prototype.slice.call(arguments, 2); }
700
- modes[name] = mode;
701
- }
702
-
703
- function defineMIME(mime, spec) {
704
- mimeModes[mime] = spec;
705
- }
706
-
707
- // Given a MIME type, a {name, ...options} config object, or a name
708
- // string, return a mode config object.
709
- function resolveMode(spec) {
710
- if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) {
711
- spec = mimeModes[spec];
712
- } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) {
713
- var found = mimeModes[spec.name];
714
- if (typeof found == "string") { found = {name: found}; }
715
- spec = createObj(found, spec);
716
- spec.name = found.name;
717
- } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) {
718
- return resolveMode("application/xml")
719
- } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+json$/.test(spec)) {
720
- return resolveMode("application/json")
721
- }
722
- if (typeof spec == "string") { return {name: spec} }
723
- else { return spec || {name: "null"} }
724
- }
725
-
726
- // Given a mode spec (anything that resolveMode accepts), find and
727
- // initialize an actual mode object.
728
- function getMode(options, spec) {
729
- spec = resolveMode(spec);
730
- var mfactory = modes[spec.name];
731
- if (!mfactory) { return getMode(options, "text/plain") }
732
- var modeObj = mfactory(options, spec);
733
- if (modeExtensions.hasOwnProperty(spec.name)) {
734
- var exts = modeExtensions[spec.name];
735
- for (var prop in exts) {
736
- if (!exts.hasOwnProperty(prop)) { continue }
737
- if (modeObj.hasOwnProperty(prop)) { modeObj["_" + prop] = modeObj[prop]; }
738
- modeObj[prop] = exts[prop];
739
- }
740
- }
741
- modeObj.name = spec.name;
742
- if (spec.helperType) { modeObj.helperType = spec.helperType; }
743
- if (spec.modeProps) { for (var prop$1 in spec.modeProps)
744
- { modeObj[prop$1] = spec.modeProps[prop$1]; } }
745
-
746
- return modeObj
747
- }
748
-
749
- // This can be used to attach properties to mode objects from
750
- // outside the actual mode definition.
751
- var modeExtensions = {};
752
- function extendMode(mode, properties) {
753
- var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});
754
- copyObj(properties, exts);
755
- }
756
-
757
- function copyState(mode, state) {
758
- if (state === true) { return state }
759
- if (mode.copyState) { return mode.copyState(state) }
760
- var nstate = {};
761
- for (var n in state) {
762
- var val = state[n];
763
- if (val instanceof Array) { val = val.concat([]); }
764
- nstate[n] = val;
765
- }
766
- return nstate
767
- }
768
-
769
- // Given a mode and a state (for that mode), find the inner mode and
770
- // state at the position that the state refers to.
771
- function innerMode(mode, state) {
772
- var info;
773
- while (mode.innerMode) {
774
- info = mode.innerMode(state);
775
- if (!info || info.mode == mode) { break }
776
- state = info.state;
777
- mode = info.mode;
778
- }
779
- return info || {mode: mode, state: state}
780
- }
781
-
782
- function startState(mode, a1, a2) {
783
- return mode.startState ? mode.startState(a1, a2) : true
784
- }
785
-
786
- // STRING STREAM
787
-
788
- // Fed to the mode parsers, provides helper functions to make
789
- // parsers more succinct.
790
-
791
- var StringStream = function(string, tabSize, lineOracle) {
792
- this.pos = this.start = 0;
793
- this.string = string;
794
- this.tabSize = tabSize || 8;
795
- this.lastColumnPos = this.lastColumnValue = 0;
796
- this.lineStart = 0;
797
- this.lineOracle = lineOracle;
798
- };
799
-
800
- StringStream.prototype.eol = function () {return this.pos >= this.string.length};
801
- StringStream.prototype.sol = function () {return this.pos == this.lineStart};
802
- StringStream.prototype.peek = function () {return this.string.charAt(this.pos) || undefined};
803
- StringStream.prototype.next = function () {
804
- if (this.pos < this.string.length)
805
- { return this.string.charAt(this.pos++) }
806
- };
807
- StringStream.prototype.eat = function (match) {
808
- var ch = this.string.charAt(this.pos);
809
- var ok;
810
- if (typeof match == "string") { ok = ch == match; }
811
- else { ok = ch && (match.test ? match.test(ch) : match(ch)); }
812
- if (ok) {++this.pos; return ch}
813
- };
814
- StringStream.prototype.eatWhile = function (match) {
815
- var start = this.pos;
816
- while (this.eat(match)){}
817
- return this.pos > start
818
- };
819
- StringStream.prototype.eatSpace = function () {
820
- var this$1 = this;
821
-
822
- var start = this.pos;
823
- while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) { ++this$1.pos; }
824
- return this.pos > start
825
- };
826
- StringStream.prototype.skipToEnd = function () {this.pos = this.string.length;};
827
- StringStream.prototype.skipTo = function (ch) {
828
- var found = this.string.indexOf(ch, this.pos);
829
- if (found > -1) {this.pos = found; return true}
830
- };
831
- StringStream.prototype.backUp = function (n) {this.pos -= n;};
832
- StringStream.prototype.column = function () {
833
- if (this.lastColumnPos < this.start) {
834
- this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue);
835
- this.lastColumnPos = this.start;
836
- }
837
- return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
838
- };
839
- StringStream.prototype.indentation = function () {
840
- return countColumn(this.string, null, this.tabSize) -
841
- (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
842
- };
843
- StringStream.prototype.match = function (pattern, consume, caseInsensitive) {
844
- if (typeof pattern == "string") {
845
- var cased = function (str) { return caseInsensitive ? str.toLowerCase() : str; };
846
- var substr = this.string.substr(this.pos, pattern.length);
847
- if (cased(substr) == cased(pattern)) {
848
- if (consume !== false) { this.pos += pattern.length; }
849
- return true
850
- }
851
- } else {
852
- var match = this.string.slice(this.pos).match(pattern);
853
- if (match && match.index > 0) { return null }
854
- if (match && consume !== false) { this.pos += match[0].length; }
855
- return match
856
- }
857
- };
858
- StringStream.prototype.current = function (){return this.string.slice(this.start, this.pos)};
859
- StringStream.prototype.hideFirstChars = function (n, inner) {
860
- this.lineStart += n;
861
- try { return inner() }
862
- finally { this.lineStart -= n; }
863
- };
864
- StringStream.prototype.lookAhead = function (n) {
865
- var oracle = this.lineOracle;
866
- return oracle && oracle.lookAhead(n)
867
- };
868
- StringStream.prototype.baseToken = function () {
869
- var oracle = this.lineOracle;
870
- return oracle && oracle.baseToken(this.pos)
871
- };
872
-
873
- // Find the line object corresponding to the given line number.
874
- function getLine(doc, n) {
875
- n -= doc.first;
876
- if (n < 0 || n >= doc.size) { throw new Error("There is no line " + (n + doc.first) + " in the document.") }
877
- var chunk = doc;
878
- while (!chunk.lines) {
879
- for (var i = 0;; ++i) {
880
- var child = chunk.children[i], sz = child.chunkSize();
881
- if (n < sz) { chunk = child; break }
882
- n -= sz;
883
- }
884
- }
885
- return chunk.lines[n]
886
- }
887
-
888
- // Get the part of a document between two positions, as an array of
889
- // strings.
890
- function getBetween(doc, start, end) {
891
- var out = [], n = start.line;
892
- doc.iter(start.line, end.line + 1, function (line) {
893
- var text = line.text;
894
- if (n == end.line) { text = text.slice(0, end.ch); }
895
- if (n == start.line) { text = text.slice(start.ch); }
896
- out.push(text);
897
- ++n;
898
- });
899
- return out
900
- }
901
- // Get the lines between from and to, as array of strings.
902
- function getLines(doc, from, to) {
903
- var out = [];
904
- doc.iter(from, to, function (line) { out.push(line.text); }); // iter aborts when callback returns truthy value
905
- return out
906
- }
907
-
908
- // Update the height of a line, propagating the height change
909
- // upwards to parent nodes.
910
- function updateLineHeight(line, height) {
911
- var diff = height - line.height;
912
- if (diff) { for (var n = line; n; n = n.parent) { n.height += diff; } }
913
- }
914
-
915
- // Given a line object, find its line number by walking up through
916
- // its parent links.
917
- function lineNo(line) {
918
- if (line.parent == null) { return null }
919
- var cur = line.parent, no = indexOf(cur.lines, line);
920
- for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {
921
- for (var i = 0;; ++i) {
922
- if (chunk.children[i] == cur) { break }
923
- no += chunk.children[i].chunkSize();
924
- }
925
- }
926
- return no + cur.first
927
- }
928
-
929
- // Find the line at the given vertical position, using the height
930
- // information in the document tree.
931
- function lineAtHeight(chunk, h) {
932
- var n = chunk.first;
933
- outer: do {
934
- for (var i$1 = 0; i$1 < chunk.children.length; ++i$1) {
935
- var child = chunk.children[i$1], ch = child.height;
936
- if (h < ch) { chunk = child; continue outer }
937
- h -= ch;
938
- n += child.chunkSize();
939
- }
940
- return n
941
- } while (!chunk.lines)
942
- var i = 0;
943
- for (; i < chunk.lines.length; ++i) {
944
- var line = chunk.lines[i], lh = line.height;
945
- if (h < lh) { break }
946
- h -= lh;
947
- }
948
- return n + i
949
- }
950
-
951
- function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size}
952
-
953
- function lineNumberFor(options, i) {
954
- return String(options.lineNumberFormatter(i + options.firstLineNumber))
955
- }
956
-
957
- // A Pos instance represents a position within the text.
958
- function Pos(line, ch, sticky) {
959
- if ( sticky === void 0 ) sticky = null;
960
-
961
- if (!(this instanceof Pos)) { return new Pos(line, ch, sticky) }
962
- this.line = line;
963
- this.ch = ch;
964
- this.sticky = sticky;
965
- }
966
-
967
- // Compare two positions, return 0 if they are the same, a negative
968
- // number when a is less, and a positive number otherwise.
969
- function cmp(a, b) { return a.line - b.line || a.ch - b.ch }
970
-
971
- function equalCursorPos(a, b) { return a.sticky == b.sticky && cmp(a, b) == 0 }
972
-
973
- function copyPos(x) {return Pos(x.line, x.ch)}
974
- function maxPos(a, b) { return cmp(a, b) < 0 ? b : a }
975
- function minPos(a, b) { return cmp(a, b) < 0 ? a : b }
976
-
977
- // Most of the external API clips given positions to make sure they
978
- // actually exist within the document.
979
- function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1))}
980
- function clipPos(doc, pos) {
981
- if (pos.line < doc.first) { return Pos(doc.first, 0) }
982
- var last = doc.first + doc.size - 1;
983
- if (pos.line > last) { return Pos(last, getLine(doc, last).text.length) }
984
- return clipToLen(pos, getLine(doc, pos.line).text.length)
985
- }
986
- function clipToLen(pos, linelen) {
987
- var ch = pos.ch;
988
- if (ch == null || ch > linelen) { return Pos(pos.line, linelen) }
989
- else if (ch < 0) { return Pos(pos.line, 0) }
990
- else { return pos }
991
- }
992
- function clipPosArray(doc, array) {
993
- var out = [];
994
- for (var i = 0; i < array.length; i++) { out[i] = clipPos(doc, array[i]); }
995
- return out
996
- }
997
-
998
- var SavedContext = function(state, lookAhead) {
999
- this.state = state;
1000
- this.lookAhead = lookAhead;
1001
- };
1002
-
1003
- var Context = function(doc, state, line, lookAhead) {
1004
- this.state = state;
1005
- this.doc = doc;
1006
- this.line = line;
1007
- this.maxLookAhead = lookAhead || 0;
1008
- this.baseTokens = null;
1009
- this.baseTokenPos = 1;
1010
- };
1011
-
1012
- Context.prototype.lookAhead = function (n) {
1013
- var line = this.doc.getLine(this.line + n);
1014
- if (line != null && n > this.maxLookAhead) { this.maxLookAhead = n; }
1015
- return line
1016
- };
1017
-
1018
- Context.prototype.baseToken = function (n) {
1019
- var this$1 = this;
1020
-
1021
- if (!this.baseTokens) { return null }
1022
- while (this.baseTokens[this.baseTokenPos] <= n)
1023
- { this$1.baseTokenPos += 2; }
1024
- var type = this.baseTokens[this.baseTokenPos + 1];
1025
- return {type: type && type.replace(/( |^)overlay .*/, ""),
1026
- size: this.baseTokens[this.baseTokenPos] - n}
1027
- };
1028
-
1029
- Context.prototype.nextLine = function () {
1030
- this.line++;
1031
- if (this.maxLookAhead > 0) { this.maxLookAhead--; }
1032
- };
1033
-
1034
- Context.fromSaved = function (doc, saved, line) {
1035
- if (saved instanceof SavedContext)
1036
- { return new Context(doc, copyState(doc.mode, saved.state), line, saved.lookAhead) }
1037
- else
1038
- { return new Context(doc, copyState(doc.mode, saved), line) }
1039
- };
1040
-
1041
- Context.prototype.save = function (copy) {
1042
- var state = copy !== false ? copyState(this.doc.mode, this.state) : this.state;
1043
- return this.maxLookAhead > 0 ? new SavedContext(state, this.maxLookAhead) : state
1044
- };
1045
-
1046
-
1047
- // Compute a style array (an array starting with a mode generation
1048
- // -- for invalidation -- followed by pairs of end positions and
1049
- // style strings), which is used to highlight the tokens on the
1050
- // line.
1051
- function highlightLine(cm, line, context, forceToEnd) {
1052
- // A styles array always starts with a number identifying the
1053
- // mode/overlays that it is based on (for easy invalidation).
1054
- var st = [cm.state.modeGen], lineClasses = {};
1055
- // Compute the base array of styles
1056
- runMode(cm, line.text, cm.doc.mode, context, function (end, style) { return st.push(end, style); },
1057
- lineClasses, forceToEnd);
1058
- var state = context.state;
1059
-
1060
- // Run overlays, adjust style array.
1061
- var loop = function ( o ) {
1062
- context.baseTokens = st;
1063
- var overlay = cm.state.overlays[o], i = 1, at = 0;
1064
- context.state = true;
1065
- runMode(cm, line.text, overlay.mode, context, function (end, style) {
1066
- var start = i;
1067
- // Ensure there's a token end at the current position, and that i points at it
1068
- while (at < end) {
1069
- var i_end = st[i];
1070
- if (i_end > end)
1071
- { st.splice(i, 1, end, st[i+1], i_end); }
1072
- i += 2;
1073
- at = Math.min(end, i_end);
1074
- }
1075
- if (!style) { return }
1076
- if (overlay.opaque) {
1077
- st.splice(start, i - start, end, "overlay " + style);
1078
- i = start + 2;
1079
- } else {
1080
- for (; start < i; start += 2) {
1081
- var cur = st[start+1];
1082
- st[start+1] = (cur ? cur + " " : "") + "overlay " + style;
1083
- }
1084
- }
1085
- }, lineClasses);
1086
- context.state = state;
1087
- context.baseTokens = null;
1088
- context.baseTokenPos = 1;
1089
- };
1090
-
1091
- for (var o = 0; o < cm.state.overlays.length; ++o) loop( o );
1092
-
1093
- return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null}
1094
- }
1095
-
1096
- function getLineStyles(cm, line, updateFrontier) {
1097
- if (!line.styles || line.styles[0] != cm.state.modeGen) {
1098
- var context = getContextBefore(cm, lineNo(line));
1099
- var resetState = line.text.length > cm.options.maxHighlightLength && copyState(cm.doc.mode, context.state);
1100
- var result = highlightLine(cm, line, context);
1101
- if (resetState) { context.state = resetState; }
1102
- line.stateAfter = context.save(!resetState);
1103
- line.styles = result.styles;
1104
- if (result.classes) { line.styleClasses = result.classes; }
1105
- else if (line.styleClasses) { line.styleClasses = null; }
1106
- if (updateFrontier === cm.doc.highlightFrontier)
1107
- { cm.doc.modeFrontier = Math.max(cm.doc.modeFrontier, ++cm.doc.highlightFrontier); }
1108
- }
1109
- return line.styles
1110
- }
1111
-
1112
- function getContextBefore(cm, n, precise) {
1113
- var doc = cm.doc, display = cm.display;
1114
- if (!doc.mode.startState) { return new Context(doc, true, n) }
1115
- var start = findStartLine(cm, n, precise);
1116
- var saved = start > doc.first && getLine(doc, start - 1).stateAfter;
1117
- var context = saved ? Context.fromSaved(doc, saved, start) : new Context(doc, startState(doc.mode), start);
1118
-
1119
- doc.iter(start, n, function (line) {
1120
- processLine(cm, line.text, context);
1121
- var pos = context.line;
1122
- line.stateAfter = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo ? context.save() : null;
1123
- context.nextLine();
1124
- });
1125
- if (precise) { doc.modeFrontier = context.line; }
1126
- return context
1127
- }
1128
-
1129
- // Lightweight form of highlight -- proceed over this line and
1130
- // update state, but don't save a style array. Used for lines that
1131
- // aren't currently visible.
1132
- function processLine(cm, text, context, startAt) {
1133
- var mode = cm.doc.mode;
1134
- var stream = new StringStream(text, cm.options.tabSize, context);
1135
- stream.start = stream.pos = startAt || 0;
1136
- if (text == "") { callBlankLine(mode, context.state); }
1137
- while (!stream.eol()) {
1138
- readToken(mode, stream, context.state);
1139
- stream.start = stream.pos;
1140
- }
1141
- }
1142
-
1143
- function callBlankLine(mode, state) {
1144
- if (mode.blankLine) { return mode.blankLine(state) }
1145
- if (!mode.innerMode) { return }
1146
- var inner = innerMode(mode, state);
1147
- if (inner.mode.blankLine) { return inner.mode.blankLine(inner.state) }
1148
- }
1149
-
1150
- function readToken(mode, stream, state, inner) {
1151
- for (var i = 0; i < 10; i++) {
1152
- if (inner) { inner[0] = innerMode(mode, state).mode; }
1153
- var style = mode.token(stream, state);
1154
- if (stream.pos > stream.start) { return style }
1155
- }
1156
- throw new Error("Mode " + mode.name + " failed to advance stream.")
1157
- }
1158
-
1159
- var Token = function(stream, type, state) {
1160
- this.start = stream.start; this.end = stream.pos;
1161
- this.string = stream.current();
1162
- this.type = type || null;
1163
- this.state = state;
1164
- };
1165
-
1166
- // Utility for getTokenAt and getLineTokens
1167
- function takeToken(cm, pos, precise, asArray) {
1168
- var doc = cm.doc, mode = doc.mode, style;
1169
- pos = clipPos(doc, pos);
1170
- var line = getLine(doc, pos.line), context = getContextBefore(cm, pos.line, precise);
1171
- var stream = new StringStream(line.text, cm.options.tabSize, context), tokens;
1172
- if (asArray) { tokens = []; }
1173
- while ((asArray || stream.pos < pos.ch) && !stream.eol()) {
1174
- stream.start = stream.pos;
1175
- style = readToken(mode, stream, context.state);
1176
- if (asArray) { tokens.push(new Token(stream, style, copyState(doc.mode, context.state))); }
1177
- }
1178
- return asArray ? tokens : new Token(stream, style, context.state)
1179
- }
1180
-
1181
- function extractLineClasses(type, output) {
1182
- if (type) { for (;;) {
1183
- var lineClass = type.match(/(?:^|\s+)line-(background-)?(\S+)/);
1184
- if (!lineClass) { break }
1185
- type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length);
1186
- var prop = lineClass[1] ? "bgClass" : "textClass";
1187
- if (output[prop] == null)
1188
- { output[prop] = lineClass[2]; }
1189
- else if (!(new RegExp("(?:^|\s)" + lineClass[2] + "(?:$|\s)")).test(output[prop]))
1190
- { output[prop] += " " + lineClass[2]; }
1191
- } }
1192
- return type
1193
- }
1194
-
1195
- // Run the given mode's parser over a line, calling f for each token.
1196
- function runMode(cm, text, mode, context, f, lineClasses, forceToEnd) {
1197
- var flattenSpans = mode.flattenSpans;
1198
- if (flattenSpans == null) { flattenSpans = cm.options.flattenSpans; }
1199
- var curStart = 0, curStyle = null;
1200
- var stream = new StringStream(text, cm.options.tabSize, context), style;
1201
- var inner = cm.options.addModeClass && [null];
1202
- if (text == "") { extractLineClasses(callBlankLine(mode, context.state), lineClasses); }
1203
- while (!stream.eol()) {
1204
- if (stream.pos > cm.options.maxHighlightLength) {
1205
- flattenSpans = false;
1206
- if (forceToEnd) { processLine(cm, text, context, stream.pos); }
1207
- stream.pos = text.length;
1208
- style = null;
1209
- } else {
1210
- style = extractLineClasses(readToken(mode, stream, context.state, inner), lineClasses);
1211
- }
1212
- if (inner) {
1213
- var mName = inner[0].name;
1214
- if (mName) { style = "m-" + (style ? mName + " " + style : mName); }
1215
- }
1216
- if (!flattenSpans || curStyle != style) {
1217
- while (curStart < stream.start) {
1218
- curStart = Math.min(stream.start, curStart + 5000);
1219
- f(curStart, curStyle);
1220
- }
1221
- curStyle = style;
1222
- }
1223
- stream.start = stream.pos;
1224
- }
1225
- while (curStart < stream.pos) {
1226
- // Webkit seems to refuse to render text nodes longer than 57444
1227
- // characters, and returns inaccurate measurements in nodes
1228
- // starting around 5000 chars.
1229
- var pos = Math.min(stream.pos, curStart + 5000);
1230
- f(pos, curStyle);
1231
- curStart = pos;
1232
- }
1233
- }
1234
-
1235
- // Finds the line to start with when starting a parse. Tries to
1236
- // find a line with a stateAfter, so that it can start with a
1237
- // valid state. If that fails, it returns the line with the
1238
- // smallest indentation, which tends to need the least context to
1239
- // parse correctly.
1240
- function findStartLine(cm, n, precise) {
1241
- var minindent, minline, doc = cm.doc;
1242
- var lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100);
1243
- for (var search = n; search > lim; --search) {
1244
- if (search <= doc.first) { return doc.first }
1245
- var line = getLine(doc, search - 1), after = line.stateAfter;
1246
- if (after && (!precise || search + (after instanceof SavedContext ? after.lookAhead : 0) <= doc.modeFrontier))
1247
- { return search }
1248
- var indented = countColumn(line.text, null, cm.options.tabSize);
1249
- if (minline == null || minindent > indented) {
1250
- minline = search - 1;
1251
- minindent = indented;
1252
- }
1253
- }
1254
- return minline
1255
- }
1256
-
1257
- function retreatFrontier(doc, n) {
1258
- doc.modeFrontier = Math.min(doc.modeFrontier, n);
1259
- if (doc.highlightFrontier < n - 10) { return }
1260
- var start = doc.first;
1261
- for (var line = n - 1; line > start; line--) {
1262
- var saved = getLine(doc, line).stateAfter;
1263
- // change is on 3
1264
- // state on line 1 looked ahead 2 -- so saw 3
1265
- // test 1 + 2 < 3 should cover this
1266
- if (saved && (!(saved instanceof SavedContext) || line + saved.lookAhead < n)) {
1267
- start = line + 1;
1268
- break
1269
- }
1270
- }
1271
- doc.highlightFrontier = Math.min(doc.highlightFrontier, start);
1272
- }
1273
-
1274
- // Optimize some code when these features are not used.
1275
- var sawReadOnlySpans = false, sawCollapsedSpans = false;
1276
-
1277
- function seeReadOnlySpans() {
1278
- sawReadOnlySpans = true;
1279
- }
1280
-
1281
- function seeCollapsedSpans() {
1282
- sawCollapsedSpans = true;
1283
- }
1284
-
1285
- // TEXTMARKER SPANS
1286
-
1287
- function MarkedSpan(marker, from, to) {
1288
- this.marker = marker;
1289
- this.from = from; this.to = to;
1290
- }
1291
-
1292
- // Search an array of spans for a span matching the given marker.
1293
- function getMarkedSpanFor(spans, marker) {
1294
- if (spans) { for (var i = 0; i < spans.length; ++i) {
1295
- var span = spans[i];
1296
- if (span.marker == marker) { return span }
1297
- } }
1298
- }
1299
- // Remove a span from an array, returning undefined if no spans are
1300
- // left (we don't store arrays for lines without spans).
1301
- function removeMarkedSpan(spans, span) {
1302
- var r;
1303
- for (var i = 0; i < spans.length; ++i)
1304
- { if (spans[i] != span) { (r || (r = [])).push(spans[i]); } }
1305
- return r
1306
- }
1307
- // Add a span to a line.
1308
- function addMarkedSpan(line, span) {
1309
- line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span];
1310
- span.marker.attachLine(line);
1311
- }
1312
-
1313
- // Used for the algorithm that adjusts markers for a change in the
1314
- // document. These functions cut an array of spans at a given
1315
- // character position, returning an array of remaining chunks (or
1316
- // undefined if nothing remains).
1317
- function markedSpansBefore(old, startCh, isInsert) {
1318
- var nw;
1319
- if (old) { for (var i = 0; i < old.length; ++i) {
1320
- var span = old[i], marker = span.marker;
1321
- var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh);
1322
- if (startsBefore || span.from == startCh && marker.type == "bookmark" && (!isInsert || !span.marker.insertLeft)) {
1323
- var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh)
1324
- ;(nw || (nw = [])).push(new MarkedSpan(marker, span.from, endsAfter ? null : span.to));
1325
- }
1326
- } }
1327
- return nw
1328
- }
1329
- function markedSpansAfter(old, endCh, isInsert) {
1330
- var nw;
1331
- if (old) { for (var i = 0; i < old.length; ++i) {
1332
- var span = old[i], marker = span.marker;
1333
- var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh);
1334
- if (endsAfter || span.from == endCh && marker.type == "bookmark" && (!isInsert || span.marker.insertLeft)) {
1335
- var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh)
1336
- ;(nw || (nw = [])).push(new MarkedSpan(marker, startsBefore ? null : span.from - endCh,
1337
- span.to == null ? null : span.to - endCh));
1338
- }
1339
- } }
1340
- return nw
1341
- }
1342
-
1343
- // Given a change object, compute the new set of marker spans that
1344
- // cover the line in which the change took place. Removes spans
1345
- // entirely within the change, reconnects spans belonging to the
1346
- // same marker that appear on both sides of the change, and cuts off
1347
- // spans partially within the change. Returns an array of span
1348
- // arrays with one element for each line in (after) the change.
1349
- function stretchSpansOverChange(doc, change) {
1350
- if (change.full) { return null }
1351
- var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans;
1352
- var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans;
1353
- if (!oldFirst && !oldLast) { return null }
1354
-
1355
- var startCh = change.from.ch, endCh = change.to.ch, isInsert = cmp(change.from, change.to) == 0;
1356
- // Get the spans that 'stick out' on both sides
1357
- var first = markedSpansBefore(oldFirst, startCh, isInsert);
1358
- var last = markedSpansAfter(oldLast, endCh, isInsert);
1359
-
1360
- // Next, merge those two ends
1361
- var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0);
1362
- if (first) {
1363
- // Fix up .to properties of first
1364
- for (var i = 0; i < first.length; ++i) {
1365
- var span = first[i];
1366
- if (span.to == null) {
1367
- var found = getMarkedSpanFor(last, span.marker);
1368
- if (!found) { span.to = startCh; }
1369
- else if (sameLine) { span.to = found.to == null ? null : found.to + offset; }
1370
- }
1371
- }
1372
- }
1373
- if (last) {
1374
- // Fix up .from in last (or move them into first in case of sameLine)
1375
- for (var i$1 = 0; i$1 < last.length; ++i$1) {
1376
- var span$1 = last[i$1];
1377
- if (span$1.to != null) { span$1.to += offset; }
1378
- if (span$1.from == null) {
1379
- var found$1 = getMarkedSpanFor(first, span$1.marker);
1380
- if (!found$1) {
1381
- span$1.from = offset;
1382
- if (sameLine) { (first || (first = [])).push(span$1); }
1383
- }
1384
- } else {
1385
- span$1.from += offset;
1386
- if (sameLine) { (first || (first = [])).push(span$1); }
1387
- }
1388
- }
1389
- }
1390
- // Make sure we didn't create any zero-length spans
1391
- if (first) { first = clearEmptySpans(first); }
1392
- if (last && last != first) { last = clearEmptySpans(last); }
1393
-
1394
- var newMarkers = [first];
1395
- if (!sameLine) {
1396
- // Fill gap with whole-line-spans
1397
- var gap = change.text.length - 2, gapMarkers;
1398
- if (gap > 0 && first)
1399
- { for (var i$2 = 0; i$2 < first.length; ++i$2)
1400
- { if (first[i$2].to == null)
1401
- { (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i$2].marker, null, null)); } } }
1402
- for (var i$3 = 0; i$3 < gap; ++i$3)
1403
- { newMarkers.push(gapMarkers); }
1404
- newMarkers.push(last);
1405
- }
1406
- return newMarkers
1407
- }
1408
-
1409
- // Remove spans that are empty and don't have a clearWhenEmpty
1410
- // option of false.
1411
- function clearEmptySpans(spans) {
1412
- for (var i = 0; i < spans.length; ++i) {
1413
- var span = spans[i];
1414
- if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false)
1415
- { spans.splice(i--, 1); }
1416
- }
1417
- if (!spans.length) { return null }
1418
- return spans
1419
- }
1420
-
1421
- // Used to 'clip' out readOnly ranges when making a change.
1422
- function removeReadOnlyRanges(doc, from, to) {
1423
- var markers = null;
1424
- doc.iter(from.line, to.line + 1, function (line) {
1425
- if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) {
1426
- var mark = line.markedSpans[i].marker;
1427
- if (mark.readOnly && (!markers || indexOf(markers, mark) == -1))
1428
- { (markers || (markers = [])).push(mark); }
1429
- } }
1430
- });
1431
- if (!markers) { return null }
1432
- var parts = [{from: from, to: to}];
1433
- for (var i = 0; i < markers.length; ++i) {
1434
- var mk = markers[i], m = mk.find(0);
1435
- for (var j = 0; j < parts.length; ++j) {
1436
- var p = parts[j];
1437
- if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) { continue }
1438
- var newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to);
1439
- if (dfrom < 0 || !mk.inclusiveLeft && !dfrom)
1440
- { newParts.push({from: p.from, to: m.from}); }
1441
- if (dto > 0 || !mk.inclusiveRight && !dto)
1442
- { newParts.push({from: m.to, to: p.to}); }
1443
- parts.splice.apply(parts, newParts);
1444
- j += newParts.length - 3;
1445
- }
1446
- }
1447
- return parts
1448
- }
1449
-
1450
- // Connect or disconnect spans from a line.
1451
- function detachMarkedSpans(line) {
1452
- var spans = line.markedSpans;
1453
- if (!spans) { return }
1454
- for (var i = 0; i < spans.length; ++i)
1455
- { spans[i].marker.detachLine(line); }
1456
- line.markedSpans = null;
1457
- }
1458
- function attachMarkedSpans(line, spans) {
1459
- if (!spans) { return }
1460
- for (var i = 0; i < spans.length; ++i)
1461
- { spans[i].marker.attachLine(line); }
1462
- line.markedSpans = spans;
1463
- }
1464
-
1465
- // Helpers used when computing which overlapping collapsed span
1466
- // counts as the larger one.
1467
- function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0 }
1468
- function extraRight(marker) { return marker.inclusiveRight ? 1 : 0 }
1469
-
1470
- // Returns a number indicating which of two overlapping collapsed
1471
- // spans is larger (and thus includes the other). Falls back to
1472
- // comparing ids when the spans cover exactly the same range.
1473
- function compareCollapsedMarkers(a, b) {
1474
- var lenDiff = a.lines.length - b.lines.length;
1475
- if (lenDiff != 0) { return lenDiff }
1476
- var aPos = a.find(), bPos = b.find();
1477
- var fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b);
1478
- if (fromCmp) { return -fromCmp }
1479
- var toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b);
1480
- if (toCmp) { return toCmp }
1481
- return b.id - a.id
1482
- }
1483
-
1484
- // Find out whether a line ends or starts in a collapsed span. If
1485
- // so, return the marker for that span.
1486
- function collapsedSpanAtSide(line, start) {
1487
- var sps = sawCollapsedSpans && line.markedSpans, found;
1488
- if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) {
1489
- sp = sps[i];
1490
- if (sp.marker.collapsed && (start ? sp.from : sp.to) == null &&
1491
- (!found || compareCollapsedMarkers(found, sp.marker) < 0))
1492
- { found = sp.marker; }
1493
- } }
1494
- return found
1495
- }
1496
- function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true) }
1497
- function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false) }
1498
-
1499
- function collapsedSpanAround(line, ch) {
1500
- var sps = sawCollapsedSpans && line.markedSpans, found;
1501
- if (sps) { for (var i = 0; i < sps.length; ++i) {
1502
- var sp = sps[i];
1503
- if (sp.marker.collapsed && (sp.from == null || sp.from < ch) && (sp.to == null || sp.to > ch) &&
1504
- (!found || compareCollapsedMarkers(found, sp.marker) < 0)) { found = sp.marker; }
1505
- } }
1506
- return found
1507
- }
1508
-
1509
- // Test whether there exists a collapsed span that partially
1510
- // overlaps (covers the start or end, but not both) of a new span.
1511
- // Such overlap is not allowed.
1512
- function conflictingCollapsedRange(doc, lineNo$$1, from, to, marker) {
1513
- var line = getLine(doc, lineNo$$1);
1514
- var sps = sawCollapsedSpans && line.markedSpans;
1515
- if (sps) { for (var i = 0; i < sps.length; ++i) {
1516
- var sp = sps[i];
1517
- if (!sp.marker.collapsed) { continue }
1518
- var found = sp.marker.find(0);
1519
- var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker);
1520
- var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker);
1521
- if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) { continue }
1522
- if (fromCmp <= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.to, from) >= 0 : cmp(found.to, from) > 0) ||
1523
- fromCmp >= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.from, to) <= 0 : cmp(found.from, to) < 0))
1524
- { return true }
1525
- } }
1526
- }
1527
-
1528
- // A visual line is a line as drawn on the screen. Folding, for
1529
- // example, can cause multiple logical lines to appear on the same
1530
- // visual line. This finds the start of the visual line that the
1531
- // given line is part of (usually that is the line itself).
1532
- function visualLine(line) {
1533
- var merged;
1534
- while (merged = collapsedSpanAtStart(line))
1535
- { line = merged.find(-1, true).line; }
1536
- return line
1537
- }
1538
-
1539
- function visualLineEnd(line) {
1540
- var merged;
1541
- while (merged = collapsedSpanAtEnd(line))
1542
- { line = merged.find(1, true).line; }
1543
- return line
1544
- }
1545
-
1546
- // Returns an array of logical lines that continue the visual line
1547
- // started by the argument, or undefined if there are no such lines.
1548
- function visualLineContinued(line) {
1549
- var merged, lines;
1550
- while (merged = collapsedSpanAtEnd(line)) {
1551
- line = merged.find(1, true).line
1552
- ;(lines || (lines = [])).push(line);
1553
- }
1554
- return lines
1555
- }
1556
-
1557
- // Get the line number of the start of the visual line that the
1558
- // given line number is part of.
1559
- function visualLineNo(doc, lineN) {
1560
- var line = getLine(doc, lineN), vis = visualLine(line);
1561
- if (line == vis) { return lineN }
1562
- return lineNo(vis)
1563
- }
1564
-
1565
- // Get the line number of the start of the next visual line after
1566
- // the given line.
1567
- function visualLineEndNo(doc, lineN) {
1568
- if (lineN > doc.lastLine()) { return lineN }
1569
- var line = getLine(doc, lineN), merged;
1570
- if (!lineIsHidden(doc, line)) { return lineN }
1571
- while (merged = collapsedSpanAtEnd(line))
1572
- { line = merged.find(1, true).line; }
1573
- return lineNo(line) + 1
1574
- }
1575
-
1576
- // Compute whether a line is hidden. Lines count as hidden when they
1577
- // are part of a visual line that starts with another line, or when
1578
- // they are entirely covered by collapsed, non-widget span.
1579
- function lineIsHidden(doc, line) {
1580
- var sps = sawCollapsedSpans && line.markedSpans;
1581
- if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) {
1582
- sp = sps[i];
1583
- if (!sp.marker.collapsed) { continue }
1584
- if (sp.from == null) { return true }
1585
- if (sp.marker.widgetNode) { continue }
1586
- if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp))
1587
- { return true }
1588
- } }
1589
- }
1590
- function lineIsHiddenInner(doc, line, span) {
1591
- if (span.to == null) {
1592
- var end = span.marker.find(1, true);
1593
- return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpans, span.marker))
1594
- }
1595
- if (span.marker.inclusiveRight && span.to == line.text.length)
1596
- { return true }
1597
- for (var sp = (void 0), i = 0; i < line.markedSpans.length; ++i) {
1598
- sp = line.markedSpans[i];
1599
- if (sp.marker.collapsed && !sp.marker.widgetNode && sp.from == span.to &&
1600
- (sp.to == null || sp.to != span.from) &&
1601
- (sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
1602
- lineIsHiddenInner(doc, line, sp)) { return true }
1603
- }
1604
- }
1605
-
1606
- // Find the height above the given line.
1607
- function heightAtLine(lineObj) {
1608
- lineObj = visualLine(lineObj);
1609
-
1610
- var h = 0, chunk = lineObj.parent;
1611
- for (var i = 0; i < chunk.lines.length; ++i) {
1612
- var line = chunk.lines[i];
1613
- if (line == lineObj) { break }
1614
- else { h += line.height; }
1615
- }
1616
- for (var p = chunk.parent; p; chunk = p, p = chunk.parent) {
1617
- for (var i$1 = 0; i$1 < p.children.length; ++i$1) {
1618
- var cur = p.children[i$1];
1619
- if (cur == chunk) { break }
1620
- else { h += cur.height; }
1621
- }
1622
- }
1623
- return h
1624
- }
1625
-
1626
- // Compute the character length of a line, taking into account
1627
- // collapsed ranges (see markText) that might hide parts, and join
1628
- // other lines onto it.
1629
- function lineLength(line) {
1630
- if (line.height == 0) { return 0 }
1631
- var len = line.text.length, merged, cur = line;
1632
- while (merged = collapsedSpanAtStart(cur)) {
1633
- var found = merged.find(0, true);
1634
- cur = found.from.line;
1635
- len += found.from.ch - found.to.ch;
1636
- }
1637
- cur = line;
1638
- while (merged = collapsedSpanAtEnd(cur)) {
1639
- var found$1 = merged.find(0, true);
1640
- len -= cur.text.length - found$1.from.ch;
1641
- cur = found$1.to.line;
1642
- len += cur.text.length - found$1.to.ch;
1643
- }
1644
- return len
1645
- }
1646
-
1647
- // Find the longest line in the document.
1648
- function findMaxLine(cm) {
1649
- var d = cm.display, doc = cm.doc;
1650
- d.maxLine = getLine(doc, doc.first);
1651
- d.maxLineLength = lineLength(d.maxLine);
1652
- d.maxLineChanged = true;
1653
- doc.iter(function (line) {
1654
- var len = lineLength(line);
1655
- if (len > d.maxLineLength) {
1656
- d.maxLineLength = len;
1657
- d.maxLine = line;
1658
- }
1659
- });
1660
- }
1661
-
1662
- // LINE DATA STRUCTURE
1663
-
1664
- // Line objects. These hold state related to a line, including
1665
- // highlighting info (the styles array).
1666
- var Line = function(text, markedSpans, estimateHeight) {
1667
- this.text = text;
1668
- attachMarkedSpans(this, markedSpans);
1669
- this.height = estimateHeight ? estimateHeight(this) : 1;
1670
- };
1671
-
1672
- Line.prototype.lineNo = function () { return lineNo(this) };
1673
- eventMixin(Line);
1674
-
1675
- // Change the content (text, markers) of a line. Automatically
1676
- // invalidates cached information and tries to re-estimate the
1677
- // line's height.
1678
- function updateLine(line, text, markedSpans, estimateHeight) {
1679
- line.text = text;
1680
- if (line.stateAfter) { line.stateAfter = null; }
1681
- if (line.styles) { line.styles = null; }
1682
- if (line.order != null) { line.order = null; }
1683
- detachMarkedSpans(line);
1684
- attachMarkedSpans(line, markedSpans);
1685
- var estHeight = estimateHeight ? estimateHeight(line) : 1;
1686
- if (estHeight != line.height) { updateLineHeight(line, estHeight); }
1687
- }
1688
-
1689
- // Detach a line from the document tree and its markers.
1690
- function cleanUpLine(line) {
1691
- line.parent = null;
1692
- detachMarkedSpans(line);
1693
- }
1694
-
1695
- // Convert a style as returned by a mode (either null, or a string
1696
- // containing one or more styles) to a CSS style. This is cached,
1697
- // and also looks for line-wide styles.
1698
- var styleToClassCache = {}, styleToClassCacheWithMode = {};
1699
- function interpretTokenStyle(style, options) {
1700
- if (!style || /^\s*$/.test(style)) { return null }
1701
- var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache;
1702
- return cache[style] ||
1703
- (cache[style] = style.replace(/\S+/g, "cm-$&"))
1704
- }
1705
-
1706
- // Render the DOM representation of the text of a line. Also builds
1707
- // up a 'line map', which points at the DOM nodes that represent
1708
- // specific stretches of text, and is used by the measuring code.
1709
- // The returned object contains the DOM node, this map, and
1710
- // information about line-wide styles that were set by the mode.
1711
- function buildLineContent(cm, lineView) {
1712
- // The padding-right forces the element to have a 'border', which
1713
- // is needed on Webkit to be able to get line-level bounding
1714
- // rectangles for it (in measureChar).
1715
- var content = eltP("span", null, null, webkit ? "padding-right: .1px" : null);
1716
- var builder = {pre: eltP("pre", [content], "CodeMirror-line"), content: content,
1717
- col: 0, pos: 0, cm: cm,
1718
- trailingSpace: false,
1719
- splitSpaces: cm.getOption("lineWrapping")};
1720
- lineView.measure = {};
1721
-
1722
- // Iterate over the logical lines that make up this visual line.
1723
- for (var i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) {
1724
- var line = i ? lineView.rest[i - 1] : lineView.line, order = (void 0);
1725
- builder.pos = 0;
1726
- builder.addToken = buildToken;
1727
- // Optionally wire in some hacks into the token-rendering
1728
- // algorithm, to deal with browser quirks.
1729
- if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line, cm.doc.direction)))
1730
- { builder.addToken = buildTokenBadBidi(builder.addToken, order); }
1731
- builder.map = [];
1732
- var allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line);
1733
- insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate));
1734
- if (line.styleClasses) {
1735
- if (line.styleClasses.bgClass)
1736
- { builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || ""); }
1737
- if (line.styleClasses.textClass)
1738
- { builder.textClass = joinClasses(line.styleClasses.textClass, builder.textClass || ""); }
1739
- }
1740
-
1741
- // Ensure at least a single node is present, for measuring.
1742
- if (builder.map.length == 0)
1743
- { builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.display.measure))); }
1744
-
1745
- // Store the map and a cache object for the current logical line
1746
- if (i == 0) {
1747
- lineView.measure.map = builder.map;
1748
- lineView.measure.cache = {};
1749
- } else {
1750
- (lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map)
1751
- ;(lineView.measure.caches || (lineView.measure.caches = [])).push({});
1752
- }
1753
- }
1754
-
1755
- // See issue #2901
1756
- if (webkit) {
1757
- var last = builder.content.lastChild;
1758
- if (/\bcm-tab\b/.test(last.className) || (last.querySelector && last.querySelector(".cm-tab")))
1759
- { builder.content.className = "cm-tab-wrap-hack"; }
1760
- }
1761
-
1762
- signal(cm, "renderLine", cm, lineView.line, builder.pre);
1763
- if (builder.pre.className)
1764
- { builder.textClass = joinClasses(builder.pre.className, builder.textClass || ""); }
1765
-
1766
- return builder
1767
- }
1768
-
1769
- function defaultSpecialCharPlaceholder(ch) {
1770
- var token = elt("span", "\u2022", "cm-invalidchar");
1771
- token.title = "\\u" + ch.charCodeAt(0).toString(16);
1772
- token.setAttribute("aria-label", token.title);
1773
- return token
1774
- }
1775
-
1776
- // Build up the DOM representation for a single token, and add it to
1777
- // the line map. Takes care to render special characters separately.
1778
- function buildToken(builder, text, style, startStyle, endStyle, css, attributes) {
1779
- if (!text) { return }
1780
- var displayText = builder.splitSpaces ? splitSpaces(text, builder.trailingSpace) : text;
1781
- var special = builder.cm.state.specialChars, mustWrap = false;
1782
- var content;
1783
- if (!special.test(text)) {
1784
- builder.col += text.length;
1785
- content = document.createTextNode(displayText);
1786
- builder.map.push(builder.pos, builder.pos + text.length, content);
1787
- if (ie && ie_version < 9) { mustWrap = true; }
1788
- builder.pos += text.length;
1789
- } else {
1790
- content = document.createDocumentFragment();
1791
- var pos = 0;
1792
- while (true) {
1793
- special.lastIndex = pos;
1794
- var m = special.exec(text);
1795
- var skipped = m ? m.index - pos : text.length - pos;
1796
- if (skipped) {
1797
- var txt = document.createTextNode(displayText.slice(pos, pos + skipped));
1798
- if (ie && ie_version < 9) { content.appendChild(elt("span", [txt])); }
1799
- else { content.appendChild(txt); }
1800
- builder.map.push(builder.pos, builder.pos + skipped, txt);
1801
- builder.col += skipped;
1802
- builder.pos += skipped;
1803
- }
1804
- if (!m) { break }
1805
- pos += skipped + 1;
1806
- var txt$1 = (void 0);
1807
- if (m[0] == "\t") {
1808
- var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize;
1809
- txt$1 = content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab"));
1810
- txt$1.setAttribute("role", "presentation");
1811
- txt$1.setAttribute("cm-text", "\t");
1812
- builder.col += tabWidth;
1813
- } else if (m[0] == "\r" || m[0] == "\n") {
1814
- txt$1 = content.appendChild(elt("span", m[0] == "\r" ? "\u240d" : "\u2424", "cm-invalidchar"));
1815
- txt$1.setAttribute("cm-text", m[0]);
1816
- builder.col += 1;
1817
- } else {
1818
- txt$1 = builder.cm.options.specialCharPlaceholder(m[0]);
1819
- txt$1.setAttribute("cm-text", m[0]);
1820
- if (ie && ie_version < 9) { content.appendChild(elt("span", [txt$1])); }
1821
- else { content.appendChild(txt$1); }
1822
- builder.col += 1;
1823
- }
1824
- builder.map.push(builder.pos, builder.pos + 1, txt$1);
1825
- builder.pos++;
1826
- }
1827
- }
1828
- builder.trailingSpace = displayText.charCodeAt(text.length - 1) == 32;
1829
- if (style || startStyle || endStyle || mustWrap || css) {
1830
- var fullStyle = style || "";
1831
- if (startStyle) { fullStyle += startStyle; }
1832
- if (endStyle) { fullStyle += endStyle; }
1833
- var token = elt("span", [content], fullStyle, css);
1834
- if (attributes) {
1835
- for (var attr in attributes) { if (attributes.hasOwnProperty(attr) && attr != "style" && attr != "class")
1836
- { token.setAttribute(attr, attributes[attr]); } }
1837
- }
1838
- return builder.content.appendChild(token)
1839
- }
1840
- builder.content.appendChild(content);
1841
- }
1842
-
1843
- // Change some spaces to NBSP to prevent the browser from collapsing
1844
- // trailing spaces at the end of a line when rendering text (issue #1362).
1845
- function splitSpaces(text, trailingBefore) {
1846
- if (text.length > 1 && !/ /.test(text)) { return text }
1847
- var spaceBefore = trailingBefore, result = "";
1848
- for (var i = 0; i < text.length; i++) {
1849
- var ch = text.charAt(i);
1850
- if (ch == " " && spaceBefore && (i == text.length - 1 || text.charCodeAt(i + 1) == 32))
1851
- { ch = "\u00a0"; }
1852
- result += ch;
1853
- spaceBefore = ch == " ";
1854
- }
1855
- return result
1856
- }
1857
-
1858
- // Work around nonsense dimensions being reported for stretches of
1859
- // right-to-left text.
1860
- function buildTokenBadBidi(inner, order) {
1861
- return function (builder, text, style, startStyle, endStyle, css, attributes) {
1862
- style = style ? style + " cm-force-border" : "cm-force-border";
1863
- var start = builder.pos, end = start + text.length;
1864
- for (;;) {
1865
- // Find the part that overlaps with the start of this text
1866
- var part = (void 0);
1867
- for (var i = 0; i < order.length; i++) {
1868
- part = order[i];
1869
- if (part.to > start && part.from <= start) { break }
1870
- }
1871
- if (part.to >= end) { return inner(builder, text, style, startStyle, endStyle, css, attributes) }
1872
- inner(builder, text.slice(0, part.to - start), style, startStyle, null, css, attributes);
1873
- startStyle = null;
1874
- text = text.slice(part.to - start);
1875
- start = part.to;
1876
- }
1877
- }
1878
- }
1879
-
1880
- function buildCollapsedSpan(builder, size, marker, ignoreWidget) {
1881
- var widget = !ignoreWidget && marker.widgetNode;
1882
- if (widget) { builder.map.push(builder.pos, builder.pos + size, widget); }
1883
- if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) {
1884
- if (!widget)
1885
- { widget = builder.content.appendChild(document.createElement("span")); }
1886
- widget.setAttribute("cm-marker", marker.id);
1887
- }
1888
- if (widget) {
1889
- builder.cm.display.input.setUneditable(widget);
1890
- builder.content.appendChild(widget);
1891
- }
1892
- builder.pos += size;
1893
- builder.trailingSpace = false;
1894
- }
1895
-
1896
- // Outputs a number of spans to make up a line, taking highlighting
1897
- // and marked text into account.
1898
- function insertLineContent(line, builder, styles) {
1899
- var spans = line.markedSpans, allText = line.text, at = 0;
1900
- if (!spans) {
1901
- for (var i$1 = 1; i$1 < styles.length; i$1+=2)
1902
- { builder.addToken(builder, allText.slice(at, at = styles[i$1]), interpretTokenStyle(styles[i$1+1], builder.cm.options)); }
1903
- return
1904
- }
1905
-
1906
- var len = allText.length, pos = 0, i = 1, text = "", style, css;
1907
- var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, collapsed, attributes;
1908
- for (;;) {
1909
- if (nextChange == pos) { // Update current marker set
1910
- spanStyle = spanEndStyle = spanStartStyle = css = "";
1911
- attributes = null;
1912
- collapsed = null; nextChange = Infinity;
1913
- var foundBookmarks = [], endStyles = (void 0);
1914
- for (var j = 0; j < spans.length; ++j) {
1915
- var sp = spans[j], m = sp.marker;
1916
- if (m.type == "bookmark" && sp.from == pos && m.widgetNode) {
1917
- foundBookmarks.push(m);
1918
- } else if (sp.from <= pos && (sp.to == null || sp.to > pos || m.collapsed && sp.to == pos && sp.from == pos)) {
1919
- if (sp.to != null && sp.to != pos && nextChange > sp.to) {
1920
- nextChange = sp.to;
1921
- spanEndStyle = "";
1922
- }
1923
- if (m.className) { spanStyle += " " + m.className; }
1924
- if (m.css) { css = (css ? css + ";" : "") + m.css; }
1925
- if (m.startStyle && sp.from == pos) { spanStartStyle += " " + m.startStyle; }
1926
- if (m.endStyle && sp.to == nextChange) { (endStyles || (endStyles = [])).push(m.endStyle, sp.to); }
1927
- // support for the old title property
1928
- // https://github.com/codemirror/CodeMirror/pull/5673
1929
- if (m.title) { (attributes || (attributes = {})).title = m.title; }
1930
- if (m.attributes) {
1931
- for (var attr in m.attributes)
1932
- { (attributes || (attributes = {}))[attr] = m.attributes[attr]; }
1933
- }
1934
- if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0))
1935
- { collapsed = sp; }
1936
- } else if (sp.from > pos && nextChange > sp.from) {
1937
- nextChange = sp.from;
1938
- }
1939
- }
1940
- if (endStyles) { for (var j$1 = 0; j$1 < endStyles.length; j$1 += 2)
1941
- { if (endStyles[j$1 + 1] == nextChange) { spanEndStyle += " " + endStyles[j$1]; } } }
1942
-
1943
- if (!collapsed || collapsed.from == pos) { for (var j$2 = 0; j$2 < foundBookmarks.length; ++j$2)
1944
- { buildCollapsedSpan(builder, 0, foundBookmarks[j$2]); } }
1945
- if (collapsed && (collapsed.from || 0) == pos) {
1946
- buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos,
1947
- collapsed.marker, collapsed.from == null);
1948
- if (collapsed.to == null) { return }
1949
- if (collapsed.to == pos) { collapsed = false; }
1950
- }
1951
- }
1952
- if (pos >= len) { break }
1953
-
1954
- var upto = Math.min(len, nextChange);
1955
- while (true) {
1956
- if (text) {
1957
- var end = pos + text.length;
1958
- if (!collapsed) {
1959
- var tokenText = end > upto ? text.slice(0, upto - pos) : text;
1960
- builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle,
1961
- spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", css, attributes);
1962
- }
1963
- if (end >= upto) {text = text.slice(upto - pos); pos = upto; break}
1964
- pos = end;
1965
- spanStartStyle = "";
1966
- }
1967
- text = allText.slice(at, at = styles[i++]);
1968
- style = interpretTokenStyle(styles[i++], builder.cm.options);
1969
- }
1970
- }
1971
- }
1972
-
1973
-
1974
- // These objects are used to represent the visible (currently drawn)
1975
- // part of the document. A LineView may correspond to multiple
1976
- // logical lines, if those are connected by collapsed ranges.
1977
- function LineView(doc, line, lineN) {
1978
- // The starting line
1979
- this.line = line;
1980
- // Continuing lines, if any
1981
- this.rest = visualLineContinued(line);
1982
- // Number of logical lines in this visual line
1983
- this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1;
1984
- this.node = this.text = null;
1985
- this.hidden = lineIsHidden(doc, line);
1986
- }
1987
-
1988
- // Create a range of LineView objects for the given lines.
1989
- function buildViewArray(cm, from, to) {
1990
- var array = [], nextPos;
1991
- for (var pos = from; pos < to; pos = nextPos) {
1992
- var view = new LineView(cm.doc, getLine(cm.doc, pos), pos);
1993
- nextPos = pos + view.size;
1994
- array.push(view);
1995
- }
1996
- return array
1997
- }
1998
-
1999
- var operationGroup = null;
2000
-
2001
- function pushOperation(op) {
2002
- if (operationGroup) {
2003
- operationGroup.ops.push(op);
2004
- } else {
2005
- op.ownsGroup = operationGroup = {
2006
- ops: [op],
2007
- delayedCallbacks: []
2008
- };
2009
- }
2010
- }
2011
-
2012
- function fireCallbacksForOps(group) {
2013
- // Calls delayed callbacks and cursorActivity handlers until no
2014
- // new ones appear
2015
- var callbacks = group.delayedCallbacks, i = 0;
2016
- do {
2017
- for (; i < callbacks.length; i++)
2018
- { callbacks[i].call(null); }
2019
- for (var j = 0; j < group.ops.length; j++) {
2020
- var op = group.ops[j];
2021
- if (op.cursorActivityHandlers)
2022
- { while (op.cursorActivityCalled < op.cursorActivityHandlers.length)
2023
- { op.cursorActivityHandlers[op.cursorActivityCalled++].call(null, op.cm); } }
2024
- }
2025
- } while (i < callbacks.length)
2026
- }
2027
-
2028
- function finishOperation(op, endCb) {
2029
- var group = op.ownsGroup;
2030
- if (!group) { return }
2031
-
2032
- try { fireCallbacksForOps(group); }
2033
- finally {
2034
- operationGroup = null;
2035
- endCb(group);
2036
- }
2037
- }
2038
-
2039
- var orphanDelayedCallbacks = null;
2040
-
2041
- // Often, we want to signal events at a point where we are in the
2042
- // middle of some work, but don't want the handler to start calling
2043
- // other methods on the editor, which might be in an inconsistent
2044
- // state or simply not expect any other events to happen.
2045
- // signalLater looks whether there are any handlers, and schedules
2046
- // them to be executed when the last operation ends, or, if no
2047
- // operation is active, when a timeout fires.
2048
- function signalLater(emitter, type /*, values...*/) {
2049
- var arr = getHandlers(emitter, type);
2050
- if (!arr.length) { return }
2051
- var args = Array.prototype.slice.call(arguments, 2), list;
2052
- if (operationGroup) {
2053
- list = operationGroup.delayedCallbacks;
2054
- } else if (orphanDelayedCallbacks) {
2055
- list = orphanDelayedCallbacks;
2056
- } else {
2057
- list = orphanDelayedCallbacks = [];
2058
- setTimeout(fireOrphanDelayed, 0);
2059
- }
2060
- var loop = function ( i ) {
2061
- list.push(function () { return arr[i].apply(null, args); });
2062
- };
2063
-
2064
- for (var i = 0; i < arr.length; ++i)
2065
- loop( i );
2066
- }
2067
-
2068
- function fireOrphanDelayed() {
2069
- var delayed = orphanDelayedCallbacks;
2070
- orphanDelayedCallbacks = null;
2071
- for (var i = 0; i < delayed.length; ++i) { delayed[i](); }
2072
- }
2073
-
2074
- // When an aspect of a line changes, a string is added to
2075
- // lineView.changes. This updates the relevant part of the line's
2076
- // DOM structure.
2077
- function updateLineForChanges(cm, lineView, lineN, dims) {
2078
- for (var j = 0; j < lineView.changes.length; j++) {
2079
- var type = lineView.changes[j];
2080
- if (type == "text") { updateLineText(cm, lineView); }
2081
- else if (type == "gutter") { updateLineGutter(cm, lineView, lineN, dims); }
2082
- else if (type == "class") { updateLineClasses(cm, lineView); }
2083
- else if (type == "widget") { updateLineWidgets(cm, lineView, dims); }
2084
- }
2085
- lineView.changes = null;
2086
- }
2087
-
2088
- // Lines with gutter elements, widgets or a background class need to
2089
- // be wrapped, and have the extra elements added to the wrapper div
2090
- function ensureLineWrapped(lineView) {
2091
- if (lineView.node == lineView.text) {
2092
- lineView.node = elt("div", null, null, "position: relative");
2093
- if (lineView.text.parentNode)
2094
- { lineView.text.parentNode.replaceChild(lineView.node, lineView.text); }
2095
- lineView.node.appendChild(lineView.text);
2096
- if (ie && ie_version < 8) { lineView.node.style.zIndex = 2; }
2097
- }
2098
- return lineView.node
2099
- }
2100
-
2101
- function updateLineBackground(cm, lineView) {
2102
- var cls = lineView.bgClass ? lineView.bgClass + " " + (lineView.line.bgClass || "") : lineView.line.bgClass;
2103
- if (cls) { cls += " CodeMirror-linebackground"; }
2104
- if (lineView.background) {
2105
- if (cls) { lineView.background.className = cls; }
2106
- else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null; }
2107
- } else if (cls) {
2108
- var wrap = ensureLineWrapped(lineView);
2109
- lineView.background = wrap.insertBefore(elt("div", null, cls), wrap.firstChild);
2110
- cm.display.input.setUneditable(lineView.background);
2111
- }
2112
- }
2113
-
2114
- // Wrapper around buildLineContent which will reuse the structure
2115
- // in display.externalMeasured when possible.
2116
- function getLineContent(cm, lineView) {
2117
- var ext = cm.display.externalMeasured;
2118
- if (ext && ext.line == lineView.line) {
2119
- cm.display.externalMeasured = null;
2120
- lineView.measure = ext.measure;
2121
- return ext.built
2122
- }
2123
- return buildLineContent(cm, lineView)
2124
- }
2125
-
2126
- // Redraw the line's text. Interacts with the background and text
2127
- // classes because the mode may output tokens that influence these
2128
- // classes.
2129
- function updateLineText(cm, lineView) {
2130
- var cls = lineView.text.className;
2131
- var built = getLineContent(cm, lineView);
2132
- if (lineView.text == lineView.node) { lineView.node = built.pre; }
2133
- lineView.text.parentNode.replaceChild(built.pre, lineView.text);
2134
- lineView.text = built.pre;
2135
- if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) {
2136
- lineView.bgClass = built.bgClass;
2137
- lineView.textClass = built.textClass;
2138
- updateLineClasses(cm, lineView);
2139
- } else if (cls) {
2140
- lineView.text.className = cls;
2141
- }
2142
- }
2143
-
2144
- function updateLineClasses(cm, lineView) {
2145
- updateLineBackground(cm, lineView);
2146
- if (lineView.line.wrapClass)
2147
- { ensureLineWrapped(lineView).className = lineView.line.wrapClass; }
2148
- else if (lineView.node != lineView.text)
2149
- { lineView.node.className = ""; }
2150
- var textClass = lineView.textClass ? lineView.textClass + " " + (lineView.line.textClass || "") : lineView.line.textClass;
2151
- lineView.text.className = textClass || "";
2152
- }
2153
-
2154
- function updateLineGutter(cm, lineView, lineN, dims) {
2155
- if (lineView.gutter) {
2156
- lineView.node.removeChild(lineView.gutter);
2157
- lineView.gutter = null;
2158
- }
2159
- if (lineView.gutterBackground) {
2160
- lineView.node.removeChild(lineView.gutterBackground);
2161
- lineView.gutterBackground = null;
2162
- }
2163
- if (lineView.line.gutterClass) {
2164
- var wrap = ensureLineWrapped(lineView);
2165
- lineView.gutterBackground = elt("div", null, "CodeMirror-gutter-background " + lineView.line.gutterClass,
2166
- ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px; width: " + (dims.gutterTotalWidth) + "px"));
2167
- cm.display.input.setUneditable(lineView.gutterBackground);
2168
- wrap.insertBefore(lineView.gutterBackground, lineView.text);
2169
- }
2170
- var markers = lineView.line.gutterMarkers;
2171
- if (cm.options.lineNumbers || markers) {
2172
- var wrap$1 = ensureLineWrapped(lineView);
2173
- var gutterWrap = lineView.gutter = elt("div", null, "CodeMirror-gutter-wrapper", ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px"));
2174
- cm.display.input.setUneditable(gutterWrap);
2175
- wrap$1.insertBefore(gutterWrap, lineView.text);
2176
- if (lineView.line.gutterClass)
2177
- { gutterWrap.className += " " + lineView.line.gutterClass; }
2178
- if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"]))
2179
- { lineView.lineNumber = gutterWrap.appendChild(
2180
- elt("div", lineNumberFor(cm.options, lineN),
2181
- "CodeMirror-linenumber CodeMirror-gutter-elt",
2182
- ("left: " + (dims.gutterLeft["CodeMirror-linenumbers"]) + "px; width: " + (cm.display.lineNumInnerWidth) + "px"))); }
2183
- if (markers) { for (var k = 0; k < cm.display.gutterSpecs.length; ++k) {
2184
- var id = cm.display.gutterSpecs[k].className, found = markers.hasOwnProperty(id) && markers[id];
2185
- if (found)
2186
- { gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt",
2187
- ("left: " + (dims.gutterLeft[id]) + "px; width: " + (dims.gutterWidth[id]) + "px"))); }
2188
- } }
2189
- }
2190
- }
2191
-
2192
- function updateLineWidgets(cm, lineView, dims) {
2193
- if (lineView.alignable) { lineView.alignable = null; }
2194
- for (var node = lineView.node.firstChild, next = (void 0); node; node = next) {
2195
- next = node.nextSibling;
2196
- if (node.className == "CodeMirror-linewidget")
2197
- { lineView.node.removeChild(node); }
2198
- }
2199
- insertLineWidgets(cm, lineView, dims);
2200
- }
2201
-
2202
- // Build a line's DOM representation from scratch
2203
- function buildLineElement(cm, lineView, lineN, dims) {
2204
- var built = getLineContent(cm, lineView);
2205
- lineView.text = lineView.node = built.pre;
2206
- if (built.bgClass) { lineView.bgClass = built.bgClass; }
2207
- if (built.textClass) { lineView.textClass = built.textClass; }
2208
-
2209
- updateLineClasses(cm, lineView);
2210
- updateLineGutter(cm, lineView, lineN, dims);
2211
- insertLineWidgets(cm, lineView, dims);
2212
- return lineView.node
2213
- }
2214
-
2215
- // A lineView may contain multiple logical lines (when merged by
2216
- // collapsed spans). The widgets for all of them need to be drawn.
2217
- function insertLineWidgets(cm, lineView, dims) {
2218
- insertLineWidgetsFor(cm, lineView.line, lineView, dims, true);
2219
- if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++)
2220
- { insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false); } }
2221
- }
2222
-
2223
- function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) {
2224
- if (!line.widgets) { return }
2225
- var wrap = ensureLineWrapped(lineView);
2226
- for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
2227
- var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget");
2228
- if (!widget.handleMouseEvents) { node.setAttribute("cm-ignore-events", "true"); }
2229
- positionLineWidget(widget, node, lineView, dims);
2230
- cm.display.input.setUneditable(node);
2231
- if (allowAbove && widget.above)
2232
- { wrap.insertBefore(node, lineView.gutter || lineView.text); }
2233
- else
2234
- { wrap.appendChild(node); }
2235
- signalLater(widget, "redraw");
2236
- }
2237
- }
2238
-
2239
- function positionLineWidget(widget, node, lineView, dims) {
2240
- if (widget.noHScroll) {
2241
- (lineView.alignable || (lineView.alignable = [])).push(node);
2242
- var width = dims.wrapperWidth;
2243
- node.style.left = dims.fixedPos + "px";
2244
- if (!widget.coverGutter) {
2245
- width -= dims.gutterTotalWidth;
2246
- node.style.paddingLeft = dims.gutterTotalWidth + "px";
2247
- }
2248
- node.style.width = width + "px";
2249
- }
2250
- if (widget.coverGutter) {
2251
- node.style.zIndex = 5;
2252
- node.style.position = "relative";
2253
- if (!widget.noHScroll) { node.style.marginLeft = -dims.gutterTotalWidth + "px"; }
2254
- }
2255
- }
2256
-
2257
- function widgetHeight(widget) {
2258
- if (widget.height != null) { return widget.height }
2259
- var cm = widget.doc.cm;
2260
- if (!cm) { return 0 }
2261
- if (!contains(document.body, widget.node)) {
2262
- var parentStyle = "position: relative;";
2263
- if (widget.coverGutter)
2264
- { parentStyle += "margin-left: -" + cm.display.gutters.offsetWidth + "px;"; }
2265
- if (widget.noHScroll)
2266
- { parentStyle += "width: " + cm.display.wrapper.clientWidth + "px;"; }
2267
- removeChildrenAndAdd(cm.display.measure, elt("div", [widget.node], null, parentStyle));
2268
- }
2269
- return widget.height = widget.node.parentNode.offsetHeight
2270
- }
2271
-
2272
- // Return true when the given mouse event happened in a widget
2273
- function eventInWidget(display, e) {
2274
- for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {
2275
- if (!n || (n.nodeType == 1 && n.getAttribute("cm-ignore-events") == "true") ||
2276
- (n.parentNode == display.sizer && n != display.mover))
2277
- { return true }
2278
- }
2279
- }
2280
-
2281
- // POSITION MEASUREMENT
2282
-
2283
- function paddingTop(display) {return display.lineSpace.offsetTop}
2284
- function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight}
2285
- function paddingH(display) {
2286
- if (display.cachedPaddingH) { return display.cachedPaddingH }
2287
- var e = removeChildrenAndAdd(display.measure, elt("pre", "x"));
2288
- var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle;
2289
- var data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)};
2290
- if (!isNaN(data.left) && !isNaN(data.right)) { display.cachedPaddingH = data; }
2291
- return data
2292
- }
2293
-
2294
- function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth }
2295
- function displayWidth(cm) {
2296
- return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth
2297
- }
2298
- function displayHeight(cm) {
2299
- return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeight
2300
- }
2301
-
2302
- // Ensure the lineView.wrapping.heights array is populated. This is
2303
- // an array of bottom offsets for the lines that make up a drawn
2304
- // line. When lineWrapping is on, there might be more than one
2305
- // height.
2306
- function ensureLineHeights(cm, lineView, rect) {
2307
- var wrapping = cm.options.lineWrapping;
2308
- var curWidth = wrapping && displayWidth(cm);
2309
- if (!lineView.measure.heights || wrapping && lineView.measure.width != curWidth) {
2310
- var heights = lineView.measure.heights = [];
2311
- if (wrapping) {
2312
- lineView.measure.width = curWidth;
2313
- var rects = lineView.text.firstChild.getClientRects();
2314
- for (var i = 0; i < rects.length - 1; i++) {
2315
- var cur = rects[i], next = rects[i + 1];
2316
- if (Math.abs(cur.bottom - next.bottom) > 2)
2317
- { heights.push((cur.bottom + next.top) / 2 - rect.top); }
2318
- }
2319
- }
2320
- heights.push(rect.bottom - rect.top);
2321
- }
2322
- }
2323
-
2324
- // Find a line map (mapping character offsets to text nodes) and a
2325
- // measurement cache for the given line number. (A line view might
2326
- // contain multiple lines when collapsed ranges are present.)
2327
- function mapFromLineView(lineView, line, lineN) {
2328
- if (lineView.line == line)
2329
- { return {map: lineView.measure.map, cache: lineView.measure.cache} }
2330
- for (var i = 0; i < lineView.rest.length; i++)
2331
- { if (lineView.rest[i] == line)
2332
- { return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]} } }
2333
- for (var i$1 = 0; i$1 < lineView.rest.length; i$1++)
2334
- { if (lineNo(lineView.rest[i$1]) > lineN)
2335
- { return {map: lineView.measure.maps[i$1], cache: lineView.measure.caches[i$1], before: true} } }
2336
- }
2337
-
2338
- // Render a line into the hidden node display.externalMeasured. Used
2339
- // when measurement is needed for a line that's not in the viewport.
2340
- function updateExternalMeasurement(cm, line) {
2341
- line = visualLine(line);
2342
- var lineN = lineNo(line);
2343
- var view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN);
2344
- view.lineN = lineN;
2345
- var built = view.built = buildLineContent(cm, view);
2346
- view.text = built.pre;
2347
- removeChildrenAndAdd(cm.display.lineMeasure, built.pre);
2348
- return view
2349
- }
2350
-
2351
- // Get a {top, bottom, left, right} box (in line-local coordinates)
2352
- // for a given character.
2353
- function measureChar(cm, line, ch, bias) {
2354
- return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias)
2355
- }
2356
-
2357
- // Find a line view that corresponds to the given line number.
2358
- function findViewForLine(cm, lineN) {
2359
- if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo)
2360
- { return cm.display.view[findViewIndex(cm, lineN)] }
2361
- var ext = cm.display.externalMeasured;
2362
- if (ext && lineN >= ext.lineN && lineN < ext.lineN + ext.size)
2363
- { return ext }
2364
- }
2365
-
2366
- // Measurement can be split in two steps, the set-up work that
2367
- // applies to the whole line, and the measurement of the actual
2368
- // character. Functions like coordsChar, that need to do a lot of
2369
- // measurements in a row, can thus ensure that the set-up work is
2370
- // only done once.
2371
- function prepareMeasureForLine(cm, line) {
2372
- var lineN = lineNo(line);
2373
- var view = findViewForLine(cm, lineN);
2374
- if (view && !view.text) {
2375
- view = null;
2376
- } else if (view && view.changes) {
2377
- updateLineForChanges(cm, view, lineN, getDimensions(cm));
2378
- cm.curOp.forceUpdate = true;
2379
- }
2380
- if (!view)
2381
- { view = updateExternalMeasurement(cm, line); }
2382
-
2383
- var info = mapFromLineView(view, line, lineN);
2384
- return {
2385
- line: line, view: view, rect: null,
2386
- map: info.map, cache: info.cache, before: info.before,
2387
- hasHeights: false
2388
- }
2389
- }
2390
-
2391
- // Given a prepared measurement object, measures the position of an
2392
- // actual character (or fetches it from the cache).
2393
- function measureCharPrepared(cm, prepared, ch, bias, varHeight) {
2394
- if (prepared.before) { ch = -1; }
2395
- var key = ch + (bias || ""), found;
2396
- if (prepared.cache.hasOwnProperty(key)) {
2397
- found = prepared.cache[key];
2398
- } else {
2399
- if (!prepared.rect)
2400
- { prepared.rect = prepared.view.text.getBoundingClientRect(); }
2401
- if (!prepared.hasHeights) {
2402
- ensureLineHeights(cm, prepared.view, prepared.rect);
2403
- prepared.hasHeights = true;
2404
- }
2405
- found = measureCharInner(cm, prepared, ch, bias);
2406
- if (!found.bogus) { prepared.cache[key] = found; }
2407
- }
2408
- return {left: found.left, right: found.right,
2409
- top: varHeight ? found.rtop : found.top,
2410
- bottom: varHeight ? found.rbottom : found.bottom}
2411
- }
2412
-
2413
- var nullRect = {left: 0, right: 0, top: 0, bottom: 0};
2414
-
2415
- function nodeAndOffsetInLineMap(map$$1, ch, bias) {
2416
- var node, start, end, collapse, mStart, mEnd;
2417
- // First, search the line map for the text node corresponding to,
2418
- // or closest to, the target character.
2419
- for (var i = 0; i < map$$1.length; i += 3) {
2420
- mStart = map$$1[i];
2421
- mEnd = map$$1[i + 1];
2422
- if (ch < mStart) {
2423
- start = 0; end = 1;
2424
- collapse = "left";
2425
- } else if (ch < mEnd) {
2426
- start = ch - mStart;
2427
- end = start + 1;
2428
- } else if (i == map$$1.length - 3 || ch == mEnd && map$$1[i + 3] > ch) {
2429
- end = mEnd - mStart;
2430
- start = end - 1;
2431
- if (ch >= mEnd) { collapse = "right"; }
2432
- }
2433
- if (start != null) {
2434
- node = map$$1[i + 2];
2435
- if (mStart == mEnd && bias == (node.insertLeft ? "left" : "right"))
2436
- { collapse = bias; }
2437
- if (bias == "left" && start == 0)
2438
- { while (i && map$$1[i - 2] == map$$1[i - 3] && map$$1[i - 1].insertLeft) {
2439
- node = map$$1[(i -= 3) + 2];
2440
- collapse = "left";
2441
- } }
2442
- if (bias == "right" && start == mEnd - mStart)
2443
- { while (i < map$$1.length - 3 && map$$1[i + 3] == map$$1[i + 4] && !map$$1[i + 5].insertLeft) {
2444
- node = map$$1[(i += 3) + 2];
2445
- collapse = "right";
2446
- } }
2447
- break
2448
- }
2449
- }
2450
- return {node: node, start: start, end: end, collapse: collapse, coverStart: mStart, coverEnd: mEnd}
2451
- }
2452
-
2453
- function getUsefulRect(rects, bias) {
2454
- var rect = nullRect;
2455
- if (bias == "left") { for (var i = 0; i < rects.length; i++) {
2456
- if ((rect = rects[i]).left != rect.right) { break }
2457
- } } else { for (var i$1 = rects.length - 1; i$1 >= 0; i$1--) {
2458
- if ((rect = rects[i$1]).left != rect.right) { break }
2459
- } }
2460
- return rect
2461
- }
2462
-
2463
- function measureCharInner(cm, prepared, ch, bias) {
2464
- var place = nodeAndOffsetInLineMap(prepared.map, ch, bias);
2465
- var node = place.node, start = place.start, end = place.end, collapse = place.collapse;
2466
-
2467
- var rect;
2468
- if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates.
2469
- for (var i$1 = 0; i$1 < 4; i$1++) { // Retry a maximum of 4 times when nonsense rectangles are returned
2470
- while (start && isExtendingChar(prepared.line.text.charAt(place.coverStart + start))) { --start; }
2471
- while (place.coverStart + end < place.coverEnd && isExtendingChar(prepared.line.text.charAt(place.coverStart + end))) { ++end; }
2472
- if (ie && ie_version < 9 && start == 0 && end == place.coverEnd - place.coverStart)
2473
- { rect = node.parentNode.getBoundingClientRect(); }
2474
- else
2475
- { rect = getUsefulRect(range(node, start, end).getClientRects(), bias); }
2476
- if (rect.left || rect.right || start == 0) { break }
2477
- end = start;
2478
- start = start - 1;
2479
- collapse = "right";
2480
- }
2481
- if (ie && ie_version < 11) { rect = maybeUpdateRectForZooming(cm.display.measure, rect); }
2482
- } else { // If it is a widget, simply get the box for the whole widget.
2483
- if (start > 0) { collapse = bias = "right"; }
2484
- var rects;
2485
- if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1)
2486
- { rect = rects[bias == "right" ? rects.length - 1 : 0]; }
2487
- else
2488
- { rect = node.getBoundingClientRect(); }
2489
- }
2490
- if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) {
2491
- var rSpan = node.parentNode.getClientRects()[0];
2492
- if (rSpan)
2493
- { rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top: rSpan.top, bottom: rSpan.bottom}; }
2494
- else
2495
- { rect = nullRect; }
2496
- }
2497
-
2498
- var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top;
2499
- var mid = (rtop + rbot) / 2;
2500
- var heights = prepared.view.measure.heights;
2501
- var i = 0;
2502
- for (; i < heights.length - 1; i++)
2503
- { if (mid < heights[i]) { break } }
2504
- var top = i ? heights[i - 1] : 0, bot = heights[i];
2505
- var result = {left: (collapse == "right" ? rect.right : rect.left) - prepared.rect.left,
2506
- right: (collapse == "left" ? rect.left : rect.right) - prepared.rect.left,
2507
- top: top, bottom: bot};
2508
- if (!rect.left && !rect.right) { result.bogus = true; }
2509
- if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbottom = rbot; }
2510
-
2511
- return result
2512
- }
2513
-
2514
- // Work around problem with bounding client rects on ranges being
2515
- // returned incorrectly when zoomed on IE10 and below.
2516
- function maybeUpdateRectForZooming(measure, rect) {
2517
- if (!window.screen || screen.logicalXDPI == null ||
2518
- screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure))
2519
- { return rect }
2520
- var scaleX = screen.logicalXDPI / screen.deviceXDPI;
2521
- var scaleY = screen.logicalYDPI / screen.deviceYDPI;
2522
- return {left: rect.left * scaleX, right: rect.right * scaleX,
2523
- top: rect.top * scaleY, bottom: rect.bottom * scaleY}
2524
- }
2525
-
2526
- function clearLineMeasurementCacheFor(lineView) {
2527
- if (lineView.measure) {
2528
- lineView.measure.cache = {};
2529
- lineView.measure.heights = null;
2530
- if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++)
2531
- { lineView.measure.caches[i] = {}; } }
2532
- }
2533
- }
2534
-
2535
- function clearLineMeasurementCache(cm) {
2536
- cm.display.externalMeasure = null;
2537
- removeChildren(cm.display.lineMeasure);
2538
- for (var i = 0; i < cm.display.view.length; i++)
2539
- { clearLineMeasurementCacheFor(cm.display.view[i]); }
2540
- }
2541
-
2542
- function clearCaches(cm) {
2543
- clearLineMeasurementCache(cm);
2544
- cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cachedPaddingH = null;
2545
- if (!cm.options.lineWrapping) { cm.display.maxLineChanged = true; }
2546
- cm.display.lineNumChars = null;
2547
- }
2548
-
2549
- function pageScrollX() {
2550
- // Work around https://bugs.chromium.org/p/chromium/issues/detail?id=489206
2551
- // which causes page_Offset and bounding client rects to use
2552
- // different reference viewports and invalidate our calculations.
2553
- if (chrome && android) { return -(document.body.getBoundingClientRect().left - parseInt(getComputedStyle(document.body).marginLeft)) }
2554
- return window.pageXOffset || (document.documentElement || document.body).scrollLeft
2555
- }
2556
- function pageScrollY() {
2557
- if (chrome && android) { return -(document.body.getBoundingClientRect().top - parseInt(getComputedStyle(document.body).marginTop)) }
2558
- return window.pageYOffset || (document.documentElement || document.body).scrollTop
2559
- }
2560
-
2561
- function widgetTopHeight(lineObj) {
2562
- var height = 0;
2563
- if (lineObj.widgets) { for (var i = 0; i < lineObj.widgets.length; ++i) { if (lineObj.widgets[i].above)
2564
- { height += widgetHeight(lineObj.widgets[i]); } } }
2565
- return height
2566
- }
2567
-
2568
- // Converts a {top, bottom, left, right} box from line-local
2569
- // coordinates into another coordinate system. Context may be one of
2570
- // "line", "div" (display.lineDiv), "local"./null (editor), "window",
2571
- // or "page".
2572
- function intoCoordSystem(cm, lineObj, rect, context, includeWidgets) {
2573
- if (!includeWidgets) {
2574
- var height = widgetTopHeight(lineObj);
2575
- rect.top += height; rect.bottom += height;
2576
- }
2577
- if (context == "line") { return rect }
2578
- if (!context) { context = "local"; }
2579
- var yOff = heightAtLine(lineObj);
2580
- if (context == "local") { yOff += paddingTop(cm.display); }
2581
- else { yOff -= cm.display.viewOffset; }
2582
- if (context == "page" || context == "window") {
2583
- var lOff = cm.display.lineSpace.getBoundingClientRect();
2584
- yOff += lOff.top + (context == "window" ? 0 : pageScrollY());
2585
- var xOff = lOff.left + (context == "window" ? 0 : pageScrollX());
2586
- rect.left += xOff; rect.right += xOff;
2587
- }
2588
- rect.top += yOff; rect.bottom += yOff;
2589
- return rect
2590
- }
2591
-
2592
- // Coverts a box from "div" coords to another coordinate system.
2593
- // Context may be "window", "page", "div", or "local"./null.
2594
- function fromCoordSystem(cm, coords, context) {
2595
- if (context == "div") { return coords }
2596
- var left = coords.left, top = coords.top;
2597
- // First move into "page" coordinate system
2598
- if (context == "page") {
2599
- left -= pageScrollX();
2600
- top -= pageScrollY();
2601
- } else if (context == "local" || !context) {
2602
- var localBox = cm.display.sizer.getBoundingClientRect();
2603
- left += localBox.left;
2604
- top += localBox.top;
2605
- }
2606
-
2607
- var lineSpaceBox = cm.display.lineSpace.getBoundingClientRect();
2608
- return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top}
2609
- }
2610
-
2611
- function charCoords(cm, pos, context, lineObj, bias) {
2612
- if (!lineObj) { lineObj = getLine(cm.doc, pos.line); }
2613
- return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, bias), context)
2614
- }
2615
-
2616
- // Returns a box for a given cursor position, which may have an
2617
- // 'other' property containing the position of the secondary cursor
2618
- // on a bidi boundary.
2619
- // A cursor Pos(line, char, "before") is on the same visual line as `char - 1`
2620
- // and after `char - 1` in writing order of `char - 1`
2621
- // A cursor Pos(line, char, "after") is on the same visual line as `char`
2622
- // and before `char` in writing order of `char`
2623
- // Examples (upper-case letters are RTL, lower-case are LTR):
2624
- // Pos(0, 1, ...)
2625
- // before after
2626
- // ab a|b a|b
2627
- // aB a|B aB|
2628
- // Ab |Ab A|b
2629
- // AB B|A B|A
2630
- // Every position after the last character on a line is considered to stick
2631
- // to the last character on the line.
2632
- function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) {
2633
- lineObj = lineObj || getLine(cm.doc, pos.line);
2634
- if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj); }
2635
- function get(ch, right) {
2636
- var m = measureCharPrepared(cm, preparedMeasure, ch, right ? "right" : "left", varHeight);
2637
- if (right) { m.left = m.right; } else { m.right = m.left; }
2638
- return intoCoordSystem(cm, lineObj, m, context)
2639
- }
2640
- var order = getOrder(lineObj, cm.doc.direction), ch = pos.ch, sticky = pos.sticky;
2641
- if (ch >= lineObj.text.length) {
2642
- ch = lineObj.text.length;
2643
- sticky = "before";
2644
- } else if (ch <= 0) {
2645
- ch = 0;
2646
- sticky = "after";
2647
- }
2648
- if (!order) { return get(sticky == "before" ? ch - 1 : ch, sticky == "before") }
2649
-
2650
- function getBidi(ch, partPos, invert) {
2651
- var part = order[partPos], right = part.level == 1;
2652
- return get(invert ? ch - 1 : ch, right != invert)
2653
- }
2654
- var partPos = getBidiPartAt(order, ch, sticky);
2655
- var other = bidiOther;
2656
- var val = getBidi(ch, partPos, sticky == "before");
2657
- if (other != null) { val.other = getBidi(ch, other, sticky != "before"); }
2658
- return val
2659
- }
2660
-
2661
- // Used to cheaply estimate the coordinates for a position. Used for
2662
- // intermediate scroll updates.
2663
- function estimateCoords(cm, pos) {
2664
- var left = 0;
2665
- pos = clipPos(cm.doc, pos);
2666
- if (!cm.options.lineWrapping) { left = charWidth(cm.display) * pos.ch; }
2667
- var lineObj = getLine(cm.doc, pos.line);
2668
- var top = heightAtLine(lineObj) + paddingTop(cm.display);
2669
- return {left: left, right: left, top: top, bottom: top + lineObj.height}
2670
- }
2671
-
2672
- // Positions returned by coordsChar contain some extra information.
2673
- // xRel is the relative x position of the input coordinates compared
2674
- // to the found position (so xRel > 0 means the coordinates are to
2675
- // the right of the character position, for example). When outside
2676
- // is true, that means the coordinates lie outside the line's
2677
- // vertical range.
2678
- function PosWithInfo(line, ch, sticky, outside, xRel) {
2679
- var pos = Pos(line, ch, sticky);
2680
- pos.xRel = xRel;
2681
- if (outside) { pos.outside = true; }
2682
- return pos
2683
- }
2684
-
2685
- // Compute the character position closest to the given coordinates.
2686
- // Input must be lineSpace-local ("div" coordinate system).
2687
- function coordsChar(cm, x, y) {
2688
- var doc = cm.doc;
2689
- y += cm.display.viewOffset;
2690
- if (y < 0) { return PosWithInfo(doc.first, 0, null, true, -1) }
2691
- var lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1;
2692
- if (lineN > last)
2693
- { return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, null, true, 1) }
2694
- if (x < 0) { x = 0; }
2695
-
2696
- var lineObj = getLine(doc, lineN);
2697
- for (;;) {
2698
- var found = coordsCharInner(cm, lineObj, lineN, x, y);
2699
- var collapsed = collapsedSpanAround(lineObj, found.ch + (found.xRel > 0 ? 1 : 0));
2700
- if (!collapsed) { return found }
2701
- var rangeEnd = collapsed.find(1);
2702
- if (rangeEnd.line == lineN) { return rangeEnd }
2703
- lineObj = getLine(doc, lineN = rangeEnd.line);
2704
- }
2705
- }
2706
-
2707
- function wrappedLineExtent(cm, lineObj, preparedMeasure, y) {
2708
- y -= widgetTopHeight(lineObj);
2709
- var end = lineObj.text.length;
2710
- var begin = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch - 1).bottom <= y; }, end, 0);
2711
- end = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch).top > y; }, begin, end);
2712
- return {begin: begin, end: end}
2713
- }
2714
-
2715
- function wrappedLineExtentChar(cm, lineObj, preparedMeasure, target) {
2716
- if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj); }
2717
- var targetTop = intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, target), "line").top;
2718
- return wrappedLineExtent(cm, lineObj, preparedMeasure, targetTop)
2719
- }
2720
-
2721
- // Returns true if the given side of a box is after the given
2722
- // coordinates, in top-to-bottom, left-to-right order.
2723
- function boxIsAfter(box, x, y, left) {
2724
- return box.bottom <= y ? false : box.top > y ? true : (left ? box.left : box.right) > x
2725
- }
2726
-
2727
- function coordsCharInner(cm, lineObj, lineNo$$1, x, y) {
2728
- // Move y into line-local coordinate space
2729
- y -= heightAtLine(lineObj);
2730
- var preparedMeasure = prepareMeasureForLine(cm, lineObj);
2731
- // When directly calling `measureCharPrepared`, we have to adjust
2732
- // for the widgets at this line.
2733
- var widgetHeight$$1 = widgetTopHeight(lineObj);
2734
- var begin = 0, end = lineObj.text.length, ltr = true;
2735
-
2736
- var order = getOrder(lineObj, cm.doc.direction);
2737
- // If the line isn't plain left-to-right text, first figure out
2738
- // which bidi section the coordinates fall into.
2739
- if (order) {
2740
- var part = (cm.options.lineWrapping ? coordsBidiPartWrapped : coordsBidiPart)
2741
- (cm, lineObj, lineNo$$1, preparedMeasure, order, x, y);
2742
- ltr = part.level != 1;
2743
- // The awkward -1 offsets are needed because findFirst (called
2744
- // on these below) will treat its first bound as inclusive,
2745
- // second as exclusive, but we want to actually address the
2746
- // characters in the part's range
2747
- begin = ltr ? part.from : part.to - 1;
2748
- end = ltr ? part.to : part.from - 1;
2749
- }
2750
-
2751
- // A binary search to find the first character whose bounding box
2752
- // starts after the coordinates. If we run across any whose box wrap
2753
- // the coordinates, store that.
2754
- var chAround = null, boxAround = null;
2755
- var ch = findFirst(function (ch) {
2756
- var box = measureCharPrepared(cm, preparedMeasure, ch);
2757
- box.top += widgetHeight$$1; box.bottom += widgetHeight$$1;
2758
- if (!boxIsAfter(box, x, y, false)) { return false }
2759
- if (box.top <= y && box.left <= x) {
2760
- chAround = ch;
2761
- boxAround = box;
2762
- }
2763
- return true
2764
- }, begin, end);
2765
-
2766
- var baseX, sticky, outside = false;
2767
- // If a box around the coordinates was found, use that
2768
- if (boxAround) {
2769
- // Distinguish coordinates nearer to the left or right side of the box
2770
- var atLeft = x - boxAround.left < boxAround.right - x, atStart = atLeft == ltr;
2771
- ch = chAround + (atStart ? 0 : 1);
2772
- sticky = atStart ? "after" : "before";
2773
- baseX = atLeft ? boxAround.left : boxAround.right;
2774
- } else {
2775
- // (Adjust for extended bound, if necessary.)
2776
- if (!ltr && (ch == end || ch == begin)) { ch++; }
2777
- // To determine which side to associate with, get the box to the
2778
- // left of the character and compare it's vertical position to the
2779
- // coordinates
2780
- sticky = ch == 0 ? "after" : ch == lineObj.text.length ? "before" :
2781
- (measureCharPrepared(cm, preparedMeasure, ch - (ltr ? 1 : 0)).bottom + widgetHeight$$1 <= y) == ltr ?
2782
- "after" : "before";
2783
- // Now get accurate coordinates for this place, in order to get a
2784
- // base X position
2785
- var coords = cursorCoords(cm, Pos(lineNo$$1, ch, sticky), "line", lineObj, preparedMeasure);
2786
- baseX = coords.left;
2787
- outside = y < coords.top || y >= coords.bottom;
2788
- }
2789
-
2790
- ch = skipExtendingChars(lineObj.text, ch, 1);
2791
- return PosWithInfo(lineNo$$1, ch, sticky, outside, x - baseX)
2792
- }
2793
-
2794
- function coordsBidiPart(cm, lineObj, lineNo$$1, preparedMeasure, order, x, y) {
2795
- // Bidi parts are sorted left-to-right, and in a non-line-wrapping
2796
- // situation, we can take this ordering to correspond to the visual
2797
- // ordering. This finds the first part whose end is after the given
2798
- // coordinates.
2799
- var index = findFirst(function (i) {
2800
- var part = order[i], ltr = part.level != 1;
2801
- return boxIsAfter(cursorCoords(cm, Pos(lineNo$$1, ltr ? part.to : part.from, ltr ? "before" : "after"),
2802
- "line", lineObj, preparedMeasure), x, y, true)
2803
- }, 0, order.length - 1);
2804
- var part = order[index];
2805
- // If this isn't the first part, the part's start is also after
2806
- // the coordinates, and the coordinates aren't on the same line as
2807
- // that start, move one part back.
2808
- if (index > 0) {
2809
- var ltr = part.level != 1;
2810
- var start = cursorCoords(cm, Pos(lineNo$$1, ltr ? part.from : part.to, ltr ? "after" : "before"),
2811
- "line", lineObj, preparedMeasure);
2812
- if (boxIsAfter(start, x, y, true) && start.top > y)
2813
- { part = order[index - 1]; }
2814
- }
2815
- return part
2816
- }
2817
-
2818
- function coordsBidiPartWrapped(cm, lineObj, _lineNo, preparedMeasure, order, x, y) {
2819
- // In a wrapped line, rtl text on wrapping boundaries can do things
2820
- // that don't correspond to the ordering in our `order` array at
2821
- // all, so a binary search doesn't work, and we want to return a
2822
- // part that only spans one line so that the binary search in
2823
- // coordsCharInner is safe. As such, we first find the extent of the
2824
- // wrapped line, and then do a flat search in which we discard any
2825
- // spans that aren't on the line.
2826
- var ref = wrappedLineExtent(cm, lineObj, preparedMeasure, y);
2827
- var begin = ref.begin;
2828
- var end = ref.end;
2829
- if (/\s/.test(lineObj.text.charAt(end - 1))) { end--; }
2830
- var part = null, closestDist = null;
2831
- for (var i = 0; i < order.length; i++) {
2832
- var p = order[i];
2833
- if (p.from >= end || p.to <= begin) { continue }
2834
- var ltr = p.level != 1;
2835
- var endX = measureCharPrepared(cm, preparedMeasure, ltr ? Math.min(end, p.to) - 1 : Math.max(begin, p.from)).right;
2836
- // Weigh against spans ending before this, so that they are only
2837
- // picked if nothing ends after
2838
- var dist = endX < x ? x - endX + 1e9 : endX - x;
2839
- if (!part || closestDist > dist) {
2840
- part = p;
2841
- closestDist = dist;
2842
- }
2843
- }
2844
- if (!part) { part = order[order.length - 1]; }
2845
- // Clip the part to the wrapped line.
2846
- if (part.from < begin) { part = {from: begin, to: part.to, level: part.level}; }
2847
- if (part.to > end) { part = {from: part.from, to: end, level: part.level}; }
2848
- return part
2849
- }
2850
-
2851
- var measureText;
2852
- // Compute the default text height.
2853
- function textHeight(display) {
2854
- if (display.cachedTextHeight != null) { return display.cachedTextHeight }
2855
- if (measureText == null) {
2856
- measureText = elt("pre");
2857
- // Measure a bunch of lines, for browsers that compute
2858
- // fractional heights.
2859
- for (var i = 0; i < 49; ++i) {
2860
- measureText.appendChild(document.createTextNode("x"));
2861
- measureText.appendChild(elt("br"));
2862
- }
2863
- measureText.appendChild(document.createTextNode("x"));
2864
- }
2865
- removeChildrenAndAdd(display.measure, measureText);
2866
- var height = measureText.offsetHeight / 50;
2867
- if (height > 3) { display.cachedTextHeight = height; }
2868
- removeChildren(display.measure);
2869
- return height || 1
2870
- }
2871
-
2872
- // Compute the default character width.
2873
- function charWidth(display) {
2874
- if (display.cachedCharWidth != null) { return display.cachedCharWidth }
2875
- var anchor = elt("span", "xxxxxxxxxx");
2876
- var pre = elt("pre", [anchor]);
2877
- removeChildrenAndAdd(display.measure, pre);
2878
- var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10;
2879
- if (width > 2) { display.cachedCharWidth = width; }
2880
- return width || 10
2881
- }
2882
-
2883
- // Do a bulk-read of the DOM positions and sizes needed to draw the
2884
- // view, so that we don't interleave reading and writing to the DOM.
2885
- function getDimensions(cm) {
2886
- var d = cm.display, left = {}, width = {};
2887
- var gutterLeft = d.gutters.clientLeft;
2888
- for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) {
2889
- var id = cm.display.gutterSpecs[i].className;
2890
- left[id] = n.offsetLeft + n.clientLeft + gutterLeft;
2891
- width[id] = n.clientWidth;
2892
- }
2893
- return {fixedPos: compensateForHScroll(d),
2894
- gutterTotalWidth: d.gutters.offsetWidth,
2895
- gutterLeft: left,
2896
- gutterWidth: width,
2897
- wrapperWidth: d.wrapper.clientWidth}
2898
- }
2899
-
2900
- // Computes display.scroller.scrollLeft + display.gutters.offsetWidth,
2901
- // but using getBoundingClientRect to get a sub-pixel-accurate
2902
- // result.
2903
- function compensateForHScroll(display) {
2904
- return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left
2905
- }
2906
-
2907
- // Returns a function that estimates the height of a line, to use as
2908
- // first approximation until the line becomes visible (and is thus
2909
- // properly measurable).
2910
- function estimateHeight(cm) {
2911
- var th = textHeight(cm.display), wrapping = cm.options.lineWrapping;
2912
- var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3);
2913
- return function (line) {
2914
- if (lineIsHidden(cm.doc, line)) { return 0 }
2915
-
2916
- var widgetsHeight = 0;
2917
- if (line.widgets) { for (var i = 0; i < line.widgets.length; i++) {
2918
- if (line.widgets[i].height) { widgetsHeight += line.widgets[i].height; }
2919
- } }
2920
-
2921
- if (wrapping)
2922
- { return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th }
2923
- else
2924
- { return widgetsHeight + th }
2925
- }
2926
- }
2927
-
2928
- function estimateLineHeights(cm) {
2929
- var doc = cm.doc, est = estimateHeight(cm);
2930
- doc.iter(function (line) {
2931
- var estHeight = est(line);
2932
- if (estHeight != line.height) { updateLineHeight(line, estHeight); }
2933
- });
2934
- }
2935
-
2936
- // Given a mouse event, find the corresponding position. If liberal
2937
- // is false, it checks whether a gutter or scrollbar was clicked,
2938
- // and returns null if it was. forRect is used by rectangular
2939
- // selections, and tries to estimate a character position even for
2940
- // coordinates beyond the right of the text.
2941
- function posFromMouse(cm, e, liberal, forRect) {
2942
- var display = cm.display;
2943
- if (!liberal && e_target(e).getAttribute("cm-not-content") == "true") { return null }
2944
-
2945
- var x, y, space = display.lineSpace.getBoundingClientRect();
2946
- // Fails unpredictably on IE[67] when mouse is dragged around quickly.
2947
- try { x = e.clientX - space.left; y = e.clientY - space.top; }
2948
- catch (e) { return null }
2949
- var coords = coordsChar(cm, x, y), line;
2950
- if (forRect && coords.xRel == 1 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) {
2951
- var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length;
2952
- coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff));
2953
- }
2954
- return coords
2955
- }
2956
-
2957
- // Find the view element corresponding to a given line. Return null
2958
- // when the line isn't visible.
2959
- function findViewIndex(cm, n) {
2960
- if (n >= cm.display.viewTo) { return null }
2961
- n -= cm.display.viewFrom;
2962
- if (n < 0) { return null }
2963
- var view = cm.display.view;
2964
- for (var i = 0; i < view.length; i++) {
2965
- n -= view[i].size;
2966
- if (n < 0) { return i }
2967
- }
2968
- }
2969
-
2970
- // Updates the display.view data structure for a given change to the
2971
- // document. From and to are in pre-change coordinates. Lendiff is
2972
- // the amount of lines added or subtracted by the change. This is
2973
- // used for changes that span multiple lines, or change the way
2974
- // lines are divided into visual lines. regLineChange (below)
2975
- // registers single-line changes.
2976
- function regChange(cm, from, to, lendiff) {
2977
- if (from == null) { from = cm.doc.first; }
2978
- if (to == null) { to = cm.doc.first + cm.doc.size; }
2979
- if (!lendiff) { lendiff = 0; }
2980
-
2981
- var display = cm.display;
2982
- if (lendiff && to < display.viewTo &&
2983
- (display.updateLineNumbers == null || display.updateLineNumbers > from))
2984
- { display.updateLineNumbers = from; }
2985
-
2986
- cm.curOp.viewChanged = true;
2987
-
2988
- if (from >= display.viewTo) { // Change after
2989
- if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo)
2990
- { resetView(cm); }
2991
- } else if (to <= display.viewFrom) { // Change before
2992
- if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.viewFrom) {
2993
- resetView(cm);
2994
- } else {
2995
- display.viewFrom += lendiff;
2996
- display.viewTo += lendiff;
2997
- }
2998
- } else if (from <= display.viewFrom && to >= display.viewTo) { // Full overlap
2999
- resetView(cm);
3000
- } else if (from <= display.viewFrom) { // Top overlap
3001
- var cut = viewCuttingPoint(cm, to, to + lendiff, 1);
3002
- if (cut) {
3003
- display.view = display.view.slice(cut.index);
3004
- display.viewFrom = cut.lineN;
3005
- display.viewTo += lendiff;
3006
- } else {
3007
- resetView(cm);
3008
- }
3009
- } else if (to >= display.viewTo) { // Bottom overlap
3010
- var cut$1 = viewCuttingPoint(cm, from, from, -1);
3011
- if (cut$1) {
3012
- display.view = display.view.slice(0, cut$1.index);
3013
- display.viewTo = cut$1.lineN;
3014
- } else {
3015
- resetView(cm);
3016
- }
3017
- } else { // Gap in the middle
3018
- var cutTop = viewCuttingPoint(cm, from, from, -1);
3019
- var cutBot = viewCuttingPoint(cm, to, to + lendiff, 1);
3020
- if (cutTop && cutBot) {
3021
- display.view = display.view.slice(0, cutTop.index)
3022
- .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN))
3023
- .concat(display.view.slice(cutBot.index));
3024
- display.viewTo += lendiff;
3025
- } else {
3026
- resetView(cm);
3027
- }
3028
- }
3029
-
3030
- var ext = display.externalMeasured;
3031
- if (ext) {
3032
- if (to < ext.lineN)
3033
- { ext.lineN += lendiff; }
3034
- else if (from < ext.lineN + ext.size)
3035
- { display.externalMeasured = null; }
3036
- }
3037
- }
3038
-
3039
- // Register a change to a single line. Type must be one of "text",
3040
- // "gutter", "class", "widget"
3041
- function regLineChange(cm, line, type) {
3042
- cm.curOp.viewChanged = true;
3043
- var display = cm.display, ext = cm.display.externalMeasured;
3044
- if (ext && line >= ext.lineN && line < ext.lineN + ext.size)
3045
- { display.externalMeasured = null; }
3046
-
3047
- if (line < display.viewFrom || line >= display.viewTo) { return }
3048
- var lineView = display.view[findViewIndex(cm, line)];
3049
- if (lineView.node == null) { return }
3050
- var arr = lineView.changes || (lineView.changes = []);
3051
- if (indexOf(arr, type) == -1) { arr.push(type); }
3052
- }
3053
-
3054
- // Clear the view.
3055
- function resetView(cm) {
3056
- cm.display.viewFrom = cm.display.viewTo = cm.doc.first;
3057
- cm.display.view = [];
3058
- cm.display.viewOffset = 0;
3059
- }
3060
-
3061
- function viewCuttingPoint(cm, oldN, newN, dir) {
3062
- var index = findViewIndex(cm, oldN), diff, view = cm.display.view;
3063
- if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size)
3064
- { return {index: index, lineN: newN} }
3065
- var n = cm.display.viewFrom;
3066
- for (var i = 0; i < index; i++)
3067
- { n += view[i].size; }
3068
- if (n != oldN) {
3069
- if (dir > 0) {
3070
- if (index == view.length - 1) { return null }
3071
- diff = (n + view[index].size) - oldN;
3072
- index++;
3073
- } else {
3074
- diff = n - oldN;
3075
- }
3076
- oldN += diff; newN += diff;
3077
- }
3078
- while (visualLineNo(cm.doc, newN) != newN) {
3079
- if (index == (dir < 0 ? 0 : view.length - 1)) { return null }
3080
- newN += dir * view[index - (dir < 0 ? 1 : 0)].size;
3081
- index += dir;
3082
- }
3083
- return {index: index, lineN: newN}
3084
- }
3085
-
3086
- // Force the view to cover a given range, adding empty view element
3087
- // or clipping off existing ones as needed.
3088
- function adjustView(cm, from, to) {
3089
- var display = cm.display, view = display.view;
3090
- if (view.length == 0 || from >= display.viewTo || to <= display.viewFrom) {
3091
- display.view = buildViewArray(cm, from, to);
3092
- display.viewFrom = from;
3093
- } else {
3094
- if (display.viewFrom > from)
3095
- { display.view = buildViewArray(cm, from, display.viewFrom).concat(display.view); }
3096
- else if (display.viewFrom < from)
3097
- { display.view = display.view.slice(findViewIndex(cm, from)); }
3098
- display.viewFrom = from;
3099
- if (display.viewTo < to)
3100
- { display.view = display.view.concat(buildViewArray(cm, display.viewTo, to)); }
3101
- else if (display.viewTo > to)
3102
- { display.view = display.view.slice(0, findViewIndex(cm, to)); }
3103
- }
3104
- display.viewTo = to;
3105
- }
3106
-
3107
- // Count the number of lines in the view whose DOM representation is
3108
- // out of date (or nonexistent).
3109
- function countDirtyView(cm) {
3110
- var view = cm.display.view, dirty = 0;
3111
- for (var i = 0; i < view.length; i++) {
3112
- var lineView = view[i];
3113
- if (!lineView.hidden && (!lineView.node || lineView.changes)) { ++dirty; }
3114
- }
3115
- return dirty
3116
- }
3117
-
3118
- function updateSelection(cm) {
3119
- cm.display.input.showSelection(cm.display.input.prepareSelection());
3120
- }
3121
-
3122
- function prepareSelection(cm, primary) {
3123
- if ( primary === void 0 ) primary = true;
3124
-
3125
- var doc = cm.doc, result = {};
3126
- var curFragment = result.cursors = document.createDocumentFragment();
3127
- var selFragment = result.selection = document.createDocumentFragment();
3128
-
3129
- for (var i = 0; i < doc.sel.ranges.length; i++) {
3130
- if (!primary && i == doc.sel.primIndex) { continue }
3131
- var range$$1 = doc.sel.ranges[i];
3132
- if (range$$1.from().line >= cm.display.viewTo || range$$1.to().line < cm.display.viewFrom) { continue }
3133
- var collapsed = range$$1.empty();
3134
- if (collapsed || cm.options.showCursorWhenSelecting)
3135
- { drawSelectionCursor(cm, range$$1.head, curFragment); }
3136
- if (!collapsed)
3137
- { drawSelectionRange(cm, range$$1, selFragment); }
3138
- }
3139
- return result
3140
- }
3141
-
3142
- // Draws a cursor for the given range
3143
- function drawSelectionCursor(cm, head, output) {
3144
- var pos = cursorCoords(cm, head, "div", null, null, !cm.options.singleCursorHeightPerLine);
3145
-
3146
- var cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor"));
3147
- cursor.style.left = pos.left + "px";
3148
- cursor.style.top = pos.top + "px";
3149
- cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px";
3150
-
3151
- if (pos.other) {
3152
- // Secondary cursor, shown when on a 'jump' in bi-directional text
3153
- var otherCursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor"));
3154
- otherCursor.style.display = "";
3155
- otherCursor.style.left = pos.other.left + "px";
3156
- otherCursor.style.top = pos.other.top + "px";
3157
- otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px";
3158
- }
3159
- }
3160
-
3161
- function cmpCoords(a, b) { return a.top - b.top || a.left - b.left }
3162
-
3163
- // Draws the given range as a highlighted selection
3164
- function drawSelectionRange(cm, range$$1, output) {
3165
- var display = cm.display, doc = cm.doc;
3166
- var fragment = document.createDocumentFragment();
3167
- var padding = paddingH(cm.display), leftSide = padding.left;
3168
- var rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right;
3169
- var docLTR = doc.direction == "ltr";
3170
-
3171
- function add(left, top, width, bottom) {
3172
- if (top < 0) { top = 0; }
3173
- top = Math.round(top);
3174
- bottom = Math.round(bottom);
3175
- fragment.appendChild(elt("div", null, "CodeMirror-selected", ("position: absolute; left: " + left + "px;\n top: " + top + "px; width: " + (width == null ? rightSide - left : width) + "px;\n height: " + (bottom - top) + "px")));
3176
- }
3177
-
3178
- function drawForLine(line, fromArg, toArg) {
3179
- var lineObj = getLine(doc, line);
3180
- var lineLen = lineObj.text.length;
3181
- var start, end;
3182
- function coords(ch, bias) {
3183
- return charCoords(cm, Pos(line, ch), "div", lineObj, bias)
3184
- }
3185
-
3186
- function wrapX(pos, dir, side) {
3187
- var extent = wrappedLineExtentChar(cm, lineObj, null, pos);
3188
- var prop = (dir == "ltr") == (side == "after") ? "left" : "right";
3189
- var ch = side == "after" ? extent.begin : extent.end - (/\s/.test(lineObj.text.charAt(extent.end - 1)) ? 2 : 1);
3190
- return coords(ch, prop)[prop]
3191
- }
3192
-
3193
- var order = getOrder(lineObj, doc.direction);
3194
- iterateBidiSections(order, fromArg || 0, toArg == null ? lineLen : toArg, function (from, to, dir, i) {
3195
- var ltr = dir == "ltr";
3196
- var fromPos = coords(from, ltr ? "left" : "right");
3197
- var toPos = coords(to - 1, ltr ? "right" : "left");
3198
-
3199
- var openStart = fromArg == null && from == 0, openEnd = toArg == null && to == lineLen;
3200
- var first = i == 0, last = !order || i == order.length - 1;
3201
- if (toPos.top - fromPos.top <= 3) { // Single line
3202
- var openLeft = (docLTR ? openStart : openEnd) && first;
3203
- var openRight = (docLTR ? openEnd : openStart) && last;
3204
- var left = openLeft ? leftSide : (ltr ? fromPos : toPos).left;
3205
- var right = openRight ? rightSide : (ltr ? toPos : fromPos).right;
3206
- add(left, fromPos.top, right - left, fromPos.bottom);
3207
- } else { // Multiple lines
3208
- var topLeft, topRight, botLeft, botRight;
3209
- if (ltr) {
3210
- topLeft = docLTR && openStart && first ? leftSide : fromPos.left;
3211
- topRight = docLTR ? rightSide : wrapX(from, dir, "before");
3212
- botLeft = docLTR ? leftSide : wrapX(to, dir, "after");
3213
- botRight = docLTR && openEnd && last ? rightSide : toPos.right;
3214
- } else {
3215
- topLeft = !docLTR ? leftSide : wrapX(from, dir, "before");
3216
- topRight = !docLTR && openStart && first ? rightSide : fromPos.right;
3217
- botLeft = !docLTR && openEnd && last ? leftSide : toPos.left;
3218
- botRight = !docLTR ? rightSide : wrapX(to, dir, "after");
3219
- }
3220
- add(topLeft, fromPos.top, topRight - topLeft, fromPos.bottom);
3221
- if (fromPos.bottom < toPos.top) { add(leftSide, fromPos.bottom, null, toPos.top); }
3222
- add(botLeft, toPos.top, botRight - botLeft, toPos.bottom);
3223
- }
3224
-
3225
- if (!start || cmpCoords(fromPos, start) < 0) { start = fromPos; }
3226
- if (cmpCoords(toPos, start) < 0) { start = toPos; }
3227
- if (!end || cmpCoords(fromPos, end) < 0) { end = fromPos; }
3228
- if (cmpCoords(toPos, end) < 0) { end = toPos; }
3229
- });
3230
- return {start: start, end: end}
3231
- }
3232
-
3233
- var sFrom = range$$1.from(), sTo = range$$1.to();
3234
- if (sFrom.line == sTo.line) {
3235
- drawForLine(sFrom.line, sFrom.ch, sTo.ch);
3236
- } else {
3237
- var fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line);
3238
- var singleVLine = visualLine(fromLine) == visualLine(toLine);
3239
- var leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end;
3240
- var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start;
3241
- if (singleVLine) {
3242
- if (leftEnd.top < rightStart.top - 2) {
3243
- add(leftEnd.right, leftEnd.top, null, leftEnd.bottom);
3244
- add(leftSide, rightStart.top, rightStart.left, rightStart.bottom);
3245
- } else {
3246
- add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom);
3247
- }
3248
- }
3249
- if (leftEnd.bottom < rightStart.top)
3250
- { add(leftSide, leftEnd.bottom, null, rightStart.top); }
3251
- }
3252
-
3253
- output.appendChild(fragment);
3254
- }
3255
-
3256
- // Cursor-blinking
3257
- function restartBlink(cm) {
3258
- if (!cm.state.focused) { return }
3259
- var display = cm.display;
3260
- clearInterval(display.blinker);
3261
- var on = true;
3262
- display.cursorDiv.style.visibility = "";
3263
- if (cm.options.cursorBlinkRate > 0)
3264
- { display.blinker = setInterval(function () { return display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden"; },
3265
- cm.options.cursorBlinkRate); }
3266
- else if (cm.options.cursorBlinkRate < 0)
3267
- { display.cursorDiv.style.visibility = "hidden"; }
3268
- }
3269
-
3270
- function ensureFocus(cm) {
3271
- if (!cm.state.focused) { cm.display.input.focus(); onFocus(cm); }
3272
- }
3273
-
3274
- function delayBlurEvent(cm) {
3275
- cm.state.delayingBlurEvent = true;
3276
- setTimeout(function () { if (cm.state.delayingBlurEvent) {
3277
- cm.state.delayingBlurEvent = false;
3278
- onBlur(cm);
3279
- } }, 100);
3280
- }
3281
-
3282
- function onFocus(cm, e) {
3283
- if (cm.state.delayingBlurEvent) { cm.state.delayingBlurEvent = false; }
3284
-
3285
- if (cm.options.readOnly == "nocursor") { return }
3286
- if (!cm.state.focused) {
3287
- signal(cm, "focus", cm, e);
3288
- cm.state.focused = true;
3289
- addClass(cm.display.wrapper, "CodeMirror-focused");
3290
- // This test prevents this from firing when a context
3291
- // menu is closed (since the input reset would kill the
3292
- // select-all detection hack)
3293
- if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) {
3294
- cm.display.input.reset();
3295
- if (webkit) { setTimeout(function () { return cm.display.input.reset(true); }, 20); } // Issue #1730
3296
- }
3297
- cm.display.input.receivedFocus();
3298
- }
3299
- restartBlink(cm);
3300
- }
3301
- function onBlur(cm, e) {
3302
- if (cm.state.delayingBlurEvent) { return }
3303
-
3304
- if (cm.state.focused) {
3305
- signal(cm, "blur", cm, e);
3306
- cm.state.focused = false;
3307
- rmClass(cm.display.wrapper, "CodeMirror-focused");
3308
- }
3309
- clearInterval(cm.display.blinker);
3310
- setTimeout(function () { if (!cm.state.focused) { cm.display.shift = false; } }, 150);
3311
- }
3312
-
3313
- // Read the actual heights of the rendered lines, and update their
3314
- // stored heights to match.
3315
- function updateHeightsInViewport(cm) {
3316
- var display = cm.display;
3317
- var prevBottom = display.lineDiv.offsetTop;
3318
- for (var i = 0; i < display.view.length; i++) {
3319
- var cur = display.view[i], wrapping = cm.options.lineWrapping;
3320
- var height = (void 0), width = 0;
3321
- if (cur.hidden) { continue }
3322
- if (ie && ie_version < 8) {
3323
- var bot = cur.node.offsetTop + cur.node.offsetHeight;
3324
- height = bot - prevBottom;
3325
- prevBottom = bot;
3326
- } else {
3327
- var box = cur.node.getBoundingClientRect();
3328
- height = box.bottom - box.top;
3329
- // Check that lines don't extend past the right of the current
3330
- // editor width
3331
- if (!wrapping && cur.text.firstChild)
3332
- { width = cur.text.firstChild.getBoundingClientRect().right - box.left - 1; }
3333
- }
3334
- var diff = cur.line.height - height;
3335
- if (diff > .005 || diff < -.005) {
3336
- updateLineHeight(cur.line, height);
3337
- updateWidgetHeight(cur.line);
3338
- if (cur.rest) { for (var j = 0; j < cur.rest.length; j++)
3339
- { updateWidgetHeight(cur.rest[j]); } }
3340
- }
3341
- if (width > cm.display.sizerWidth) {
3342
- var chWidth = Math.ceil(width / charWidth(cm.display));
3343
- if (chWidth > cm.display.maxLineLength) {
3344
- cm.display.maxLineLength = chWidth;
3345
- cm.display.maxLine = cur.line;
3346
- cm.display.maxLineChanged = true;
3347
- }
3348
- }
3349
- }
3350
- }
3351
-
3352
- // Read and store the height of line widgets associated with the
3353
- // given line.
3354
- function updateWidgetHeight(line) {
3355
- if (line.widgets) { for (var i = 0; i < line.widgets.length; ++i) {
3356
- var w = line.widgets[i], parent = w.node.parentNode;
3357
- if (parent) { w.height = parent.offsetHeight; }
3358
- } }
3359
- }
3360
-
3361
- // Compute the lines that are visible in a given viewport (defaults
3362
- // the the current scroll position). viewport may contain top,
3363
- // height, and ensure (see op.scrollToPos) properties.
3364
- function visibleLines(display, doc, viewport) {
3365
- var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop;
3366
- top = Math.floor(top - paddingTop(display));
3367
- var bottom = viewport && viewport.bottom != null ? viewport.bottom : top + display.wrapper.clientHeight;
3368
-
3369
- var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom);
3370
- // Ensure is a {from: {line, ch}, to: {line, ch}} object, and
3371
- // forces those lines into the viewport (if possible).
3372
- if (viewport && viewport.ensure) {
3373
- var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line;
3374
- if (ensureFrom < from) {
3375
- from = ensureFrom;
3376
- to = lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight);
3377
- } else if (Math.min(ensureTo, doc.lastLine()) >= to) {
3378
- from = lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight);
3379
- to = ensureTo;
3380
- }
3381
- }
3382
- return {from: from, to: Math.max(to, from + 1)}
3383
- }
3384
-
3385
- // SCROLLING THINGS INTO VIEW
3386
-
3387
- // If an editor sits on the top or bottom of the window, partially
3388
- // scrolled out of view, this ensures that the cursor is visible.
3389
- function maybeScrollWindow(cm, rect) {
3390
- if (signalDOMEvent(cm, "scrollCursorIntoView")) { return }
3391
-
3392
- var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null;
3393
- if (rect.top + box.top < 0) { doScroll = true; }
3394
- else if (rect.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) { doScroll = false; }
3395
- if (doScroll != null && !phantom) {
3396
- var scrollNode = elt("div", "\u200b", null, ("position: absolute;\n top: " + (rect.top - display.viewOffset - paddingTop(cm.display)) + "px;\n height: " + (rect.bottom - rect.top + scrollGap(cm) + display.barHeight) + "px;\n left: " + (rect.left) + "px; width: " + (Math.max(2, rect.right - rect.left)) + "px;"));
3397
- cm.display.lineSpace.appendChild(scrollNode);
3398
- scrollNode.scrollIntoView(doScroll);
3399
- cm.display.lineSpace.removeChild(scrollNode);
3400
- }
3401
- }
3402
-
3403
- // Scroll a given position into view (immediately), verifying that
3404
- // it actually became visible (as line heights are accurately
3405
- // measured, the position of something may 'drift' during drawing).
3406
- function scrollPosIntoView(cm, pos, end, margin) {
3407
- if (margin == null) { margin = 0; }
3408
- var rect;
3409
- if (!cm.options.lineWrapping && pos == end) {
3410
- // Set pos and end to the cursor positions around the character pos sticks to
3411
- // If pos.sticky == "before", that is around pos.ch - 1, otherwise around pos.ch
3412
- // If pos == Pos(_, 0, "before"), pos and end are unchanged
3413
- pos = pos.ch ? Pos(pos.line, pos.sticky == "before" ? pos.ch - 1 : pos.ch, "after") : pos;
3414
- end = pos.sticky == "before" ? Pos(pos.line, pos.ch + 1, "before") : pos;
3415
- }
3416
- for (var limit = 0; limit < 5; limit++) {
3417
- var changed = false;
3418
- var coords = cursorCoords(cm, pos);
3419
- var endCoords = !end || end == pos ? coords : cursorCoords(cm, end);
3420
- rect = {left: Math.min(coords.left, endCoords.left),
3421
- top: Math.min(coords.top, endCoords.top) - margin,
3422
- right: Math.max(coords.left, endCoords.left),
3423
- bottom: Math.max(coords.bottom, endCoords.bottom) + margin};
3424
- var scrollPos = calculateScrollPos(cm, rect);
3425
- var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft;
3426
- if (scrollPos.scrollTop != null) {
3427
- updateScrollTop(cm, scrollPos.scrollTop);
3428
- if (Math.abs(cm.doc.scrollTop - startTop) > 1) { changed = true; }
3429
- }
3430
- if (scrollPos.scrollLeft != null) {
3431
- setScrollLeft(cm, scrollPos.scrollLeft);
3432
- if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) { changed = true; }
3433
- }
3434
- if (!changed) { break }
3435
- }
3436
- return rect
3437
- }
3438
-
3439
- // Scroll a given set of coordinates into view (immediately).
3440
- function scrollIntoView(cm, rect) {
3441
- var scrollPos = calculateScrollPos(cm, rect);
3442
- if (scrollPos.scrollTop != null) { updateScrollTop(cm, scrollPos.scrollTop); }
3443
- if (scrollPos.scrollLeft != null) { setScrollLeft(cm, scrollPos.scrollLeft); }
3444
- }
3445
-
3446
- // Calculate a new scroll position needed to scroll the given
3447
- // rectangle into view. Returns an object with scrollTop and
3448
- // scrollLeft properties. When these are undefined, the
3449
- // vertical/horizontal position does not need to be adjusted.
3450
- function calculateScrollPos(cm, rect) {
3451
- var display = cm.display, snapMargin = textHeight(cm.display);
3452
- if (rect.top < 0) { rect.top = 0; }
3453
- var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop;
3454
- var screen = displayHeight(cm), result = {};
3455
- if (rect.bottom - rect.top > screen) { rect.bottom = rect.top + screen; }
3456
- var docBottom = cm.doc.height + paddingVert(display);
3457
- var atTop = rect.top < snapMargin, atBottom = rect.bottom > docBottom - snapMargin;
3458
- if (rect.top < screentop) {
3459
- result.scrollTop = atTop ? 0 : rect.top;
3460
- } else if (rect.bottom > screentop + screen) {
3461
- var newTop = Math.min(rect.top, (atBottom ? docBottom : rect.bottom) - screen);
3462
- if (newTop != screentop) { result.scrollTop = newTop; }
3463
- }
3464
-
3465
- var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft;
3466
- var screenw = displayWidth(cm) - (cm.options.fixedGutter ? display.gutters.offsetWidth : 0);
3467
- var tooWide = rect.right - rect.left > screenw;
3468
- if (tooWide) { rect.right = rect.left + screenw; }
3469
- if (rect.left < 10)
3470
- { result.scrollLeft = 0; }
3471
- else if (rect.left < screenleft)
3472
- { result.scrollLeft = Math.max(0, rect.left - (tooWide ? 0 : 10)); }
3473
- else if (rect.right > screenw + screenleft - 3)
3474
- { result.scrollLeft = rect.right + (tooWide ? 0 : 10) - screenw; }
3475
- return result
3476
- }
3477
-
3478
- // Store a relative adjustment to the scroll position in the current
3479
- // operation (to be applied when the operation finishes).
3480
- function addToScrollTop(cm, top) {
3481
- if (top == null) { return }
3482
- resolveScrollToPos(cm);
3483
- cm.curOp.scrollTop = (cm.curOp.scrollTop == null ? cm.doc.scrollTop : cm.curOp.scrollTop) + top;
3484
- }
3485
-
3486
- // Make sure that at the end of the operation the current cursor is
3487
- // shown.
3488
- function ensureCursorVisible(cm) {
3489
- resolveScrollToPos(cm);
3490
- var cur = cm.getCursor();
3491
- cm.curOp.scrollToPos = {from: cur, to: cur, margin: cm.options.cursorScrollMargin};
3492
- }
3493
-
3494
- function scrollToCoords(cm, x, y) {
3495
- if (x != null || y != null) { resolveScrollToPos(cm); }
3496
- if (x != null) { cm.curOp.scrollLeft = x; }
3497
- if (y != null) { cm.curOp.scrollTop = y; }
3498
- }
3499
-
3500
- function scrollToRange(cm, range$$1) {
3501
- resolveScrollToPos(cm);
3502
- cm.curOp.scrollToPos = range$$1;
3503
- }
3504
-
3505
- // When an operation has its scrollToPos property set, and another
3506
- // scroll action is applied before the end of the operation, this
3507
- // 'simulates' scrolling that position into view in a cheap way, so
3508
- // that the effect of intermediate scroll commands is not ignored.
3509
- function resolveScrollToPos(cm) {
3510
- var range$$1 = cm.curOp.scrollToPos;
3511
- if (range$$1) {
3512
- cm.curOp.scrollToPos = null;
3513
- var from = estimateCoords(cm, range$$1.from), to = estimateCoords(cm, range$$1.to);
3514
- scrollToCoordsRange(cm, from, to, range$$1.margin);
3515
- }
3516
- }
3517
-
3518
- function scrollToCoordsRange(cm, from, to, margin) {
3519
- var sPos = calculateScrollPos(cm, {
3520
- left: Math.min(from.left, to.left),
3521
- top: Math.min(from.top, to.top) - margin,
3522
- right: Math.max(from.right, to.right),
3523
- bottom: Math.max(from.bottom, to.bottom) + margin
3524
- });
3525
- scrollToCoords(cm, sPos.scrollLeft, sPos.scrollTop);
3526
- }
3527
-
3528
- // Sync the scrollable area and scrollbars, ensure the viewport
3529
- // covers the visible area.
3530
- function updateScrollTop(cm, val) {
3531
- if (Math.abs(cm.doc.scrollTop - val) < 2) { return }
3532
- if (!gecko) { updateDisplaySimple(cm, {top: val}); }
3533
- setScrollTop(cm, val, true);
3534
- if (gecko) { updateDisplaySimple(cm); }
3535
- startWorker(cm, 100);
3536
- }
3537
-
3538
- function setScrollTop(cm, val, forceScroll) {
3539
- val = Math.min(cm.display.scroller.scrollHeight - cm.display.scroller.clientHeight, val);
3540
- if (cm.display.scroller.scrollTop == val && !forceScroll) { return }
3541
- cm.doc.scrollTop = val;
3542
- cm.display.scrollbars.setScrollTop(val);
3543
- if (cm.display.scroller.scrollTop != val) { cm.display.scroller.scrollTop = val; }
3544
- }
3545
-
3546
- // Sync scroller and scrollbar, ensure the gutter elements are
3547
- // aligned.
3548
- function setScrollLeft(cm, val, isScroller, forceScroll) {
3549
- val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth);
3550
- if ((isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) && !forceScroll) { return }
3551
- cm.doc.scrollLeft = val;
3552
- alignHorizontally(cm);
3553
- if (cm.display.scroller.scrollLeft != val) { cm.display.scroller.scrollLeft = val; }
3554
- cm.display.scrollbars.setScrollLeft(val);
3555
- }
3556
-
3557
- // SCROLLBARS
3558
-
3559
- // Prepare DOM reads needed to update the scrollbars. Done in one
3560
- // shot to minimize update/measure roundtrips.
3561
- function measureForScrollbars(cm) {
3562
- var d = cm.display, gutterW = d.gutters.offsetWidth;
3563
- var docH = Math.round(cm.doc.height + paddingVert(cm.display));
3564
- return {
3565
- clientHeight: d.scroller.clientHeight,
3566
- viewHeight: d.wrapper.clientHeight,
3567
- scrollWidth: d.scroller.scrollWidth, clientWidth: d.scroller.clientWidth,
3568
- viewWidth: d.wrapper.clientWidth,
3569
- barLeft: cm.options.fixedGutter ? gutterW : 0,
3570
- docHeight: docH,
3571
- scrollHeight: docH + scrollGap(cm) + d.barHeight,
3572
- nativeBarWidth: d.nativeBarWidth,
3573
- gutterWidth: gutterW
3574
- }
3575
- }
3576
-
3577
- var NativeScrollbars = function(place, scroll, cm) {
3578
- this.cm = cm;
3579
- var vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar");
3580
- var horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar");
3581
- vert.tabIndex = horiz.tabIndex = -1;
3582
- place(vert); place(horiz);
3583
-
3584
- on(vert, "scroll", function () {
3585
- if (vert.clientHeight) { scroll(vert.scrollTop, "vertical"); }
3586
- });
3587
- on(horiz, "scroll", function () {
3588
- if (horiz.clientWidth) { scroll(horiz.scrollLeft, "horizontal"); }
3589
- });
3590
-
3591
- this.checkedZeroWidth = false;
3592
- // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
3593
- if (ie && ie_version < 8) { this.horiz.style.minHeight = this.vert.style.minWidth = "18px"; }
3594
- };
3595
-
3596
- NativeScrollbars.prototype.update = function (measure) {
3597
- var needsH = measure.scrollWidth > measure.clientWidth + 1;
3598
- var needsV = measure.scrollHeight > measure.clientHeight + 1;
3599
- var sWidth = measure.nativeBarWidth;
3600
-
3601
- if (needsV) {
3602
- this.vert.style.display = "block";
3603
- this.vert.style.bottom = needsH ? sWidth + "px" : "0";
3604
- var totalHeight = measure.viewHeight - (needsH ? sWidth : 0);
3605
- // A bug in IE8 can cause this value to be negative, so guard it.
3606
- this.vert.firstChild.style.height =
3607
- Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + "px";
3608
- } else {
3609
- this.vert.style.display = "";
3610
- this.vert.firstChild.style.height = "0";
3611
- }
3612
-
3613
- if (needsH) {
3614
- this.horiz.style.display = "block";
3615
- this.horiz.style.right = needsV ? sWidth + "px" : "0";
3616
- this.horiz.style.left = measure.barLeft + "px";
3617
- var totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0);
3618
- this.horiz.firstChild.style.width =
3619
- Math.max(0, measure.scrollWidth - measure.clientWidth + totalWidth) + "px";
3620
- } else {
3621
- this.horiz.style.display = "";
3622
- this.horiz.firstChild.style.width = "0";
3623
- }
3624
-
3625
- if (!this.checkedZeroWidth && measure.clientHeight > 0) {
3626
- if (sWidth == 0) { this.zeroWidthHack(); }
3627
- this.checkedZeroWidth = true;
3628
- }
3629
-
3630
- return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0}
3631
- };
3632
-
3633
- NativeScrollbars.prototype.setScrollLeft = function (pos) {
3634
- if (this.horiz.scrollLeft != pos) { this.horiz.scrollLeft = pos; }
3635
- if (this.disableHoriz) { this.enableZeroWidthBar(this.horiz, this.disableHoriz, "horiz"); }
3636
- };
3637
-
3638
- NativeScrollbars.prototype.setScrollTop = function (pos) {
3639
- if (this.vert.scrollTop != pos) { this.vert.scrollTop = pos; }
3640
- if (this.disableVert) { this.enableZeroWidthBar(this.vert, this.disableVert, "vert"); }
3641
- };
3642
-
3643
- NativeScrollbars.prototype.zeroWidthHack = function () {
3644
- var w = mac && !mac_geMountainLion ? "12px" : "18px";
3645
- this.horiz.style.height = this.vert.style.width = w;
3646
- this.horiz.style.pointerEvents = this.vert.style.pointerEvents = "none";
3647
- this.disableHoriz = new Delayed;
3648
- this.disableVert = new Delayed;
3649
- };
3650
-
3651
- NativeScrollbars.prototype.enableZeroWidthBar = function (bar, delay, type) {
3652
- bar.style.pointerEvents = "auto";
3653
- function maybeDisable() {
3654
- // To find out whether the scrollbar is still visible, we
3655
- // check whether the element under the pixel in the bottom
3656
- // right corner of the scrollbar box is the scrollbar box
3657
- // itself (when the bar is still visible) or its filler child
3658
- // (when the bar is hidden). If it is still visible, we keep
3659
- // it enabled, if it's hidden, we disable pointer events.
3660
- var box = bar.getBoundingClientRect();
3661
- var elt$$1 = type == "vert" ? document.elementFromPoint(box.right - 1, (box.top + box.bottom) / 2)
3662
- : document.elementFromPoint((box.right + box.left) / 2, box.bottom - 1);
3663
- if (elt$$1 != bar) { bar.style.pointerEvents = "none"; }
3664
- else { delay.set(1000, maybeDisable); }
3665
- }
3666
- delay.set(1000, maybeDisable);
3667
- };
3668
-
3669
- NativeScrollbars.prototype.clear = function () {
3670
- var parent = this.horiz.parentNode;
3671
- parent.removeChild(this.horiz);
3672
- parent.removeChild(this.vert);
3673
- };
3674
-
3675
- var NullScrollbars = function () {};
3676
-
3677
- NullScrollbars.prototype.update = function () { return {bottom: 0, right: 0} };
3678
- NullScrollbars.prototype.setScrollLeft = function () {};
3679
- NullScrollbars.prototype.setScrollTop = function () {};
3680
- NullScrollbars.prototype.clear = function () {};
3681
-
3682
- function updateScrollbars(cm, measure) {
3683
- if (!measure) { measure = measureForScrollbars(cm); }
3684
- var startWidth = cm.display.barWidth, startHeight = cm.display.barHeight;
3685
- updateScrollbarsInner(cm, measure);
3686
- for (var i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != cm.display.barHeight; i++) {
3687
- if (startWidth != cm.display.barWidth && cm.options.lineWrapping)
3688
- { updateHeightsInViewport(cm); }
3689
- updateScrollbarsInner(cm, measureForScrollbars(cm));
3690
- startWidth = cm.display.barWidth; startHeight = cm.display.barHeight;
3691
- }
3692
- }
3693
-
3694
- // Re-synchronize the fake scrollbars with the actual size of the
3695
- // content.
3696
- function updateScrollbarsInner(cm, measure) {
3697
- var d = cm.display;
3698
- var sizes = d.scrollbars.update(measure);
3699
-
3700
- d.sizer.style.paddingRight = (d.barWidth = sizes.right) + "px";
3701
- d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + "px";
3702
- d.heightForcer.style.borderBottom = sizes.bottom + "px solid transparent";
3703
-
3704
- if (sizes.right && sizes.bottom) {
3705
- d.scrollbarFiller.style.display = "block";
3706
- d.scrollbarFiller.style.height = sizes.bottom + "px";
3707
- d.scrollbarFiller.style.width = sizes.right + "px";
3708
- } else { d.scrollbarFiller.style.display = ""; }
3709
- if (sizes.bottom && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) {
3710
- d.gutterFiller.style.display = "block";
3711
- d.gutterFiller.style.height = sizes.bottom + "px";
3712
- d.gutterFiller.style.width = measure.gutterWidth + "px";
3713
- } else { d.gutterFiller.style.display = ""; }
3714
- }
3715
-
3716
- var scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbars};
3717
-
3718
- function initScrollbars(cm) {
3719
- if (cm.display.scrollbars) {
3720
- cm.display.scrollbars.clear();
3721
- if (cm.display.scrollbars.addClass)
3722
- { rmClass(cm.display.wrapper, cm.display.scrollbars.addClass); }
3723
- }
3724
-
3725
- cm.display.scrollbars = new scrollbarModel[cm.options.scrollbarStyle](function (node) {
3726
- cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller);
3727
- // Prevent clicks in the scrollbars from killing focus
3728
- on(node, "mousedown", function () {
3729
- if (cm.state.focused) { setTimeout(function () { return cm.display.input.focus(); }, 0); }
3730
- });
3731
- node.setAttribute("cm-not-content", "true");
3732
- }, function (pos, axis) {
3733
- if (axis == "horizontal") { setScrollLeft(cm, pos); }
3734
- else { updateScrollTop(cm, pos); }
3735
- }, cm);
3736
- if (cm.display.scrollbars.addClass)
3737
- { addClass(cm.display.wrapper, cm.display.scrollbars.addClass); }
3738
- }
3739
-
3740
- // Operations are used to wrap a series of changes to the editor
3741
- // state in such a way that each change won't have to update the
3742
- // cursor and display (which would be awkward, slow, and
3743
- // error-prone). Instead, display updates are batched and then all
3744
- // combined and executed at once.
3745
-
3746
- var nextOpId = 0;
3747
- // Start a new operation.
3748
- function startOperation(cm) {
3749
- cm.curOp = {
3750
- cm: cm,
3751
- viewChanged: false, // Flag that indicates that lines might need to be redrawn
3752
- startHeight: cm.doc.height, // Used to detect need to update scrollbar
3753
- forceUpdate: false, // Used to force a redraw
3754
- updateInput: 0, // Whether to reset the input textarea
3755
- typing: false, // Whether this reset should be careful to leave existing text (for compositing)
3756
- changeObjs: null, // Accumulated changes, for firing change events
3757
- cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on
3758
- cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already
3759
- selectionChanged: false, // Whether the selection needs to be redrawn
3760
- updateMaxLine: false, // Set when the widest line needs to be determined anew
3761
- scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet
3762
- scrollToPos: null, // Used to scroll to a specific position
3763
- focus: false,
3764
- id: ++nextOpId // Unique ID
3765
- };
3766
- pushOperation(cm.curOp);
3767
- }
3768
-
3769
- // Finish an operation, updating the display and signalling delayed events
3770
- function endOperation(cm) {
3771
- var op = cm.curOp;
3772
- if (op) { finishOperation(op, function (group) {
3773
- for (var i = 0; i < group.ops.length; i++)
3774
- { group.ops[i].cm.curOp = null; }
3775
- endOperations(group);
3776
- }); }
3777
- }
3778
-
3779
- // The DOM updates done when an operation finishes are batched so
3780
- // that the minimum number of relayouts are required.
3781
- function endOperations(group) {
3782
- var ops = group.ops;
3783
- for (var i = 0; i < ops.length; i++) // Read DOM
3784
- { endOperation_R1(ops[i]); }
3785
- for (var i$1 = 0; i$1 < ops.length; i$1++) // Write DOM (maybe)
3786
- { endOperation_W1(ops[i$1]); }
3787
- for (var i$2 = 0; i$2 < ops.length; i$2++) // Read DOM
3788
- { endOperation_R2(ops[i$2]); }
3789
- for (var i$3 = 0; i$3 < ops.length; i$3++) // Write DOM (maybe)
3790
- { endOperation_W2(ops[i$3]); }
3791
- for (var i$4 = 0; i$4 < ops.length; i$4++) // Read DOM
3792
- { endOperation_finish(ops[i$4]); }
3793
- }
3794
-
3795
- function endOperation_R1(op) {
3796
- var cm = op.cm, display = cm.display;
3797
- maybeClipScrollbars(cm);
3798
- if (op.updateMaxLine) { findMaxLine(cm); }
3799
-
3800
- op.mustUpdate = op.viewChanged || op.forceUpdate || op.scrollTop != null ||
3801
- op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom ||
3802
- op.scrollToPos.to.line >= display.viewTo) ||
3803
- display.maxLineChanged && cm.options.lineWrapping;
3804
- op.update = op.mustUpdate &&
3805
- new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate);
3806
- }
3807
-
3808
- function endOperation_W1(op) {
3809
- op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update);
3810
- }
3811
-
3812
- function endOperation_R2(op) {
3813
- var cm = op.cm, display = cm.display;
3814
- if (op.updatedDisplay) { updateHeightsInViewport(cm); }
3815
-
3816
- op.barMeasure = measureForScrollbars(cm);
3817
-
3818
- // If the max line changed since it was last measured, measure it,
3819
- // and ensure the document's width matches it.
3820
- // updateDisplay_W2 will use these properties to do the actual resizing
3821
- if (display.maxLineChanged && !cm.options.lineWrapping) {
3822
- op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.length).left + 3;
3823
- cm.display.sizerWidth = op.adjustWidthTo;
3824
- op.barMeasure.scrollWidth =
3825
- Math.max(display.scroller.clientWidth, display.sizer.offsetLeft + op.adjustWidthTo + scrollGap(cm) + cm.display.barWidth);
3826
- op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm));
3827
- }
3828
-
3829
- if (op.updatedDisplay || op.selectionChanged)
3830
- { op.preparedSelection = display.input.prepareSelection(); }
3831
- }
3832
-
3833
- function endOperation_W2(op) {
3834
- var cm = op.cm;
3835
-
3836
- if (op.adjustWidthTo != null) {
3837
- cm.display.sizer.style.minWidth = op.adjustWidthTo + "px";
3838
- if (op.maxScrollLeft < cm.doc.scrollLeft)
3839
- { setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true); }
3840
- cm.display.maxLineChanged = false;
3841
- }
3842
-
3843
- var takeFocus = op.focus && op.focus == activeElt();
3844
- if (op.preparedSelection)
3845
- { cm.display.input.showSelection(op.preparedSelection, takeFocus); }
3846
- if (op.updatedDisplay || op.startHeight != cm.doc.height)
3847
- { updateScrollbars(cm, op.barMeasure); }
3848
- if (op.updatedDisplay)
3849
- { setDocumentHeight(cm, op.barMeasure); }
3850
-
3851
- if (op.selectionChanged) { restartBlink(cm); }
3852
-
3853
- if (cm.state.focused && op.updateInput)
3854
- { cm.display.input.reset(op.typing); }
3855
- if (takeFocus) { ensureFocus(op.cm); }
3856
- }
3857
-
3858
- function endOperation_finish(op) {
3859
- var cm = op.cm, display = cm.display, doc = cm.doc;
3860
-
3861
- if (op.updatedDisplay) { postUpdateDisplay(cm, op.update); }
3862
-
3863
- // Abort mouse wheel delta measurement, when scrolling explicitly
3864
- if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos))
3865
- { display.wheelStartX = display.wheelStartY = null; }
3866
-
3867
- // Propagate the scroll position to the actual DOM scroller
3868
- if (op.scrollTop != null) { setScrollTop(cm, op.scrollTop, op.forceScroll); }
3869
-
3870
- if (op.scrollLeft != null) { setScrollLeft(cm, op.scrollLeft, true, true); }
3871
- // If we need to scroll a specific position into view, do so.
3872
- if (op.scrollToPos) {
3873
- var rect = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from),
3874
- clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin);
3875
- maybeScrollWindow(cm, rect);
3876
- }
3877
-
3878
- // Fire events for markers that are hidden/unidden by editing or
3879
- // undoing
3880
- var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers;
3881
- if (hidden) { for (var i = 0; i < hidden.length; ++i)
3882
- { if (!hidden[i].lines.length) { signal(hidden[i], "hide"); } } }
3883
- if (unhidden) { for (var i$1 = 0; i$1 < unhidden.length; ++i$1)
3884
- { if (unhidden[i$1].lines.length) { signal(unhidden[i$1], "unhide"); } } }
3885
-
3886
- if (display.wrapper.offsetHeight)
3887
- { doc.scrollTop = cm.display.scroller.scrollTop; }
3888
-
3889
- // Fire change events, and delayed event handlers
3890
- if (op.changeObjs)
3891
- { signal(cm, "changes", cm, op.changeObjs); }
3892
- if (op.update)
3893
- { op.update.finish(); }
3894
- }
3895
-
3896
- // Run the given function in an operation
3897
- function runInOp(cm, f) {
3898
- if (cm.curOp) { return f() }
3899
- startOperation(cm);
3900
- try { return f() }
3901
- finally { endOperation(cm); }
3902
- }
3903
- // Wraps a function in an operation. Returns the wrapped function.
3904
- function operation(cm, f) {
3905
- return function() {
3906
- if (cm.curOp) { return f.apply(cm, arguments) }
3907
- startOperation(cm);
3908
- try { return f.apply(cm, arguments) }
3909
- finally { endOperation(cm); }
3910
- }
3911
- }
3912
- // Used to add methods to editor and doc instances, wrapping them in
3913
- // operations.
3914
- function methodOp(f) {
3915
- return function() {
3916
- if (this.curOp) { return f.apply(this, arguments) }
3917
- startOperation(this);
3918
- try { return f.apply(this, arguments) }
3919
- finally { endOperation(this); }
3920
- }
3921
- }
3922
- function docMethodOp(f) {
3923
- return function() {
3924
- var cm = this.cm;
3925
- if (!cm || cm.curOp) { return f.apply(this, arguments) }
3926
- startOperation(cm);
3927
- try { return f.apply(this, arguments) }
3928
- finally { endOperation(cm); }
3929
- }
3930
- }
3931
-
3932
- // HIGHLIGHT WORKER
3933
-
3934
- function startWorker(cm, time) {
3935
- if (cm.doc.highlightFrontier < cm.display.viewTo)
3936
- { cm.state.highlight.set(time, bind(highlightWorker, cm)); }
3937
- }
3938
-
3939
- function highlightWorker(cm) {
3940
- var doc = cm.doc;
3941
- if (doc.highlightFrontier >= cm.display.viewTo) { return }
3942
- var end = +new Date + cm.options.workTime;
3943
- var context = getContextBefore(cm, doc.highlightFrontier);
3944
- var changedLines = [];
3945
-
3946
- doc.iter(context.line, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function (line) {
3947
- if (context.line >= cm.display.viewFrom) { // Visible
3948
- var oldStyles = line.styles;
3949
- var resetState = line.text.length > cm.options.maxHighlightLength ? copyState(doc.mode, context.state) : null;
3950
- var highlighted = highlightLine(cm, line, context, true);
3951
- if (resetState) { context.state = resetState; }
3952
- line.styles = highlighted.styles;
3953
- var oldCls = line.styleClasses, newCls = highlighted.classes;
3954
- if (newCls) { line.styleClasses = newCls; }
3955
- else if (oldCls) { line.styleClasses = null; }
3956
- var ischange = !oldStyles || oldStyles.length != line.styles.length ||
3957
- oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgClass || oldCls.textClass != newCls.textClass);
3958
- for (var i = 0; !ischange && i < oldStyles.length; ++i) { ischange = oldStyles[i] != line.styles[i]; }
3959
- if (ischange) { changedLines.push(context.line); }
3960
- line.stateAfter = context.save();
3961
- context.nextLine();
3962
- } else {
3963
- if (line.text.length <= cm.options.maxHighlightLength)
3964
- { processLine(cm, line.text, context); }
3965
- line.stateAfter = context.line % 5 == 0 ? context.save() : null;
3966
- context.nextLine();
3967
- }
3968
- if (+new Date > end) {
3969
- startWorker(cm, cm.options.workDelay);
3970
- return true
3971
- }
3972
- });
3973
- doc.highlightFrontier = context.line;
3974
- doc.modeFrontier = Math.max(doc.modeFrontier, context.line);
3975
- if (changedLines.length) { runInOp(cm, function () {
3976
- for (var i = 0; i < changedLines.length; i++)
3977
- { regLineChange(cm, changedLines[i], "text"); }
3978
- }); }
3979
- }
3980
-
3981
- // DISPLAY DRAWING
3982
-
3983
- var DisplayUpdate = function(cm, viewport, force) {
3984
- var display = cm.display;
3985
-
3986
- this.viewport = viewport;
3987
- // Store some values that we'll need later (but don't want to force a relayout for)
3988
- this.visible = visibleLines(display, cm.doc, viewport);
3989
- this.editorIsHidden = !display.wrapper.offsetWidth;
3990
- this.wrapperHeight = display.wrapper.clientHeight;
3991
- this.wrapperWidth = display.wrapper.clientWidth;
3992
- this.oldDisplayWidth = displayWidth(cm);
3993
- this.force = force;
3994
- this.dims = getDimensions(cm);
3995
- this.events = [];
3996
- };
3997
-
3998
- DisplayUpdate.prototype.signal = function (emitter, type) {
3999
- if (hasHandler(emitter, type))
4000
- { this.events.push(arguments); }
4001
- };
4002
- DisplayUpdate.prototype.finish = function () {
4003
- var this$1 = this;
4004
-
4005
- for (var i = 0; i < this.events.length; i++)
4006
- { signal.apply(null, this$1.events[i]); }
4007
- };
4008
-
4009
- function maybeClipScrollbars(cm) {
4010
- var display = cm.display;
4011
- if (!display.scrollbarsClipped && display.scroller.offsetWidth) {
4012
- display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.clientWidth;
4013
- display.heightForcer.style.height = scrollGap(cm) + "px";
4014
- display.sizer.style.marginBottom = -display.nativeBarWidth + "px";
4015
- display.sizer.style.borderRightWidth = scrollGap(cm) + "px";
4016
- display.scrollbarsClipped = true;
4017
- }
4018
- }
4019
-
4020
- function selectionSnapshot(cm) {
4021
- if (cm.hasFocus()) { return null }
4022
- var active = activeElt();
4023
- if (!active || !contains(cm.display.lineDiv, active)) { return null }
4024
- var result = {activeElt: active};
4025
- if (window.getSelection) {
4026
- var sel = window.getSelection();
4027
- if (sel.anchorNode && sel.extend && contains(cm.display.lineDiv, sel.anchorNode)) {
4028
- result.anchorNode = sel.anchorNode;
4029
- result.anchorOffset = sel.anchorOffset;
4030
- result.focusNode = sel.focusNode;
4031
- result.focusOffset = sel.focusOffset;
4032
- }
4033
- }
4034
- return result
4035
- }
4036
-
4037
- function restoreSelection(snapshot) {
4038
- if (!snapshot || !snapshot.activeElt || snapshot.activeElt == activeElt()) { return }
4039
- snapshot.activeElt.focus();
4040
- if (snapshot.anchorNode && contains(document.body, snapshot.anchorNode) && contains(document.body, snapshot.focusNode)) {
4041
- var sel = window.getSelection(), range$$1 = document.createRange();
4042
- range$$1.setEnd(snapshot.anchorNode, snapshot.anchorOffset);
4043
- range$$1.collapse(false);
4044
- sel.removeAllRanges();
4045
- sel.addRange(range$$1);
4046
- sel.extend(snapshot.focusNode, snapshot.focusOffset);
4047
- }
4048
- }
4049
-
4050
- // Does the actual updating of the line display. Bails out
4051
- // (returning false) when there is nothing to be done and forced is
4052
- // false.
4053
- function updateDisplayIfNeeded(cm, update) {
4054
- var display = cm.display, doc = cm.doc;
4055
-
4056
- if (update.editorIsHidden) {
4057
- resetView(cm);
4058
- return false
4059
- }
4060
-
4061
- // Bail out if the visible area is already rendered and nothing changed.
4062
- if (!update.force &&
4063
- update.visible.from >= display.viewFrom && update.visible.to <= display.viewTo &&
4064
- (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo) &&
4065
- display.renderedView == display.view && countDirtyView(cm) == 0)
4066
- { return false }
4067
-
4068
- if (maybeUpdateLineNumberWidth(cm)) {
4069
- resetView(cm);
4070
- update.dims = getDimensions(cm);
4071
- }
4072
-
4073
- // Compute a suitable new viewport (from & to)
4074
- var end = doc.first + doc.size;
4075
- var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first);
4076
- var to = Math.min(end, update.visible.to + cm.options.viewportMargin);
4077
- if (display.viewFrom < from && from - display.viewFrom < 20) { from = Math.max(doc.first, display.viewFrom); }
4078
- if (display.viewTo > to && display.viewTo - to < 20) { to = Math.min(end, display.viewTo); }
4079
- if (sawCollapsedSpans) {
4080
- from = visualLineNo(cm.doc, from);
4081
- to = visualLineEndNo(cm.doc, to);
4082
- }
4083
-
4084
- var different = from != display.viewFrom || to != display.viewTo ||
4085
- display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != update.wrapperWidth;
4086
- adjustView(cm, from, to);
4087
-
4088
- display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom));
4089
- // Position the mover div to align with the current scroll position
4090
- cm.display.mover.style.top = display.viewOffset + "px";
4091
-
4092
- var toUpdate = countDirtyView(cm);
4093
- if (!different && toUpdate == 0 && !update.force && display.renderedView == display.view &&
4094
- (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo))
4095
- { return false }
4096
-
4097
- // For big changes, we hide the enclosing element during the
4098
- // update, since that speeds up the operations on most browsers.
4099
- var selSnapshot = selectionSnapshot(cm);
4100
- if (toUpdate > 4) { display.lineDiv.style.display = "none"; }
4101
- patchDisplay(cm, display.updateLineNumbers, update.dims);
4102
- if (toUpdate > 4) { display.lineDiv.style.display = ""; }
4103
- display.renderedView = display.view;
4104
- // There might have been a widget with a focused element that got
4105
- // hidden or updated, if so re-focus it.
4106
- restoreSelection(selSnapshot);
4107
-
4108
- // Prevent selection and cursors from interfering with the scroll
4109
- // width and height.
4110
- removeChildren(display.cursorDiv);
4111
- removeChildren(display.selectionDiv);
4112
- display.gutters.style.height = display.sizer.style.minHeight = 0;
4113
-
4114
- if (different) {
4115
- display.lastWrapHeight = update.wrapperHeight;
4116
- display.lastWrapWidth = update.wrapperWidth;
4117
- startWorker(cm, 400);
4118
- }
4119
-
4120
- display.updateLineNumbers = null;
4121
-
4122
- return true
4123
- }
4124
-
4125
- function postUpdateDisplay(cm, update) {
4126
- var viewport = update.viewport;
4127
-
4128
- for (var first = true;; first = false) {
4129
- if (!first || !cm.options.lineWrapping || update.oldDisplayWidth == displayWidth(cm)) {
4130
- // Clip forced viewport to actual scrollable area.
4131
- if (viewport && viewport.top != null)
4132
- { viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - displayHeight(cm), viewport.top)}; }
4133
- // Updated line heights might result in the drawn area not
4134
- // actually covering the viewport. Keep looping until it does.
4135
- update.visible = visibleLines(cm.display, cm.doc, viewport);
4136
- if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo)
4137
- { break }
4138
- }
4139
- if (!updateDisplayIfNeeded(cm, update)) { break }
4140
- updateHeightsInViewport(cm);
4141
- var barMeasure = measureForScrollbars(cm);
4142
- updateSelection(cm);
4143
- updateScrollbars(cm, barMeasure);
4144
- setDocumentHeight(cm, barMeasure);
4145
- update.force = false;
4146
- }
4147
-
4148
- update.signal(cm, "update", cm);
4149
- if (cm.display.viewFrom != cm.display.reportedViewFrom || cm.display.viewTo != cm.display.reportedViewTo) {
4150
- update.signal(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo);
4151
- cm.display.reportedViewFrom = cm.display.viewFrom; cm.display.reportedViewTo = cm.display.viewTo;
4152
- }
4153
- }
4154
-
4155
- function updateDisplaySimple(cm, viewport) {
4156
- var update = new DisplayUpdate(cm, viewport);
4157
- if (updateDisplayIfNeeded(cm, update)) {
4158
- updateHeightsInViewport(cm);
4159
- postUpdateDisplay(cm, update);
4160
- var barMeasure = measureForScrollbars(cm);
4161
- updateSelection(cm);
4162
- updateScrollbars(cm, barMeasure);
4163
- setDocumentHeight(cm, barMeasure);
4164
- update.finish();
4165
- }
4166
- }
4167
-
4168
- // Sync the actual display DOM structure with display.view, removing
4169
- // nodes for lines that are no longer in view, and creating the ones
4170
- // that are not there yet, and updating the ones that are out of
4171
- // date.
4172
- function patchDisplay(cm, updateNumbersFrom, dims) {
4173
- var display = cm.display, lineNumbers = cm.options.lineNumbers;
4174
- var container = display.lineDiv, cur = container.firstChild;
4175
-
4176
- function rm(node) {
4177
- var next = node.nextSibling;
4178
- // Works around a throw-scroll bug in OS X Webkit
4179
- if (webkit && mac && cm.display.currentWheelTarget == node)
4180
- { node.style.display = "none"; }
4181
- else
4182
- { node.parentNode.removeChild(node); }
4183
- return next
4184
- }
4185
-
4186
- var view = display.view, lineN = display.viewFrom;
4187
- // Loop over the elements in the view, syncing cur (the DOM nodes
4188
- // in display.lineDiv) with the view as we go.
4189
- for (var i = 0; i < view.length; i++) {
4190
- var lineView = view[i];
4191
- if (lineView.hidden) ; else if (!lineView.node || lineView.node.parentNode != container) { // Not drawn yet
4192
- var node = buildLineElement(cm, lineView, lineN, dims);
4193
- container.insertBefore(node, cur);
4194
- } else { // Already drawn
4195
- while (cur != lineView.node) { cur = rm(cur); }
4196
- var updateNumber = lineNumbers && updateNumbersFrom != null &&
4197
- updateNumbersFrom <= lineN && lineView.lineNumber;
4198
- if (lineView.changes) {
4199
- if (indexOf(lineView.changes, "gutter") > -1) { updateNumber = false; }
4200
- updateLineForChanges(cm, lineView, lineN, dims);
4201
- }
4202
- if (updateNumber) {
4203
- removeChildren(lineView.lineNumber);
4204
- lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm.options, lineN)));
4205
- }
4206
- cur = lineView.node.nextSibling;
4207
- }
4208
- lineN += lineView.size;
4209
- }
4210
- while (cur) { cur = rm(cur); }
4211
- }
4212
-
4213
- function updateGutterSpace(display) {
4214
- var width = display.gutters.offsetWidth;
4215
- display.sizer.style.marginLeft = width + "px";
4216
- }
4217
-
4218
- function setDocumentHeight(cm, measure) {
4219
- cm.display.sizer.style.minHeight = measure.docHeight + "px";
4220
- cm.display.heightForcer.style.top = measure.docHeight + "px";
4221
- cm.display.gutters.style.height = (measure.docHeight + cm.display.barHeight + scrollGap(cm)) + "px";
4222
- }
4223
-
4224
- // Re-align line numbers and gutter marks to compensate for
4225
- // horizontal scrolling.
4226
- function alignHorizontally(cm) {
4227
- var display = cm.display, view = display.view;
4228
- if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) { return }
4229
- var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft;
4230
- var gutterW = display.gutters.offsetWidth, left = comp + "px";
4231
- for (var i = 0; i < view.length; i++) { if (!view[i].hidden) {
4232
- if (cm.options.fixedGutter) {
4233
- if (view[i].gutter)
4234
- { view[i].gutter.style.left = left; }
4235
- if (view[i].gutterBackground)
4236
- { view[i].gutterBackground.style.left = left; }
4237
- }
4238
- var align = view[i].alignable;
4239
- if (align) { for (var j = 0; j < align.length; j++)
4240
- { align[j].style.left = left; } }
4241
- } }
4242
- if (cm.options.fixedGutter)
4243
- { display.gutters.style.left = (comp + gutterW) + "px"; }
4244
- }
4245
-
4246
- // Used to ensure that the line number gutter is still the right
4247
- // size for the current document size. Returns true when an update
4248
- // is needed.
4249
- function maybeUpdateLineNumberWidth(cm) {
4250
- if (!cm.options.lineNumbers) { return false }
4251
- var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display;
4252
- if (last.length != display.lineNumChars) {
4253
- var test = display.measure.appendChild(elt("div", [elt("div", last)],
4254
- "CodeMirror-linenumber CodeMirror-gutter-elt"));
4255
- var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW;
4256
- display.lineGutter.style.width = "";
4257
- display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding) + 1;
4258
- display.lineNumWidth = display.lineNumInnerWidth + padding;
4259
- display.lineNumChars = display.lineNumInnerWidth ? last.length : -1;
4260
- display.lineGutter.style.width = display.lineNumWidth + "px";
4261
- updateGutterSpace(cm.display);
4262
- return true
4263
- }
4264
- return false
4265
- }
4266
-
4267
- function getGutters(gutters, lineNumbers) {
4268
- var result = [], sawLineNumbers = false;
4269
- for (var i = 0; i < gutters.length; i++) {
4270
- var name = gutters[i], style = null;
4271
- if (typeof name != "string") { style = name.style; name = name.className; }
4272
- if (name == "CodeMirror-linenumbers") {
4273
- if (!lineNumbers) { continue }
4274
- else { sawLineNumbers = true; }
4275
- }
4276
- result.push({className: name, style: style});
4277
- }
4278
- if (lineNumbers && !sawLineNumbers) { result.push({className: "CodeMirror-linenumbers", style: null}); }
4279
- return result
4280
- }
4281
-
4282
- // Rebuild the gutter elements, ensure the margin to the left of the
4283
- // code matches their width.
4284
- function renderGutters(display) {
4285
- var gutters = display.gutters, specs = display.gutterSpecs;
4286
- removeChildren(gutters);
4287
- display.lineGutter = null;
4288
- for (var i = 0; i < specs.length; ++i) {
4289
- var ref = specs[i];
4290
- var className = ref.className;
4291
- var style = ref.style;
4292
- var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + className));
4293
- if (style) { gElt.style.cssText = style; }
4294
- if (className == "CodeMirror-linenumbers") {
4295
- display.lineGutter = gElt;
4296
- gElt.style.width = (display.lineNumWidth || 1) + "px";
4297
- }
4298
- }
4299
- gutters.style.display = specs.length ? "" : "none";
4300
- updateGutterSpace(display);
4301
- }
4302
-
4303
- function updateGutters(cm) {
4304
- renderGutters(cm.display);
4305
- regChange(cm);
4306
- alignHorizontally(cm);
4307
- }
4308
-
4309
- // The display handles the DOM integration, both for input reading
4310
- // and content drawing. It holds references to DOM nodes and
4311
- // display-related state.
4312
-
4313
- function Display(place, doc, input, options) {
4314
- var d = this;
4315
- this.input = input;
4316
-
4317
- // Covers bottom-right square when both scrollbars are present.
4318
- d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler");
4319
- d.scrollbarFiller.setAttribute("cm-not-content", "true");
4320
- // Covers bottom of gutter when coverGutterNextToScrollbar is on
4321
- // and h scrollbar is present.
4322
- d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler");
4323
- d.gutterFiller.setAttribute("cm-not-content", "true");
4324
- // Will contain the actual code, positioned to cover the viewport.
4325
- d.lineDiv = eltP("div", null, "CodeMirror-code");
4326
- // Elements are added to these to represent selection and cursors.
4327
- d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1");
4328
- d.cursorDiv = elt("div", null, "CodeMirror-cursors");
4329
- // A visibility: hidden element used to find the size of things.
4330
- d.measure = elt("div", null, "CodeMirror-measure");
4331
- // When lines outside of the viewport are measured, they are drawn in this.
4332
- d.lineMeasure = elt("div", null, "CodeMirror-measure");
4333
- // Wraps everything that needs to exist inside the vertically-padded coordinate system
4334
- d.lineSpace = eltP("div", [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv],
4335
- null, "position: relative; outline: none");
4336
- var lines = eltP("div", [d.lineSpace], "CodeMirror-lines");
4337
- // Moved around its parent to cover visible view.
4338
- d.mover = elt("div", [lines], null, "position: relative");
4339
- // Set to the height of the document, allowing scrolling.
4340
- d.sizer = elt("div", [d.mover], "CodeMirror-sizer");
4341
- d.sizerWidth = null;
4342
- // Behavior of elts with overflow: auto and padding is
4343
- // inconsistent across browsers. This is used to ensure the
4344
- // scrollable area is big enough.
4345
- d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerGap + "px; width: 1px;");
4346
- // Will contain the gutters, if any.
4347
- d.gutters = elt("div", null, "CodeMirror-gutters");
4348
- d.lineGutter = null;
4349
- // Actual scrollable element.
4350
- d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-scroll");
4351
- d.scroller.setAttribute("tabIndex", "-1");
4352
- // The element in which the editor lives.
4353
- d.wrapper = elt("div", [d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror");
4354
-
4355
- // Work around IE7 z-index bug (not perfect, hence IE7 not really being supported)
4356
- if (ie && ie_version < 8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; }
4357
- if (!webkit && !(gecko && mobile)) { d.scroller.draggable = true; }
4358
-
4359
- if (place) {
4360
- if (place.appendChild) { place.appendChild(d.wrapper); }
4361
- else { place(d.wrapper); }
4362
- }
4363
-
4364
- // Current rendered range (may be bigger than the view window).
4365
- d.viewFrom = d.viewTo = doc.first;
4366
- d.reportedViewFrom = d.reportedViewTo = doc.first;
4367
- // Information about the rendered lines.
4368
- d.view = [];
4369
- d.renderedView = null;
4370
- // Holds info about a single rendered line when it was rendered
4371
- // for measurement, while not in view.
4372
- d.externalMeasured = null;
4373
- // Empty space (in pixels) above the view
4374
- d.viewOffset = 0;
4375
- d.lastWrapHeight = d.lastWrapWidth = 0;
4376
- d.updateLineNumbers = null;
4377
-
4378
- d.nativeBarWidth = d.barHeight = d.barWidth = 0;
4379
- d.scrollbarsClipped = false;
4380
-
4381
- // Used to only resize the line number gutter when necessary (when
4382
- // the amount of lines crosses a boundary that makes its width change)
4383
- d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null;
4384
- // Set to true when a non-horizontal-scrolling line widget is
4385
- // added. As an optimization, line widget aligning is skipped when
4386
- // this is false.
4387
- d.alignWidgets = false;
4388
-
4389
- d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;
4390
-
4391
- // Tracks the maximum line length so that the horizontal scrollbar
4392
- // can be kept static when scrolling.
4393
- d.maxLine = null;
4394
- d.maxLineLength = 0;
4395
- d.maxLineChanged = false;
4396
-
4397
- // Used for measuring wheel scrolling granularity
4398
- d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null;
4399
-
4400
- // True when shift is held down.
4401
- d.shift = false;
4402
-
4403
- // Used to track whether anything happened since the context menu
4404
- // was opened.
4405
- d.selForContextMenu = null;
4406
-
4407
- d.activeTouch = null;
4408
-
4409
- d.gutterSpecs = getGutters(options.gutters, options.lineNumbers);
4410
- renderGutters(d);
4411
-
4412
- input.init(d);
4413
- }
4414
-
4415
- // Since the delta values reported on mouse wheel events are
4416
- // unstandardized between browsers and even browser versions, and
4417
- // generally horribly unpredictable, this code starts by measuring
4418
- // the scroll effect that the first few mouse wheel events have,
4419
- // and, from that, detects the way it can convert deltas to pixel
4420
- // offsets afterwards.
4421
- //
4422
- // The reason we want to know the amount a wheel event will scroll
4423
- // is that it gives us a chance to update the display before the
4424
- // actual scrolling happens, reducing flickering.
4425
-
4426
- var wheelSamples = 0, wheelPixelsPerUnit = null;
4427
- // Fill in a browser-detected starting value on browsers where we
4428
- // know one. These don't have to be accurate -- the result of them
4429
- // being wrong would just be a slight flicker on the first wheel
4430
- // scroll (if it is large enough).
4431
- if (ie) { wheelPixelsPerUnit = -.53; }
4432
- else if (gecko) { wheelPixelsPerUnit = 15; }
4433
- else if (chrome) { wheelPixelsPerUnit = -.7; }
4434
- else if (safari) { wheelPixelsPerUnit = -1/3; }
4435
-
4436
- function wheelEventDelta(e) {
4437
- var dx = e.wheelDeltaX, dy = e.wheelDeltaY;
4438
- if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) { dx = e.detail; }
4439
- if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) { dy = e.detail; }
4440
- else if (dy == null) { dy = e.wheelDelta; }
4441
- return {x: dx, y: dy}
4442
- }
4443
- function wheelEventPixels(e) {
4444
- var delta = wheelEventDelta(e);
4445
- delta.x *= wheelPixelsPerUnit;
4446
- delta.y *= wheelPixelsPerUnit;
4447
- return delta
4448
- }
4449
-
4450
- function onScrollWheel(cm, e) {
4451
- var delta = wheelEventDelta(e), dx = delta.x, dy = delta.y;
4452
-
4453
- var display = cm.display, scroll = display.scroller;
4454
- // Quit if there's nothing to scroll here
4455
- var canScrollX = scroll.scrollWidth > scroll.clientWidth;
4456
- var canScrollY = scroll.scrollHeight > scroll.clientHeight;
4457
- if (!(dx && canScrollX || dy && canScrollY)) { return }
4458
-
4459
- // Webkit browsers on OS X abort momentum scrolls when the target
4460
- // of the scroll event is removed from the scrollable element.
4461
- // This hack (see related code in patchDisplay) makes sure the
4462
- // element is kept around.
4463
- if (dy && mac && webkit) {
4464
- outer: for (var cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) {
4465
- for (var i = 0; i < view.length; i++) {
4466
- if (view[i].node == cur) {
4467
- cm.display.currentWheelTarget = cur;
4468
- break outer
4469
- }
4470
- }
4471
- }
4472
- }
4473
-
4474
- // On some browsers, horizontal scrolling will cause redraws to
4475
- // happen before the gutter has been realigned, causing it to
4476
- // wriggle around in a most unseemly way. When we have an
4477
- // estimated pixels/delta value, we just handle horizontal
4478
- // scrolling entirely here. It'll be slightly off from native, but
4479
- // better than glitching out.
4480
- if (dx && !gecko && !presto && wheelPixelsPerUnit != null) {
4481
- if (dy && canScrollY)
4482
- { updateScrollTop(cm, Math.max(0, scroll.scrollTop + dy * wheelPixelsPerUnit)); }
4483
- setScrollLeft(cm, Math.max(0, scroll.scrollLeft + dx * wheelPixelsPerUnit));
4484
- // Only prevent default scrolling if vertical scrolling is
4485
- // actually possible. Otherwise, it causes vertical scroll
4486
- // jitter on OSX trackpads when deltaX is small and deltaY
4487
- // is large (issue #3579)
4488
- if (!dy || (dy && canScrollY))
4489
- { e_preventDefault(e); }
4490
- display.wheelStartX = null; // Abort measurement, if in progress
4491
- return
4492
- }
4493
-
4494
- // 'Project' the visible viewport to cover the area that is being
4495
- // scrolled into view (if we know enough to estimate it).
4496
- if (dy && wheelPixelsPerUnit != null) {
4497
- var pixels = dy * wheelPixelsPerUnit;
4498
- var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight;
4499
- if (pixels < 0) { top = Math.max(0, top + pixels - 50); }
4500
- else { bot = Math.min(cm.doc.height, bot + pixels + 50); }
4501
- updateDisplaySimple(cm, {top: top, bottom: bot});
4502
- }
4503
-
4504
- if (wheelSamples < 20) {
4505
- if (display.wheelStartX == null) {
4506
- display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop;
4507
- display.wheelDX = dx; display.wheelDY = dy;
4508
- setTimeout(function () {
4509
- if (display.wheelStartX == null) { return }
4510
- var movedX = scroll.scrollLeft - display.wheelStartX;
4511
- var movedY = scroll.scrollTop - display.wheelStartY;
4512
- var sample = (movedY && display.wheelDY && movedY / display.wheelDY) ||
4513
- (movedX && display.wheelDX && movedX / display.wheelDX);
4514
- display.wheelStartX = display.wheelStartY = null;
4515
- if (!sample) { return }
4516
- wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1);
4517
- ++wheelSamples;
4518
- }, 200);
4519
- } else {
4520
- display.wheelDX += dx; display.wheelDY += dy;
4521
- }
4522
- }
4523
- }
4524
-
4525
- // Selection objects are immutable. A new one is created every time
4526
- // the selection changes. A selection is one or more non-overlapping
4527
- // (and non-touching) ranges, sorted, and an integer that indicates
4528
- // which one is the primary selection (the one that's scrolled into
4529
- // view, that getCursor returns, etc).
4530
- var Selection = function(ranges, primIndex) {
4531
- this.ranges = ranges;
4532
- this.primIndex = primIndex;
4533
- };
4534
-
4535
- Selection.prototype.primary = function () { return this.ranges[this.primIndex] };
4536
-
4537
- Selection.prototype.equals = function (other) {
4538
- var this$1 = this;
4539
-
4540
- if (other == this) { return true }
4541
- if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) { return false }
4542
- for (var i = 0; i < this.ranges.length; i++) {
4543
- var here = this$1.ranges[i], there = other.ranges[i];
4544
- if (!equalCursorPos(here.anchor, there.anchor) || !equalCursorPos(here.head, there.head)) { return false }
4545
- }
4546
- return true
4547
- };
4548
-
4549
- Selection.prototype.deepCopy = function () {
4550
- var this$1 = this;
4551
-
4552
- var out = [];
4553
- for (var i = 0; i < this.ranges.length; i++)
4554
- { out[i] = new Range(copyPos(this$1.ranges[i].anchor), copyPos(this$1.ranges[i].head)); }
4555
- return new Selection(out, this.primIndex)
4556
- };
4557
-
4558
- Selection.prototype.somethingSelected = function () {
4559
- var this$1 = this;
4560
-
4561
- for (var i = 0; i < this.ranges.length; i++)
4562
- { if (!this$1.ranges[i].empty()) { return true } }
4563
- return false
4564
- };
4565
-
4566
- Selection.prototype.contains = function (pos, end) {
4567
- var this$1 = this;
4568
-
4569
- if (!end) { end = pos; }
4570
- for (var i = 0; i < this.ranges.length; i++) {
4571
- var range = this$1.ranges[i];
4572
- if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0)
4573
- { return i }
4574
- }
4575
- return -1
4576
- };
4577
-
4578
- var Range = function(anchor, head) {
4579
- this.anchor = anchor; this.head = head;
4580
- };
4581
-
4582
- Range.prototype.from = function () { return minPos(this.anchor, this.head) };
4583
- Range.prototype.to = function () { return maxPos(this.anchor, this.head) };
4584
- Range.prototype.empty = function () { return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch };
4585
-
4586
- // Take an unsorted, potentially overlapping set of ranges, and
4587
- // build a selection out of it. 'Consumes' ranges array (modifying
4588
- // it).
4589
- function normalizeSelection(cm, ranges, primIndex) {
4590
- var mayTouch = cm && cm.options.selectionsMayTouch;
4591
- var prim = ranges[primIndex];
4592
- ranges.sort(function (a, b) { return cmp(a.from(), b.from()); });
4593
- primIndex = indexOf(ranges, prim);
4594
- for (var i = 1; i < ranges.length; i++) {
4595
- var cur = ranges[i], prev = ranges[i - 1];
4596
- var diff = cmp(prev.to(), cur.from());
4597
- if (mayTouch && !cur.empty() ? diff > 0 : diff >= 0) {
4598
- var from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to());
4599
- var inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head;
4600
- if (i <= primIndex) { --primIndex; }
4601
- ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to));
4602
- }
4603
- }
4604
- return new Selection(ranges, primIndex)
4605
- }
4606
-
4607
- function simpleSelection(anchor, head) {
4608
- return new Selection([new Range(anchor, head || anchor)], 0)
4609
- }
4610
-
4611
- // Compute the position of the end of a change (its 'to' property
4612
- // refers to the pre-change end).
4613
- function changeEnd(change) {
4614
- if (!change.text) { return change.to }
4615
- return Pos(change.from.line + change.text.length - 1,
4616
- lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0))
4617
- }
4618
-
4619
- // Adjust a position to refer to the post-change position of the
4620
- // same text, or the end of the change if the change covers it.
4621
- function adjustForChange(pos, change) {
4622
- if (cmp(pos, change.from) < 0) { return pos }
4623
- if (cmp(pos, change.to) <= 0) { return changeEnd(change) }
4624
-
4625
- var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch;
4626
- if (pos.line == change.to.line) { ch += changeEnd(change).ch - change.to.ch; }
4627
- return Pos(line, ch)
4628
- }
4629
-
4630
- function computeSelAfterChange(doc, change) {
4631
- var out = [];
4632
- for (var i = 0; i < doc.sel.ranges.length; i++) {
4633
- var range = doc.sel.ranges[i];
4634
- out.push(new Range(adjustForChange(range.anchor, change),
4635
- adjustForChange(range.head, change)));
4636
- }
4637
- return normalizeSelection(doc.cm, out, doc.sel.primIndex)
4638
- }
4639
-
4640
- function offsetPos(pos, old, nw) {
4641
- if (pos.line == old.line)
4642
- { return Pos(nw.line, pos.ch - old.ch + nw.ch) }
4643
- else
4644
- { return Pos(nw.line + (pos.line - old.line), pos.ch) }
4645
- }
4646
-
4647
- // Used by replaceSelections to allow moving the selection to the
4648
- // start or around the replaced test. Hint may be "start" or "around".
4649
- function computeReplacedSel(doc, changes, hint) {
4650
- var out = [];
4651
- var oldPrev = Pos(doc.first, 0), newPrev = oldPrev;
4652
- for (var i = 0; i < changes.length; i++) {
4653
- var change = changes[i];
4654
- var from = offsetPos(change.from, oldPrev, newPrev);
4655
- var to = offsetPos(changeEnd(change), oldPrev, newPrev);
4656
- oldPrev = change.to;
4657
- newPrev = to;
4658
- if (hint == "around") {
4659
- var range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) < 0;
4660
- out[i] = new Range(inv ? to : from, inv ? from : to);
4661
- } else {
4662
- out[i] = new Range(from, from);
4663
- }
4664
- }
4665
- return new Selection(out, doc.sel.primIndex)
4666
- }
4667
-
4668
- // Used to get the editor into a consistent state again when options change.
4669
-
4670
- function loadMode(cm) {
4671
- cm.doc.mode = getMode(cm.options, cm.doc.modeOption);
4672
- resetModeState(cm);
4673
- }
4674
-
4675
- function resetModeState(cm) {
4676
- cm.doc.iter(function (line) {
4677
- if (line.stateAfter) { line.stateAfter = null; }
4678
- if (line.styles) { line.styles = null; }
4679
- });
4680
- cm.doc.modeFrontier = cm.doc.highlightFrontier = cm.doc.first;
4681
- startWorker(cm, 100);
4682
- cm.state.modeGen++;
4683
- if (cm.curOp) { regChange(cm); }
4684
- }
4685
-
4686
- // DOCUMENT DATA STRUCTURE
4687
-
4688
- // By default, updates that start and end at the beginning of a line
4689
- // are treated specially, in order to make the association of line
4690
- // widgets and marker elements with the text behave more intuitive.
4691
- function isWholeLineUpdate(doc, change) {
4692
- return change.from.ch == 0 && change.to.ch == 0 && lst(change.text) == "" &&
4693
- (!doc.cm || doc.cm.options.wholeLineUpdateBefore)
4694
- }
4695
-
4696
- // Perform a change on the document data structure.
4697
- function updateDoc(doc, change, markedSpans, estimateHeight$$1) {
4698
- function spansFor(n) {return markedSpans ? markedSpans[n] : null}
4699
- function update(line, text, spans) {
4700
- updateLine(line, text, spans, estimateHeight$$1);
4701
- signalLater(line, "change", line, change);
4702
- }
4703
- function linesFor(start, end) {
4704
- var result = [];
4705
- for (var i = start; i < end; ++i)
4706
- { result.push(new Line(text[i], spansFor(i), estimateHeight$$1)); }
4707
- return result
4708
- }
4709
-
4710
- var from = change.from, to = change.to, text = change.text;
4711
- var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line);
4712
- var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line;
4713
-
4714
- // Adjust the line structure
4715
- if (change.full) {
4716
- doc.insert(0, linesFor(0, text.length));
4717
- doc.remove(text.length, doc.size - text.length);
4718
- } else if (isWholeLineUpdate(doc, change)) {
4719
- // This is a whole-line replace. Treated specially to make
4720
- // sure line objects move the way they are supposed to.
4721
- var added = linesFor(0, text.length - 1);
4722
- update(lastLine, lastLine.text, lastSpans);
4723
- if (nlines) { doc.remove(from.line, nlines); }
4724
- if (added.length) { doc.insert(from.line, added); }
4725
- } else if (firstLine == lastLine) {
4726
- if (text.length == 1) {
4727
- update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans);
4728
- } else {
4729
- var added$1 = linesFor(1, text.length - 1);
4730
- added$1.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight$$1));
4731
- update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
4732
- doc.insert(from.line + 1, added$1);
4733
- }
4734
- } else if (text.length == 1) {
4735
- update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0));
4736
- doc.remove(from.line + 1, nlines);
4737
- } else {
4738
- update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
4739
- update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans);
4740
- var added$2 = linesFor(1, text.length - 1);
4741
- if (nlines > 1) { doc.remove(from.line + 1, nlines - 1); }
4742
- doc.insert(from.line + 1, added$2);
4743
- }
4744
-
4745
- signalLater(doc, "change", doc, change);
4746
- }
4747
-
4748
- // Call f for all linked documents.
4749
- function linkedDocs(doc, f, sharedHistOnly) {
4750
- function propagate(doc, skip, sharedHist) {
4751
- if (doc.linked) { for (var i = 0; i < doc.linked.length; ++i) {
4752
- var rel = doc.linked[i];
4753
- if (rel.doc == skip) { continue }
4754
- var shared = sharedHist && rel.sharedHist;
4755
- if (sharedHistOnly && !shared) { continue }
4756
- f(rel.doc, shared);
4757
- propagate(rel.doc, doc, shared);
4758
- } }
4759
- }
4760
- propagate(doc, null, true);
4761
- }
4762
-
4763
- // Attach a document to an editor.
4764
- function attachDoc(cm, doc) {
4765
- if (doc.cm) { throw new Error("This document is already in use.") }
4766
- cm.doc = doc;
4767
- doc.cm = cm;
4768
- estimateLineHeights(cm);
4769
- loadMode(cm);
4770
- setDirectionClass(cm);
4771
- if (!cm.options.lineWrapping) { findMaxLine(cm); }
4772
- cm.options.mode = doc.modeOption;
4773
- regChange(cm);
4774
- }
4775
-
4776
- function setDirectionClass(cm) {
4777
- (cm.doc.direction == "rtl" ? addClass : rmClass)(cm.display.lineDiv, "CodeMirror-rtl");
4778
- }
4779
-
4780
- function directionChanged(cm) {
4781
- runInOp(cm, function () {
4782
- setDirectionClass(cm);
4783
- regChange(cm);
4784
- });
4785
- }
4786
-
4787
- function History(startGen) {
4788
- // Arrays of change events and selections. Doing something adds an
4789
- // event to done and clears undo. Undoing moves events from done
4790
- // to undone, redoing moves them in the other direction.
4791
- this.done = []; this.undone = [];
4792
- this.undoDepth = Infinity;
4793
- // Used to track when changes can be merged into a single undo
4794
- // event
4795
- this.lastModTime = this.lastSelTime = 0;
4796
- this.lastOp = this.lastSelOp = null;
4797
- this.lastOrigin = this.lastSelOrigin = null;
4798
- // Used by the isClean() method
4799
- this.generation = this.maxGeneration = startGen || 1;
4800
- }
4801
-
4802
- // Create a history change event from an updateDoc-style change
4803
- // object.
4804
- function historyChangeFromChange(doc, change) {
4805
- var histChange = {from: copyPos(change.from), to: changeEnd(change), text: getBetween(doc, change.from, change.to)};
4806
- attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);
4807
- linkedDocs(doc, function (doc) { return attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1); }, true);
4808
- return histChange
4809
- }
4810
-
4811
- // Pop all selection events off the end of a history array. Stop at
4812
- // a change event.
4813
- function clearSelectionEvents(array) {
4814
- while (array.length) {
4815
- var last = lst(array);
4816
- if (last.ranges) { array.pop(); }
4817
- else { break }
4818
- }
4819
- }
4820
-
4821
- // Find the top change event in the history. Pop off selection
4822
- // events that are in the way.
4823
- function lastChangeEvent(hist, force) {
4824
- if (force) {
4825
- clearSelectionEvents(hist.done);
4826
- return lst(hist.done)
4827
- } else if (hist.done.length && !lst(hist.done).ranges) {
4828
- return lst(hist.done)
4829
- } else if (hist.done.length > 1 && !hist.done[hist.done.length - 2].ranges) {
4830
- hist.done.pop();
4831
- return lst(hist.done)
4832
- }
4833
- }
4834
-
4835
- // Register a change in the history. Merges changes that are within
4836
- // a single operation, or are close together with an origin that
4837
- // allows merging (starting with "+") into a single event.
4838
- function addChangeToHistory(doc, change, selAfter, opId) {
4839
- var hist = doc.history;
4840
- hist.undone.length = 0;
4841
- var time = +new Date, cur;
4842
- var last;
4843
-
4844
- if ((hist.lastOp == opId ||
4845
- hist.lastOrigin == change.origin && change.origin &&
4846
- ((change.origin.charAt(0) == "+" && hist.lastModTime > time - (doc.cm ? doc.cm.options.historyEventDelay : 500)) ||
4847
- change.origin.charAt(0) == "*")) &&
4848
- (cur = lastChangeEvent(hist, hist.lastOp == opId))) {
4849
- // Merge this change into the last event
4850
- last = lst(cur.changes);
4851
- if (cmp(change.from, change.to) == 0 && cmp(change.from, last.to) == 0) {
4852
- // Optimized case for simple insertion -- don't want to add
4853
- // new changesets for every character typed
4854
- last.to = changeEnd(change);
4855
- } else {
4856
- // Add new sub-event
4857
- cur.changes.push(historyChangeFromChange(doc, change));
4858
- }
4859
- } else {
4860
- // Can not be merged, start a new event.
4861
- var before = lst(hist.done);
4862
- if (!before || !before.ranges)
4863
- { pushSelectionToHistory(doc.sel, hist.done); }
4864
- cur = {changes: [historyChangeFromChange(doc, change)],
4865
- generation: hist.generation};
4866
- hist.done.push(cur);
4867
- while (hist.done.length > hist.undoDepth) {
4868
- hist.done.shift();
4869
- if (!hist.done[0].ranges) { hist.done.shift(); }
4870
- }
4871
- }
4872
- hist.done.push(selAfter);
4873
- hist.generation = ++hist.maxGeneration;
4874
- hist.lastModTime = hist.lastSelTime = time;
4875
- hist.lastOp = hist.lastSelOp = opId;
4876
- hist.lastOrigin = hist.lastSelOrigin = change.origin;
4877
-
4878
- if (!last) { signal(doc, "historyAdded"); }
4879
- }
4880
-
4881
- function selectionEventCanBeMerged(doc, origin, prev, sel) {
4882
- var ch = origin.charAt(0);
4883
- return ch == "*" ||
4884
- ch == "+" &&
4885
- prev.ranges.length == sel.ranges.length &&
4886
- prev.somethingSelected() == sel.somethingSelected() &&
4887
- new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEventDelay : 500)
4888
- }
4889
-
4890
- // Called whenever the selection changes, sets the new selection as
4891
- // the pending selection in the history, and pushes the old pending
4892
- // selection into the 'done' array when it was significantly
4893
- // different (in number of selected ranges, emptiness, or time).
4894
- function addSelectionToHistory(doc, sel, opId, options) {
4895
- var hist = doc.history, origin = options && options.origin;
4896
-
4897
- // A new event is started when the previous origin does not match
4898
- // the current, or the origins don't allow matching. Origins
4899
- // starting with * are always merged, those starting with + are
4900
- // merged when similar and close together in time.
4901
- if (opId == hist.lastSelOp ||
4902
- (origin && hist.lastSelOrigin == origin &&
4903
- (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin ||
4904
- selectionEventCanBeMerged(doc, origin, lst(hist.done), sel))))
4905
- { hist.done[hist.done.length - 1] = sel; }
4906
- else
4907
- { pushSelectionToHistory(sel, hist.done); }
4908
-
4909
- hist.lastSelTime = +new Date;
4910
- hist.lastSelOrigin = origin;
4911
- hist.lastSelOp = opId;
4912
- if (options && options.clearRedo !== false)
4913
- { clearSelectionEvents(hist.undone); }
4914
- }
4915
-
4916
- function pushSelectionToHistory(sel, dest) {
4917
- var top = lst(dest);
4918
- if (!(top && top.ranges && top.equals(sel)))
4919
- { dest.push(sel); }
4920
- }
4921
-
4922
- // Used to store marked span information in the history.
4923
- function attachLocalSpans(doc, change, from, to) {
4924
- var existing = change["spans_" + doc.id], n = 0;
4925
- doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function (line) {
4926
- if (line.markedSpans)
4927
- { (existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans; }
4928
- ++n;
4929
- });
4930
- }
4931
-
4932
- // When un/re-doing restores text containing marked spans, those
4933
- // that have been explicitly cleared should not be restored.
4934
- function removeClearedSpans(spans) {
4935
- if (!spans) { return null }
4936
- var out;
4937
- for (var i = 0; i < spans.length; ++i) {
4938
- if (spans[i].marker.explicitlyCleared) { if (!out) { out = spans.slice(0, i); } }
4939
- else if (out) { out.push(spans[i]); }
4940
- }
4941
- return !out ? spans : out.length ? out : null
4942
- }
4943
-
4944
- // Retrieve and filter the old marked spans stored in a change event.
4945
- function getOldSpans(doc, change) {
4946
- var found = change["spans_" + doc.id];
4947
- if (!found) { return null }
4948
- var nw = [];
4949
- for (var i = 0; i < change.text.length; ++i)
4950
- { nw.push(removeClearedSpans(found[i])); }
4951
- return nw
4952
- }
4953
-
4954
- // Used for un/re-doing changes from the history. Combines the
4955
- // result of computing the existing spans with the set of spans that
4956
- // existed in the history (so that deleting around a span and then
4957
- // undoing brings back the span).
4958
- function mergeOldSpans(doc, change) {
4959
- var old = getOldSpans(doc, change);
4960
- var stretched = stretchSpansOverChange(doc, change);
4961
- if (!old) { return stretched }
4962
- if (!stretched) { return old }
4963
-
4964
- for (var i = 0; i < old.length; ++i) {
4965
- var oldCur = old[i], stretchCur = stretched[i];
4966
- if (oldCur && stretchCur) {
4967
- spans: for (var j = 0; j < stretchCur.length; ++j) {
4968
- var span = stretchCur[j];
4969
- for (var k = 0; k < oldCur.length; ++k)
4970
- { if (oldCur[k].marker == span.marker) { continue spans } }
4971
- oldCur.push(span);
4972
- }
4973
- } else if (stretchCur) {
4974
- old[i] = stretchCur;
4975
- }
4976
- }
4977
- return old
4978
- }
4979
-
4980
- // Used both to provide a JSON-safe object in .getHistory, and, when
4981
- // detaching a document, to split the history in two
4982
- function copyHistoryArray(events, newGroup, instantiateSel) {
4983
- var copy = [];
4984
- for (var i = 0; i < events.length; ++i) {
4985
- var event = events[i];
4986
- if (event.ranges) {
4987
- copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event);
4988
- continue
4989
- }
4990
- var changes = event.changes, newChanges = [];
4991
- copy.push({changes: newChanges});
4992
- for (var j = 0; j < changes.length; ++j) {
4993
- var change = changes[j], m = (void 0);
4994
- newChanges.push({from: change.from, to: change.to, text: change.text});
4995
- if (newGroup) { for (var prop in change) { if (m = prop.match(/^spans_(\d+)$/)) {
4996
- if (indexOf(newGroup, Number(m[1])) > -1) {
4997
- lst(newChanges)[prop] = change[prop];
4998
- delete change[prop];
4999
- }
5000
- } } }
5001
- }
5002
- }
5003
- return copy
5004
- }
5005
-
5006
- // The 'scroll' parameter given to many of these indicated whether
5007
- // the new cursor position should be scrolled into view after
5008
- // modifying the selection.
5009
-
5010
- // If shift is held or the extend flag is set, extends a range to
5011
- // include a given position (and optionally a second position).
5012
- // Otherwise, simply returns the range between the given positions.
5013
- // Used for cursor motion and such.
5014
- function extendRange(range, head, other, extend) {
5015
- if (extend) {
5016
- var anchor = range.anchor;
5017
- if (other) {
5018
- var posBefore = cmp(head, anchor) < 0;
5019
- if (posBefore != (cmp(other, anchor) < 0)) {
5020
- anchor = head;
5021
- head = other;
5022
- } else if (posBefore != (cmp(head, other) < 0)) {
5023
- head = other;
5024
- }
5025
- }
5026
- return new Range(anchor, head)
5027
- } else {
5028
- return new Range(other || head, head)
5029
- }
5030
- }
5031
-
5032
- // Extend the primary selection range, discard the rest.
5033
- function extendSelection(doc, head, other, options, extend) {
5034
- if (extend == null) { extend = doc.cm && (doc.cm.display.shift || doc.extend); }
5035
- setSelection(doc, new Selection([extendRange(doc.sel.primary(), head, other, extend)], 0), options);
5036
- }
5037
-
5038
- // Extend all selections (pos is an array of selections with length
5039
- // equal the number of selections)
5040
- function extendSelections(doc, heads, options) {
5041
- var out = [];
5042
- var extend = doc.cm && (doc.cm.display.shift || doc.extend);
5043
- for (var i = 0; i < doc.sel.ranges.length; i++)
5044
- { out[i] = extendRange(doc.sel.ranges[i], heads[i], null, extend); }
5045
- var newSel = normalizeSelection(doc.cm, out, doc.sel.primIndex);
5046
- setSelection(doc, newSel, options);
5047
- }
5048
-
5049
- // Updates a single range in the selection.
5050
- function replaceOneSelection(doc, i, range, options) {
5051
- var ranges = doc.sel.ranges.slice(0);
5052
- ranges[i] = range;
5053
- setSelection(doc, normalizeSelection(doc.cm, ranges, doc.sel.primIndex), options);
5054
- }
5055
-
5056
- // Reset the selection to a single range.
5057
- function setSimpleSelection(doc, anchor, head, options) {
5058
- setSelection(doc, simpleSelection(anchor, head), options);
5059
- }
5060
-
5061
- // Give beforeSelectionChange handlers a change to influence a
5062
- // selection update.
5063
- function filterSelectionChange(doc, sel, options) {
5064
- var obj = {
5065
- ranges: sel.ranges,
5066
- update: function(ranges) {
5067
- var this$1 = this;
5068
-
5069
- this.ranges = [];
5070
- for (var i = 0; i < ranges.length; i++)
5071
- { this$1.ranges[i] = new Range(clipPos(doc, ranges[i].anchor),
5072
- clipPos(doc, ranges[i].head)); }
5073
- },
5074
- origin: options && options.origin
5075
- };
5076
- signal(doc, "beforeSelectionChange", doc, obj);
5077
- if (doc.cm) { signal(doc.cm, "beforeSelectionChange", doc.cm, obj); }
5078
- if (obj.ranges != sel.ranges) { return normalizeSelection(doc.cm, obj.ranges, obj.ranges.length - 1) }
5079
- else { return sel }
5080
- }
5081
-
5082
- function setSelectionReplaceHistory(doc, sel, options) {
5083
- var done = doc.history.done, last = lst(done);
5084
- if (last && last.ranges) {
5085
- done[done.length - 1] = sel;
5086
- setSelectionNoUndo(doc, sel, options);
5087
- } else {
5088
- setSelection(doc, sel, options);
5089
- }
5090
- }
5091
-
5092
- // Set a new selection.
5093
- function setSelection(doc, sel, options) {
5094
- setSelectionNoUndo(doc, sel, options);
5095
- addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options);
5096
- }
5097
-
5098
- function setSelectionNoUndo(doc, sel, options) {
5099
- if (hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange"))
5100
- { sel = filterSelectionChange(doc, sel, options); }
5101
-
5102
- var bias = options && options.bias ||
5103
- (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1);
5104
- setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true));
5105
-
5106
- if (!(options && options.scroll === false) && doc.cm)
5107
- { ensureCursorVisible(doc.cm); }
5108
- }
5109
-
5110
- function setSelectionInner(doc, sel) {
5111
- if (sel.equals(doc.sel)) { return }
5112
-
5113
- doc.sel = sel;
5114
-
5115
- if (doc.cm) {
5116
- doc.cm.curOp.updateInput = 1;
5117
- doc.cm.curOp.selectionChanged = true;
5118
- signalCursorActivity(doc.cm);
5119
- }
5120
- signalLater(doc, "cursorActivity", doc);
5121
- }
5122
-
5123
- // Verify that the selection does not partially select any atomic
5124
- // marked ranges.
5125
- function reCheckSelection(doc) {
5126
- setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false));
5127
- }
5128
-
5129
- // Return a selection that does not partially select any atomic
5130
- // ranges.
5131
- function skipAtomicInSelection(doc, sel, bias, mayClear) {
5132
- var out;
5133
- for (var i = 0; i < sel.ranges.length; i++) {
5134
- var range = sel.ranges[i];
5135
- var old = sel.ranges.length == doc.sel.ranges.length && doc.sel.ranges[i];
5136
- var newAnchor = skipAtomic(doc, range.anchor, old && old.anchor, bias, mayClear);
5137
- var newHead = skipAtomic(doc, range.head, old && old.head, bias, mayClear);
5138
- if (out || newAnchor != range.anchor || newHead != range.head) {
5139
- if (!out) { out = sel.ranges.slice(0, i); }
5140
- out[i] = new Range(newAnchor, newHead);
5141
- }
5142
- }
5143
- return out ? normalizeSelection(doc.cm, out, sel.primIndex) : sel
5144
- }
5145
-
5146
- function skipAtomicInner(doc, pos, oldPos, dir, mayClear) {
5147
- var line = getLine(doc, pos.line);
5148
- if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) {
5149
- var sp = line.markedSpans[i], m = sp.marker;
5150
-
5151
- // Determine if we should prevent the cursor being placed to the left/right of an atomic marker
5152
- // Historically this was determined using the inclusiveLeft/Right option, but the new way to control it
5153
- // is with selectLeft/Right
5154
- var preventCursorLeft = ("selectLeft" in m) ? !m.selectLeft : m.inclusiveLeft;
5155
- var preventCursorRight = ("selectRight" in m) ? !m.selectRight : m.inclusiveRight;
5156
-
5157
- if ((sp.from == null || (preventCursorLeft ? sp.from <= pos.ch : sp.from < pos.ch)) &&
5158
- (sp.to == null || (preventCursorRight ? sp.to >= pos.ch : sp.to > pos.ch))) {
5159
- if (mayClear) {
5160
- signal(m, "beforeCursorEnter");
5161
- if (m.explicitlyCleared) {
5162
- if (!line.markedSpans) { break }
5163
- else {--i; continue}
5164
- }
5165
- }
5166
- if (!m.atomic) { continue }
5167
-
5168
- if (oldPos) {
5169
- var near = m.find(dir < 0 ? 1 : -1), diff = (void 0);
5170
- if (dir < 0 ? preventCursorRight : preventCursorLeft)
5171
- { near = movePos(doc, near, -dir, near && near.line == pos.line ? line : null); }
5172
- if (near && near.line == pos.line && (diff = cmp(near, oldPos)) && (dir < 0 ? diff < 0 : diff > 0))
5173
- { return skipAtomicInner(doc, near, pos, dir, mayClear) }
5174
- }
5175
-
5176
- var far = m.find(dir < 0 ? -1 : 1);
5177
- if (dir < 0 ? preventCursorLeft : preventCursorRight)
5178
- { far = movePos(doc, far, dir, far.line == pos.line ? line : null); }
5179
- return far ? skipAtomicInner(doc, far, pos, dir, mayClear) : null
5180
- }
5181
- } }
5182
- return pos
5183
- }
5184
-
5185
- // Ensure a given position is not inside an atomic range.
5186
- function skipAtomic(doc, pos, oldPos, bias, mayClear) {
5187
- var dir = bias || 1;
5188
- var found = skipAtomicInner(doc, pos, oldPos, dir, mayClear) ||
5189
- (!mayClear && skipAtomicInner(doc, pos, oldPos, dir, true)) ||
5190
- skipAtomicInner(doc, pos, oldPos, -dir, mayClear) ||
5191
- (!mayClear && skipAtomicInner(doc, pos, oldPos, -dir, true));
5192
- if (!found) {
5193
- doc.cantEdit = true;
5194
- return Pos(doc.first, 0)
5195
- }
5196
- return found
5197
- }
5198
-
5199
- function movePos(doc, pos, dir, line) {
5200
- if (dir < 0 && pos.ch == 0) {
5201
- if (pos.line > doc.first) { return clipPos(doc, Pos(pos.line - 1)) }
5202
- else { return null }
5203
- } else if (dir > 0 && pos.ch == (line || getLine(doc, pos.line)).text.length) {
5204
- if (pos.line < doc.first + doc.size - 1) { return Pos(pos.line + 1, 0) }
5205
- else { return null }
5206
- } else {
5207
- return new Pos(pos.line, pos.ch + dir)
5208
- }
5209
- }
5210
-
5211
- function selectAll(cm) {
5212
- cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll);
5213
- }
5214
-
5215
- // UPDATING
5216
-
5217
- // Allow "beforeChange" event handlers to influence a change
5218
- function filterChange(doc, change, update) {
5219
- var obj = {
5220
- canceled: false,
5221
- from: change.from,
5222
- to: change.to,
5223
- text: change.text,
5224
- origin: change.origin,
5225
- cancel: function () { return obj.canceled = true; }
5226
- };
5227
- if (update) { obj.update = function (from, to, text, origin) {
5228
- if (from) { obj.from = clipPos(doc, from); }
5229
- if (to) { obj.to = clipPos(doc, to); }
5230
- if (text) { obj.text = text; }
5231
- if (origin !== undefined) { obj.origin = origin; }
5232
- }; }
5233
- signal(doc, "beforeChange", doc, obj);
5234
- if (doc.cm) { signal(doc.cm, "beforeChange", doc.cm, obj); }
5235
-
5236
- if (obj.canceled) {
5237
- if (doc.cm) { doc.cm.curOp.updateInput = 2; }
5238
- return null
5239
- }
5240
- return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin}
5241
- }
5242
-
5243
- // Apply a change to a document, and add it to the document's
5244
- // history, and propagating it to all linked documents.
5245
- function makeChange(doc, change, ignoreReadOnly) {
5246
- if (doc.cm) {
5247
- if (!doc.cm.curOp) { return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly) }
5248
- if (doc.cm.state.suppressEdits) { return }
5249
- }
5250
-
5251
- if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) {
5252
- change = filterChange(doc, change, true);
5253
- if (!change) { return }
5254
- }
5255
-
5256
- // Possibly split or suppress the update based on the presence
5257
- // of read-only spans in its range.
5258
- var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to);
5259
- if (split) {
5260
- for (var i = split.length - 1; i >= 0; --i)
5261
- { makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [""] : change.text, origin: change.origin}); }
5262
- } else {
5263
- makeChangeInner(doc, change);
5264
- }
5265
- }
5266
-
5267
- function makeChangeInner(doc, change) {
5268
- if (change.text.length == 1 && change.text[0] == "" && cmp(change.from, change.to) == 0) { return }
5269
- var selAfter = computeSelAfterChange(doc, change);
5270
- addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN);
5271
-
5272
- makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change));
5273
- var rebased = [];
5274
-
5275
- linkedDocs(doc, function (doc, sharedHist) {
5276
- if (!sharedHist && indexOf(rebased, doc.history) == -1) {
5277
- rebaseHist(doc.history, change);
5278
- rebased.push(doc.history);
5279
- }
5280
- makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change));
5281
- });
5282
- }
5283
-
5284
- // Revert a change stored in a document's history.
5285
- function makeChangeFromHistory(doc, type, allowSelectionOnly) {
5286
- var suppress = doc.cm && doc.cm.state.suppressEdits;
5287
- if (suppress && !allowSelectionOnly) { return }
5288
-
5289
- var hist = doc.history, event, selAfter = doc.sel;
5290
- var source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done;
5291
-
5292
- // Verify that there is a useable event (so that ctrl-z won't
5293
- // needlessly clear selection events)
5294
- var i = 0;
5295
- for (; i < source.length; i++) {
5296
- event = source[i];
5297
- if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ranges)
5298
- { break }
5299
- }
5300
- if (i == source.length) { return }
5301
- hist.lastOrigin = hist.lastSelOrigin = null;
5302
-
5303
- for (;;) {
5304
- event = source.pop();
5305
- if (event.ranges) {
5306
- pushSelectionToHistory(event, dest);
5307
- if (allowSelectionOnly && !event.equals(doc.sel)) {
5308
- setSelection(doc, event, {clearRedo: false});
5309
- return
5310
- }
5311
- selAfter = event;
5312
- } else if (suppress) {
5313
- source.push(event);
5314
- return
5315
- } else { break }
5316
- }
5317
-
5318
- // Build up a reverse change object to add to the opposite history
5319
- // stack (redo when undoing, and vice versa).
5320
- var antiChanges = [];
5321
- pushSelectionToHistory(selAfter, dest);
5322
- dest.push({changes: antiChanges, generation: hist.generation});
5323
- hist.generation = event.generation || ++hist.maxGeneration;
5324
-
5325
- var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange");
5326
-
5327
- var loop = function ( i ) {
5328
- var change = event.changes[i];
5329
- change.origin = type;
5330
- if (filter && !filterChange(doc, change, false)) {
5331
- source.length = 0;
5332
- return {}
5333
- }
5334
-
5335
- antiChanges.push(historyChangeFromChange(doc, change));
5336
-
5337
- var after = i ? computeSelAfterChange(doc, change) : lst(source);
5338
- makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change));
5339
- if (!i && doc.cm) { doc.cm.scrollIntoView({from: change.from, to: changeEnd(change)}); }
5340
- var rebased = [];
5341
-
5342
- // Propagate to the linked documents
5343
- linkedDocs(doc, function (doc, sharedHist) {
5344
- if (!sharedHist && indexOf(rebased, doc.history) == -1) {
5345
- rebaseHist(doc.history, change);
5346
- rebased.push(doc.history);
5347
- }
5348
- makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change));
5349
- });
5350
- };
5351
-
5352
- for (var i$1 = event.changes.length - 1; i$1 >= 0; --i$1) {
5353
- var returned = loop( i$1 );
5354
-
5355
- if ( returned ) return returned.v;
5356
- }
5357
- }
5358
-
5359
- // Sub-views need their line numbers shifted when text is added
5360
- // above or below them in the parent document.
5361
- function shiftDoc(doc, distance) {
5362
- if (distance == 0) { return }
5363
- doc.first += distance;
5364
- doc.sel = new Selection(map(doc.sel.ranges, function (range) { return new Range(
5365
- Pos(range.anchor.line + distance, range.anchor.ch),
5366
- Pos(range.head.line + distance, range.head.ch)
5367
- ); }), doc.sel.primIndex);
5368
- if (doc.cm) {
5369
- regChange(doc.cm, doc.first, doc.first - distance, distance);
5370
- for (var d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++)
5371
- { regLineChange(doc.cm, l, "gutter"); }
5372
- }
5373
- }
5374
-
5375
- // More lower-level change function, handling only a single document
5376
- // (not linked ones).
5377
- function makeChangeSingleDoc(doc, change, selAfter, spans) {
5378
- if (doc.cm && !doc.cm.curOp)
5379
- { return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans) }
5380
-
5381
- if (change.to.line < doc.first) {
5382
- shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line));
5383
- return
5384
- }
5385
- if (change.from.line > doc.lastLine()) { return }
5386
-
5387
- // Clip the change to the size of this doc
5388
- if (change.from.line < doc.first) {
5389
- var shift = change.text.length - 1 - (doc.first - change.from.line);
5390
- shiftDoc(doc, shift);
5391
- change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch),
5392
- text: [lst(change.text)], origin: change.origin};
5393
- }
5394
- var last = doc.lastLine();
5395
- if (change.to.line > last) {
5396
- change = {from: change.from, to: Pos(last, getLine(doc, last).text.length),
5397
- text: [change.text[0]], origin: change.origin};
5398
- }
5399
-
5400
- change.removed = getBetween(doc, change.from, change.to);
5401
-
5402
- if (!selAfter) { selAfter = computeSelAfterChange(doc, change); }
5403
- if (doc.cm) { makeChangeSingleDocInEditor(doc.cm, change, spans); }
5404
- else { updateDoc(doc, change, spans); }
5405
- setSelectionNoUndo(doc, selAfter, sel_dontScroll);
5406
- }
5407
-
5408
- // Handle the interaction of a change to a document with the editor
5409
- // that this document is part of.
5410
- function makeChangeSingleDocInEditor(cm, change, spans) {
5411
- var doc = cm.doc, display = cm.display, from = change.from, to = change.to;
5412
-
5413
- var recomputeMaxLength = false, checkWidthStart = from.line;
5414
- if (!cm.options.lineWrapping) {
5415
- checkWidthStart = lineNo(visualLine(getLine(doc, from.line)));
5416
- doc.iter(checkWidthStart, to.line + 1, function (line) {
5417
- if (line == display.maxLine) {
5418
- recomputeMaxLength = true;
5419
- return true
5420
- }
5421
- });
5422
- }
5423
-
5424
- if (doc.sel.contains(change.from, change.to) > -1)
5425
- { signalCursorActivity(cm); }
5426
-
5427
- updateDoc(doc, change, spans, estimateHeight(cm));
5428
-
5429
- if (!cm.options.lineWrapping) {
5430
- doc.iter(checkWidthStart, from.line + change.text.length, function (line) {
5431
- var len = lineLength(line);
5432
- if (len > display.maxLineLength) {
5433
- display.maxLine = line;
5434
- display.maxLineLength = len;
5435
- display.maxLineChanged = true;
5436
- recomputeMaxLength = false;
5437
- }
5438
- });
5439
- if (recomputeMaxLength) { cm.curOp.updateMaxLine = true; }
5440
- }
5441
-
5442
- retreatFrontier(doc, from.line);
5443
- startWorker(cm, 400);
5444
-
5445
- var lendiff = change.text.length - (to.line - from.line) - 1;
5446
- // Remember that these lines changed, for updating the display
5447
- if (change.full)
5448
- { regChange(cm); }
5449
- else if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change))
5450
- { regLineChange(cm, from.line, "text"); }
5451
- else
5452
- { regChange(cm, from.line, to.line + 1, lendiff); }
5453
-
5454
- var changesHandler = hasHandler(cm, "changes"), changeHandler = hasHandler(cm, "change");
5455
- if (changeHandler || changesHandler) {
5456
- var obj = {
5457
- from: from, to: to,
5458
- text: change.text,
5459
- removed: change.removed,
5460
- origin: change.origin
5461
- };
5462
- if (changeHandler) { signalLater(cm, "change", cm, obj); }
5463
- if (changesHandler) { (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj); }
5464
- }
5465
- cm.display.selForContextMenu = null;
5466
- }
5467
-
5468
- function replaceRange(doc, code, from, to, origin) {
5469
- var assign;
5470
-
5471
- if (!to) { to = from; }
5472
- if (cmp(to, from) < 0) { (assign = [to, from], from = assign[0], to = assign[1]); }
5473
- if (typeof code == "string") { code = doc.splitLines(code); }
5474
- makeChange(doc, {from: from, to: to, text: code, origin: origin});
5475
- }
5476
-
5477
- // Rebasing/resetting history to deal with externally-sourced changes
5478
-
5479
- function rebaseHistSelSingle(pos, from, to, diff) {
5480
- if (to < pos.line) {
5481
- pos.line += diff;
5482
- } else if (from < pos.line) {
5483
- pos.line = from;
5484
- pos.ch = 0;
5485
- }
5486
- }
5487
-
5488
- // Tries to rebase an array of history events given a change in the
5489
- // document. If the change touches the same lines as the event, the
5490
- // event, and everything 'behind' it, is discarded. If the change is
5491
- // before the event, the event's positions are updated. Uses a
5492
- // copy-on-write scheme for the positions, to avoid having to
5493
- // reallocate them all on every rebase, but also avoid problems with
5494
- // shared position objects being unsafely updated.
5495
- function rebaseHistArray(array, from, to, diff) {
5496
- for (var i = 0; i < array.length; ++i) {
5497
- var sub = array[i], ok = true;
5498
- if (sub.ranges) {
5499
- if (!sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied = true; }
5500
- for (var j = 0; j < sub.ranges.length; j++) {
5501
- rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff);
5502
- rebaseHistSelSingle(sub.ranges[j].head, from, to, diff);
5503
- }
5504
- continue
5505
- }
5506
- for (var j$1 = 0; j$1 < sub.changes.length; ++j$1) {
5507
- var cur = sub.changes[j$1];
5508
- if (to < cur.from.line) {
5509
- cur.from = Pos(cur.from.line + diff, cur.from.ch);
5510
- cur.to = Pos(cur.to.line + diff, cur.to.ch);
5511
- } else if (from <= cur.to.line) {
5512
- ok = false;
5513
- break
5514
- }
5515
- }
5516
- if (!ok) {
5517
- array.splice(0, i + 1);
5518
- i = 0;
5519
- }
5520
- }
5521
- }
5522
-
5523
- function rebaseHist(hist, change) {
5524
- var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1;
5525
- rebaseHistArray(hist.done, from, to, diff);
5526
- rebaseHistArray(hist.undone, from, to, diff);
5527
- }
5528
-
5529
- // Utility for applying a change to a line by handle or number,
5530
- // returning the number and optionally registering the line as
5531
- // changed.
5532
- function changeLine(doc, handle, changeType, op) {
5533
- var no = handle, line = handle;
5534
- if (typeof handle == "number") { line = getLine(doc, clipLine(doc, handle)); }
5535
- else { no = lineNo(handle); }
5536
- if (no == null) { return null }
5537
- if (op(line, no) && doc.cm) { regLineChange(doc.cm, no, changeType); }
5538
- return line
5539
- }
5540
-
5541
- // The document is represented as a BTree consisting of leaves, with
5542
- // chunk of lines in them, and branches, with up to ten leaves or
5543
- // other branch nodes below them. The top node is always a branch
5544
- // node, and is the document object itself (meaning it has
5545
- // additional methods and properties).
5546
- //
5547
- // All nodes have parent links. The tree is used both to go from
5548
- // line numbers to line objects, and to go from objects to numbers.
5549
- // It also indexes by height, and is used to convert between height
5550
- // and line object, and to find the total height of the document.
5551
- //
5552
- // See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html
5553
-
5554
- function LeafChunk(lines) {
5555
- var this$1 = this;
5556
-
5557
- this.lines = lines;
5558
- this.parent = null;
5559
- var height = 0;
5560
- for (var i = 0; i < lines.length; ++i) {
5561
- lines[i].parent = this$1;
5562
- height += lines[i].height;
5563
- }
5564
- this.height = height;
5565
- }
5566
-
5567
- LeafChunk.prototype = {
5568
- chunkSize: function() { return this.lines.length },
5569
-
5570
- // Remove the n lines at offset 'at'.
5571
- removeInner: function(at, n) {
5572
- var this$1 = this;
5573
-
5574
- for (var i = at, e = at + n; i < e; ++i) {
5575
- var line = this$1.lines[i];
5576
- this$1.height -= line.height;
5577
- cleanUpLine(line);
5578
- signalLater(line, "delete");
5579
- }
5580
- this.lines.splice(at, n);
5581
- },
5582
-
5583
- // Helper used to collapse a small branch into a single leaf.
5584
- collapse: function(lines) {
5585
- lines.push.apply(lines, this.lines);
5586
- },
5587
-
5588
- // Insert the given array of lines at offset 'at', count them as
5589
- // having the given height.
5590
- insertInner: function(at, lines, height) {
5591
- var this$1 = this;
5592
-
5593
- this.height += height;
5594
- this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at));
5595
- for (var i = 0; i < lines.length; ++i) { lines[i].parent = this$1; }
5596
- },
5597
-
5598
- // Used to iterate over a part of the tree.
5599
- iterN: function(at, n, op) {
5600
- var this$1 = this;
5601
-
5602
- for (var e = at + n; at < e; ++at)
5603
- { if (op(this$1.lines[at])) { return true } }
5604
- }
5605
- };
5606
-
5607
- function BranchChunk(children) {
5608
- var this$1 = this;
5609
-
5610
- this.children = children;
5611
- var size = 0, height = 0;
5612
- for (var i = 0; i < children.length; ++i) {
5613
- var ch = children[i];
5614
- size += ch.chunkSize(); height += ch.height;
5615
- ch.parent = this$1;
5616
- }
5617
- this.size = size;
5618
- this.height = height;
5619
- this.parent = null;
5620
- }
5621
-
5622
- BranchChunk.prototype = {
5623
- chunkSize: function() { return this.size },
5624
-
5625
- removeInner: function(at, n) {
5626
- var this$1 = this;
5627
-
5628
- this.size -= n;
5629
- for (var i = 0; i < this.children.length; ++i) {
5630
- var child = this$1.children[i], sz = child.chunkSize();
5631
- if (at < sz) {
5632
- var rm = Math.min(n, sz - at), oldHeight = child.height;
5633
- child.removeInner(at, rm);
5634
- this$1.height -= oldHeight - child.height;
5635
- if (sz == rm) { this$1.children.splice(i--, 1); child.parent = null; }
5636
- if ((n -= rm) == 0) { break }
5637
- at = 0;
5638
- } else { at -= sz; }
5639
- }
5640
- // If the result is smaller than 25 lines, ensure that it is a
5641
- // single leaf node.
5642
- if (this.size - n < 25 &&
5643
- (this.children.length > 1 || !(this.children[0] instanceof LeafChunk))) {
5644
- var lines = [];
5645
- this.collapse(lines);
5646
- this.children = [new LeafChunk(lines)];
5647
- this.children[0].parent = this;
5648
- }
5649
- },
5650
-
5651
- collapse: function(lines) {
5652
- var this$1 = this;
5653
-
5654
- for (var i = 0; i < this.children.length; ++i) { this$1.children[i].collapse(lines); }
5655
- },
5656
-
5657
- insertInner: function(at, lines, height) {
5658
- var this$1 = this;
5659
-
5660
- this.size += lines.length;
5661
- this.height += height;
5662
- for (var i = 0; i < this.children.length; ++i) {
5663
- var child = this$1.children[i], sz = child.chunkSize();
5664
- if (at <= sz) {
5665
- child.insertInner(at, lines, height);
5666
- if (child.lines && child.lines.length > 50) {
5667
- // To avoid memory thrashing when child.lines is huge (e.g. first view of a large file), it's never spliced.
5668
- // Instead, small slices are taken. They're taken in order because sequential memory accesses are fastest.
5669
- var remaining = child.lines.length % 25 + 25;
5670
- for (var pos = remaining; pos < child.lines.length;) {
5671
- var leaf = new LeafChunk(child.lines.slice(pos, pos += 25));
5672
- child.height -= leaf.height;
5673
- this$1.children.splice(++i, 0, leaf);
5674
- leaf.parent = this$1;
5675
- }
5676
- child.lines = child.lines.slice(0, remaining);
5677
- this$1.maybeSpill();
5678
- }
5679
- break
5680
- }
5681
- at -= sz;
5682
- }
5683
- },
5684
-
5685
- // When a node has grown, check whether it should be split.
5686
- maybeSpill: function() {
5687
- if (this.children.length <= 10) { return }
5688
- var me = this;
5689
- do {
5690
- var spilled = me.children.splice(me.children.length - 5, 5);
5691
- var sibling = new BranchChunk(spilled);
5692
- if (!me.parent) { // Become the parent node
5693
- var copy = new BranchChunk(me.children);
5694
- copy.parent = me;
5695
- me.children = [copy, sibling];
5696
- me = copy;
5697
- } else {
5698
- me.size -= sibling.size;
5699
- me.height -= sibling.height;
5700
- var myIndex = indexOf(me.parent.children, me);
5701
- me.parent.children.splice(myIndex + 1, 0, sibling);
5702
- }
5703
- sibling.parent = me.parent;
5704
- } while (me.children.length > 10)
5705
- me.parent.maybeSpill();
5706
- },
5707
-
5708
- iterN: function(at, n, op) {
5709
- var this$1 = this;
5710
-
5711
- for (var i = 0; i < this.children.length; ++i) {
5712
- var child = this$1.children[i], sz = child.chunkSize();
5713
- if (at < sz) {
5714
- var used = Math.min(n, sz - at);
5715
- if (child.iterN(at, used, op)) { return true }
5716
- if ((n -= used) == 0) { break }
5717
- at = 0;
5718
- } else { at -= sz; }
5719
- }
5720
- }
5721
- };
5722
-
5723
- // Line widgets are block elements displayed above or below a line.
5724
-
5725
- var LineWidget = function(doc, node, options) {
5726
- var this$1 = this;
5727
-
5728
- if (options) { for (var opt in options) { if (options.hasOwnProperty(opt))
5729
- { this$1[opt] = options[opt]; } } }
5730
- this.doc = doc;
5731
- this.node = node;
5732
- };
5733
-
5734
- LineWidget.prototype.clear = function () {
5735
- var this$1 = this;
5736
-
5737
- var cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line);
5738
- if (no == null || !ws) { return }
5739
- for (var i = 0; i < ws.length; ++i) { if (ws[i] == this$1) { ws.splice(i--, 1); } }
5740
- if (!ws.length) { line.widgets = null; }
5741
- var height = widgetHeight(this);
5742
- updateLineHeight(line, Math.max(0, line.height - height));
5743
- if (cm) {
5744
- runInOp(cm, function () {
5745
- adjustScrollWhenAboveVisible(cm, line, -height);
5746
- regLineChange(cm, no, "widget");
5747
- });
5748
- signalLater(cm, "lineWidgetCleared", cm, this, no);
5749
- }
5750
- };
5751
-
5752
- LineWidget.prototype.changed = function () {
5753
- var this$1 = this;
5754
-
5755
- var oldH = this.height, cm = this.doc.cm, line = this.line;
5756
- this.height = null;
5757
- var diff = widgetHeight(this) - oldH;
5758
- if (!diff) { return }
5759
- if (!lineIsHidden(this.doc, line)) { updateLineHeight(line, line.height + diff); }
5760
- if (cm) {
5761
- runInOp(cm, function () {
5762
- cm.curOp.forceUpdate = true;
5763
- adjustScrollWhenAboveVisible(cm, line, diff);
5764
- signalLater(cm, "lineWidgetChanged", cm, this$1, lineNo(line));
5765
- });
5766
- }
5767
- };
5768
- eventMixin(LineWidget);
5769
-
5770
- function adjustScrollWhenAboveVisible(cm, line, diff) {
5771
- if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop))
5772
- { addToScrollTop(cm, diff); }
5773
- }
5774
-
5775
- function addLineWidget(doc, handle, node, options) {
5776
- var widget = new LineWidget(doc, node, options);
5777
- var cm = doc.cm;
5778
- if (cm && widget.noHScroll) { cm.display.alignWidgets = true; }
5779
- changeLine(doc, handle, "widget", function (line) {
5780
- var widgets = line.widgets || (line.widgets = []);
5781
- if (widget.insertAt == null) { widgets.push(widget); }
5782
- else { widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget); }
5783
- widget.line = line;
5784
- if (cm && !lineIsHidden(doc, line)) {
5785
- var aboveVisible = heightAtLine(line) < doc.scrollTop;
5786
- updateLineHeight(line, line.height + widgetHeight(widget));
5787
- if (aboveVisible) { addToScrollTop(cm, widget.height); }
5788
- cm.curOp.forceUpdate = true;
5789
- }
5790
- return true
5791
- });
5792
- if (cm) { signalLater(cm, "lineWidgetAdded", cm, widget, typeof handle == "number" ? handle : lineNo(handle)); }
5793
- return widget
5794
- }
5795
-
5796
- // TEXTMARKERS
5797
-
5798
- // Created with markText and setBookmark methods. A TextMarker is a
5799
- // handle that can be used to clear or find a marked position in the
5800
- // document. Line objects hold arrays (markedSpans) containing
5801
- // {from, to, marker} object pointing to such marker objects, and
5802
- // indicating that such a marker is present on that line. Multiple
5803
- // lines may point to the same marker when it spans across lines.
5804
- // The spans will have null for their from/to properties when the
5805
- // marker continues beyond the start/end of the line. Markers have
5806
- // links back to the lines they currently touch.
5807
-
5808
- // Collapsed markers have unique ids, in order to be able to order
5809
- // them, which is needed for uniquely determining an outer marker
5810
- // when they overlap (they may nest, but not partially overlap).
5811
- var nextMarkerId = 0;
5812
-
5813
- var TextMarker = function(doc, type) {
5814
- this.lines = [];
5815
- this.type = type;
5816
- this.doc = doc;
5817
- this.id = ++nextMarkerId;
5818
- };
5819
-
5820
- // Clear the marker.
5821
- TextMarker.prototype.clear = function () {
5822
- var this$1 = this;
5823
-
5824
- if (this.explicitlyCleared) { return }
5825
- var cm = this.doc.cm, withOp = cm && !cm.curOp;
5826
- if (withOp) { startOperation(cm); }
5827
- if (hasHandler(this, "clear")) {
5828
- var found = this.find();
5829
- if (found) { signalLater(this, "clear", found.from, found.to); }
5830
- }
5831
- var min = null, max = null;
5832
- for (var i = 0; i < this.lines.length; ++i) {
5833
- var line = this$1.lines[i];
5834
- var span = getMarkedSpanFor(line.markedSpans, this$1);
5835
- if (cm && !this$1.collapsed) { regLineChange(cm, lineNo(line), "text"); }
5836
- else if (cm) {
5837
- if (span.to != null) { max = lineNo(line); }
5838
- if (span.from != null) { min = lineNo(line); }
5839
- }
5840
- line.markedSpans = removeMarkedSpan(line.markedSpans, span);
5841
- if (span.from == null && this$1.collapsed && !lineIsHidden(this$1.doc, line) && cm)
5842
- { updateLineHeight(line, textHeight(cm.display)); }
5843
- }
5844
- if (cm && this.collapsed && !cm.options.lineWrapping) { for (var i$1 = 0; i$1 < this.lines.length; ++i$1) {
5845
- var visual = visualLine(this$1.lines[i$1]), len = lineLength(visual);
5846
- if (len > cm.display.maxLineLength) {
5847
- cm.display.maxLine = visual;
5848
- cm.display.maxLineLength = len;
5849
- cm.display.maxLineChanged = true;
5850
- }
5851
- } }
5852
-
5853
- if (min != null && cm && this.collapsed) { regChange(cm, min, max + 1); }
5854
- this.lines.length = 0;
5855
- this.explicitlyCleared = true;
5856
- if (this.atomic && this.doc.cantEdit) {
5857
- this.doc.cantEdit = false;
5858
- if (cm) { reCheckSelection(cm.doc); }
5859
- }
5860
- if (cm) { signalLater(cm, "markerCleared", cm, this, min, max); }
5861
- if (withOp) { endOperation(cm); }
5862
- if (this.parent) { this.parent.clear(); }
5863
- };
5864
-
5865
- // Find the position of the marker in the document. Returns a {from,
5866
- // to} object by default. Side can be passed to get a specific side
5867
- // -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the
5868
- // Pos objects returned contain a line object, rather than a line
5869
- // number (used to prevent looking up the same line twice).
5870
- TextMarker.prototype.find = function (side, lineObj) {
5871
- var this$1 = this;
5872
-
5873
- if (side == null && this.type == "bookmark") { side = 1; }
5874
- var from, to;
5875
- for (var i = 0; i < this.lines.length; ++i) {
5876
- var line = this$1.lines[i];
5877
- var span = getMarkedSpanFor(line.markedSpans, this$1);
5878
- if (span.from != null) {
5879
- from = Pos(lineObj ? line : lineNo(line), span.from);
5880
- if (side == -1) { return from }
5881
- }
5882
- if (span.to != null) {
5883
- to = Pos(lineObj ? line : lineNo(line), span.to);
5884
- if (side == 1) { return to }
5885
- }
5886
- }
5887
- return from && {from: from, to: to}
5888
- };
5889
-
5890
- // Signals that the marker's widget changed, and surrounding layout
5891
- // should be recomputed.
5892
- TextMarker.prototype.changed = function () {
5893
- var this$1 = this;
5894
-
5895
- var pos = this.find(-1, true), widget = this, cm = this.doc.cm;
5896
- if (!pos || !cm) { return }
5897
- runInOp(cm, function () {
5898
- var line = pos.line, lineN = lineNo(pos.line);
5899
- var view = findViewForLine(cm, lineN);
5900
- if (view) {
5901
- clearLineMeasurementCacheFor(view);
5902
- cm.curOp.selectionChanged = cm.curOp.forceUpdate = true;
5903
- }
5904
- cm.curOp.updateMaxLine = true;
5905
- if (!lineIsHidden(widget.doc, line) && widget.height != null) {
5906
- var oldHeight = widget.height;
5907
- widget.height = null;
5908
- var dHeight = widgetHeight(widget) - oldHeight;
5909
- if (dHeight)
5910
- { updateLineHeight(line, line.height + dHeight); }
5911
- }
5912
- signalLater(cm, "markerChanged", cm, this$1);
5913
- });
5914
- };
5915
-
5916
- TextMarker.prototype.attachLine = function (line) {
5917
- if (!this.lines.length && this.doc.cm) {
5918
- var op = this.doc.cm.curOp;
5919
- if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1)
5920
- { (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this); }
5921
- }
5922
- this.lines.push(line);
5923
- };
5924
-
5925
- TextMarker.prototype.detachLine = function (line) {
5926
- this.lines.splice(indexOf(this.lines, line), 1);
5927
- if (!this.lines.length && this.doc.cm) {
5928
- var op = this.doc.cm.curOp
5929
- ;(op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this);
5930
- }
5931
- };
5932
- eventMixin(TextMarker);
5933
-
5934
- // Create a marker, wire it up to the right lines, and
5935
- function markText(doc, from, to, options, type) {
5936
- // Shared markers (across linked documents) are handled separately
5937
- // (markTextShared will call out to this again, once per
5938
- // document).
5939
- if (options && options.shared) { return markTextShared(doc, from, to, options, type) }
5940
- // Ensure we are in an operation.
5941
- if (doc.cm && !doc.cm.curOp) { return operation(doc.cm, markText)(doc, from, to, options, type) }
5942
-
5943
- var marker = new TextMarker(doc, type), diff = cmp(from, to);
5944
- if (options) { copyObj(options, marker, false); }
5945
- // Don't connect empty markers unless clearWhenEmpty is false
5946
- if (diff > 0 || diff == 0 && marker.clearWhenEmpty !== false)
5947
- { return marker }
5948
- if (marker.replacedWith) {
5949
- // Showing up as a widget implies collapsed (widget replaces text)
5950
- marker.collapsed = true;
5951
- marker.widgetNode = eltP("span", [marker.replacedWith], "CodeMirror-widget");
5952
- if (!options.handleMouseEvents) { marker.widgetNode.setAttribute("cm-ignore-events", "true"); }
5953
- if (options.insertLeft) { marker.widgetNode.insertLeft = true; }
5954
- }
5955
- if (marker.collapsed) {
5956
- if (conflictingCollapsedRange(doc, from.line, from, to, marker) ||
5957
- from.line != to.line && conflictingCollapsedRange(doc, to.line, from, to, marker))
5958
- { throw new Error("Inserting collapsed marker partially overlapping an existing one") }
5959
- seeCollapsedSpans();
5960
- }
5961
-
5962
- if (marker.addToHistory)
5963
- { addChangeToHistory(doc, {from: from, to: to, origin: "markText"}, doc.sel, NaN); }
5964
-
5965
- var curLine = from.line, cm = doc.cm, updateMaxLine;
5966
- doc.iter(curLine, to.line + 1, function (line) {
5967
- if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) == cm.display.maxLine)
5968
- { updateMaxLine = true; }
5969
- if (marker.collapsed && curLine != from.line) { updateLineHeight(line, 0); }
5970
- addMarkedSpan(line, new MarkedSpan(marker,
5971
- curLine == from.line ? from.ch : null,
5972
- curLine == to.line ? to.ch : null));
5973
- ++curLine;
5974
- });
5975
- // lineIsHidden depends on the presence of the spans, so needs a second pass
5976
- if (marker.collapsed) { doc.iter(from.line, to.line + 1, function (line) {
5977
- if (lineIsHidden(doc, line)) { updateLineHeight(line, 0); }
5978
- }); }
5979
-
5980
- if (marker.clearOnEnter) { on(marker, "beforeCursorEnter", function () { return marker.clear(); }); }
5981
-
5982
- if (marker.readOnly) {
5983
- seeReadOnlySpans();
5984
- if (doc.history.done.length || doc.history.undone.length)
5985
- { doc.clearHistory(); }
5986
- }
5987
- if (marker.collapsed) {
5988
- marker.id = ++nextMarkerId;
5989
- marker.atomic = true;
5990
- }
5991
- if (cm) {
5992
- // Sync editor state
5993
- if (updateMaxLine) { cm.curOp.updateMaxLine = true; }
5994
- if (marker.collapsed)
5995
- { regChange(cm, from.line, to.line + 1); }
5996
- else if (marker.className || marker.startStyle || marker.endStyle || marker.css ||
5997
- marker.attributes || marker.title)
5998
- { for (var i = from.line; i <= to.line; i++) { regLineChange(cm, i, "text"); } }
5999
- if (marker.atomic) { reCheckSelection(cm.doc); }
6000
- signalLater(cm, "markerAdded", cm, marker);
6001
- }
6002
- return marker
6003
- }
6004
-
6005
- // SHARED TEXTMARKERS
6006
-
6007
- // A shared marker spans multiple linked documents. It is
6008
- // implemented as a meta-marker-object controlling multiple normal
6009
- // markers.
6010
- var SharedTextMarker = function(markers, primary) {
6011
- var this$1 = this;
6012
-
6013
- this.markers = markers;
6014
- this.primary = primary;
6015
- for (var i = 0; i < markers.length; ++i)
6016
- { markers[i].parent = this$1; }
6017
- };
6018
-
6019
- SharedTextMarker.prototype.clear = function () {
6020
- var this$1 = this;
6021
-
6022
- if (this.explicitlyCleared) { return }
6023
- this.explicitlyCleared = true;
6024
- for (var i = 0; i < this.markers.length; ++i)
6025
- { this$1.markers[i].clear(); }
6026
- signalLater(this, "clear");
6027
- };
6028
-
6029
- SharedTextMarker.prototype.find = function (side, lineObj) {
6030
- return this.primary.find(side, lineObj)
6031
- };
6032
- eventMixin(SharedTextMarker);
6033
-
6034
- function markTextShared(doc, from, to, options, type) {
6035
- options = copyObj(options);
6036
- options.shared = false;
6037
- var markers = [markText(doc, from, to, options, type)], primary = markers[0];
6038
- var widget = options.widgetNode;
6039
- linkedDocs(doc, function (doc) {
6040
- if (widget) { options.widgetNode = widget.cloneNode(true); }
6041
- markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type));
6042
- for (var i = 0; i < doc.linked.length; ++i)
6043
- { if (doc.linked[i].isParent) { return } }
6044
- primary = lst(markers);
6045
- });
6046
- return new SharedTextMarker(markers, primary)
6047
- }
6048
-
6049
- function findSharedMarkers(doc) {
6050
- return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())), function (m) { return m.parent; })
6051
- }
6052
-
6053
- function copySharedMarkers(doc, markers) {
6054
- for (var i = 0; i < markers.length; i++) {
6055
- var marker = markers[i], pos = marker.find();
6056
- var mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to);
6057
- if (cmp(mFrom, mTo)) {
6058
- var subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type);
6059
- marker.markers.push(subMark);
6060
- subMark.parent = marker;
6061
- }
6062
- }
6063
- }
6064
-
6065
- function detachSharedMarkers(markers) {
6066
- var loop = function ( i ) {
6067
- var marker = markers[i], linked = [marker.primary.doc];
6068
- linkedDocs(marker.primary.doc, function (d) { return linked.push(d); });
6069
- for (var j = 0; j < marker.markers.length; j++) {
6070
- var subMarker = marker.markers[j];
6071
- if (indexOf(linked, subMarker.doc) == -1) {
6072
- subMarker.parent = null;
6073
- marker.markers.splice(j--, 1);
6074
- }
6075
- }
6076
- };
6077
-
6078
- for (var i = 0; i < markers.length; i++) loop( i );
6079
- }
6080
-
6081
- var nextDocId = 0;
6082
- var Doc = function(text, mode, firstLine, lineSep, direction) {
6083
- if (!(this instanceof Doc)) { return new Doc(text, mode, firstLine, lineSep, direction) }
6084
- if (firstLine == null) { firstLine = 0; }
6085
-
6086
- BranchChunk.call(this, [new LeafChunk([new Line("", null)])]);
6087
- this.first = firstLine;
6088
- this.scrollTop = this.scrollLeft = 0;
6089
- this.cantEdit = false;
6090
- this.cleanGeneration = 1;
6091
- this.modeFrontier = this.highlightFrontier = firstLine;
6092
- var start = Pos(firstLine, 0);
6093
- this.sel = simpleSelection(start);
6094
- this.history = new History(null);
6095
- this.id = ++nextDocId;
6096
- this.modeOption = mode;
6097
- this.lineSep = lineSep;
6098
- this.direction = (direction == "rtl") ? "rtl" : "ltr";
6099
- this.extend = false;
6100
-
6101
- if (typeof text == "string") { text = this.splitLines(text); }
6102
- updateDoc(this, {from: start, to: start, text: text});
6103
- setSelection(this, simpleSelection(start), sel_dontScroll);
6104
- };
6105
-
6106
- Doc.prototype = createObj(BranchChunk.prototype, {
6107
- constructor: Doc,
6108
- // Iterate over the document. Supports two forms -- with only one
6109
- // argument, it calls that for each line in the document. With
6110
- // three, it iterates over the range given by the first two (with
6111
- // the second being non-inclusive).
6112
- iter: function(from, to, op) {
6113
- if (op) { this.iterN(from - this.first, to - from, op); }
6114
- else { this.iterN(this.first, this.first + this.size, from); }
6115
- },
6116
-
6117
- // Non-public interface for adding and removing lines.
6118
- insert: function(at, lines) {
6119
- var height = 0;
6120
- for (var i = 0; i < lines.length; ++i) { height += lines[i].height; }
6121
- this.insertInner(at - this.first, lines, height);
6122
- },
6123
- remove: function(at, n) { this.removeInner(at - this.first, n); },
6124
-
6125
- // From here, the methods are part of the public interface. Most
6126
- // are also available from CodeMirror (editor) instances.
6127
-
6128
- getValue: function(lineSep) {
6129
- var lines = getLines(this, this.first, this.first + this.size);
6130
- if (lineSep === false) { return lines }
6131
- return lines.join(lineSep || this.lineSeparator())
6132
- },
6133
- setValue: docMethodOp(function(code) {
6134
- var top = Pos(this.first, 0), last = this.first + this.size - 1;
6135
- makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length),
6136
- text: this.splitLines(code), origin: "setValue", full: true}, true);
6137
- if (this.cm) { scrollToCoords(this.cm, 0, 0); }
6138
- setSelection(this, simpleSelection(top), sel_dontScroll);
6139
- }),
6140
- replaceRange: function(code, from, to, origin) {
6141
- from = clipPos(this, from);
6142
- to = to ? clipPos(this, to) : from;
6143
- replaceRange(this, code, from, to, origin);
6144
- },
6145
- getRange: function(from, to, lineSep) {
6146
- var lines = getBetween(this, clipPos(this, from), clipPos(this, to));
6147
- if (lineSep === false) { return lines }
6148
- return lines.join(lineSep || this.lineSeparator())
6149
- },
6150
-
6151
- getLine: function(line) {var l = this.getLineHandle(line); return l && l.text},
6152
-
6153
- getLineHandle: function(line) {if (isLine(this, line)) { return getLine(this, line) }},
6154
- getLineNumber: function(line) {return lineNo(line)},
6155
-
6156
- getLineHandleVisualStart: function(line) {
6157
- if (typeof line == "number") { line = getLine(this, line); }
6158
- return visualLine(line)
6159
- },
6160
-
6161
- lineCount: function() {return this.size},
6162
- firstLine: function() {return this.first},
6163
- lastLine: function() {return this.first + this.size - 1},
6164
-
6165
- clipPos: function(pos) {return clipPos(this, pos)},
6166
-
6167
- getCursor: function(start) {
6168
- var range$$1 = this.sel.primary(), pos;
6169
- if (start == null || start == "head") { pos = range$$1.head; }
6170
- else if (start == "anchor") { pos = range$$1.anchor; }
6171
- else if (start == "end" || start == "to" || start === false) { pos = range$$1.to(); }
6172
- else { pos = range$$1.from(); }
6173
- return pos
6174
- },
6175
- listSelections: function() { return this.sel.ranges },
6176
- somethingSelected: function() {return this.sel.somethingSelected()},
6177
-
6178
- setCursor: docMethodOp(function(line, ch, options) {
6179
- setSimpleSelection(this, clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line), null, options);
6180
- }),
6181
- setSelection: docMethodOp(function(anchor, head, options) {
6182
- setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options);
6183
- }),
6184
- extendSelection: docMethodOp(function(head, other, options) {
6185
- extendSelection(this, clipPos(this, head), other && clipPos(this, other), options);
6186
- }),
6187
- extendSelections: docMethodOp(function(heads, options) {
6188
- extendSelections(this, clipPosArray(this, heads), options);
6189
- }),
6190
- extendSelectionsBy: docMethodOp(function(f, options) {
6191
- var heads = map(this.sel.ranges, f);
6192
- extendSelections(this, clipPosArray(this, heads), options);
6193
- }),
6194
- setSelections: docMethodOp(function(ranges, primary, options) {
6195
- var this$1 = this;
6196
-
6197
- if (!ranges.length) { return }
6198
- var out = [];
6199
- for (var i = 0; i < ranges.length; i++)
6200
- { out[i] = new Range(clipPos(this$1, ranges[i].anchor),
6201
- clipPos(this$1, ranges[i].head)); }
6202
- if (primary == null) { primary = Math.min(ranges.length - 1, this.sel.primIndex); }
6203
- setSelection(this, normalizeSelection(this.cm, out, primary), options);
6204
- }),
6205
- addSelection: docMethodOp(function(anchor, head, options) {
6206
- var ranges = this.sel.ranges.slice(0);
6207
- ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor)));
6208
- setSelection(this, normalizeSelection(this.cm, ranges, ranges.length - 1), options);
6209
- }),
6210
-
6211
- getSelection: function(lineSep) {
6212
- var this$1 = this;
6213
-
6214
- var ranges = this.sel.ranges, lines;
6215
- for (var i = 0; i < ranges.length; i++) {
6216
- var sel = getBetween(this$1, ranges[i].from(), ranges[i].to());
6217
- lines = lines ? lines.concat(sel) : sel;
6218
- }
6219
- if (lineSep === false) { return lines }
6220
- else { return lines.join(lineSep || this.lineSeparator()) }
6221
- },
6222
- getSelections: function(lineSep) {
6223
- var this$1 = this;
6224
-
6225
- var parts = [], ranges = this.sel.ranges;
6226
- for (var i = 0; i < ranges.length; i++) {
6227
- var sel = getBetween(this$1, ranges[i].from(), ranges[i].to());
6228
- if (lineSep !== false) { sel = sel.join(lineSep || this$1.lineSeparator()); }
6229
- parts[i] = sel;
6230
- }
6231
- return parts
6232
- },
6233
- replaceSelection: function(code, collapse, origin) {
6234
- var dup = [];
6235
- for (var i = 0; i < this.sel.ranges.length; i++)
6236
- { dup[i] = code; }
6237
- this.replaceSelections(dup, collapse, origin || "+input");
6238
- },
6239
- replaceSelections: docMethodOp(function(code, collapse, origin) {
6240
- var this$1 = this;
6241
-
6242
- var changes = [], sel = this.sel;
6243
- for (var i = 0; i < sel.ranges.length; i++) {
6244
- var range$$1 = sel.ranges[i];
6245
- changes[i] = {from: range$$1.from(), to: range$$1.to(), text: this$1.splitLines(code[i]), origin: origin};
6246
- }
6247
- var newSel = collapse && collapse != "end" && computeReplacedSel(this, changes, collapse);
6248
- for (var i$1 = changes.length - 1; i$1 >= 0; i$1--)
6249
- { makeChange(this$1, changes[i$1]); }
6250
- if (newSel) { setSelectionReplaceHistory(this, newSel); }
6251
- else if (this.cm) { ensureCursorVisible(this.cm); }
6252
- }),
6253
- undo: docMethodOp(function() {makeChangeFromHistory(this, "undo");}),
6254
- redo: docMethodOp(function() {makeChangeFromHistory(this, "redo");}),
6255
- undoSelection: docMethodOp(function() {makeChangeFromHistory(this, "undo", true);}),
6256
- redoSelection: docMethodOp(function() {makeChangeFromHistory(this, "redo", true);}),
6257
-
6258
- setExtending: function(val) {this.extend = val;},
6259
- getExtending: function() {return this.extend},
6260
-
6261
- historySize: function() {
6262
- var hist = this.history, done = 0, undone = 0;
6263
- for (var i = 0; i < hist.done.length; i++) { if (!hist.done[i].ranges) { ++done; } }
6264
- for (var i$1 = 0; i$1 < hist.undone.length; i$1++) { if (!hist.undone[i$1].ranges) { ++undone; } }
6265
- return {undo: done, redo: undone}
6266
- },
6267
- clearHistory: function() {this.history = new History(this.history.maxGeneration);},
6268
-
6269
- markClean: function() {
6270
- this.cleanGeneration = this.changeGeneration(true);
6271
- },
6272
- changeGeneration: function(forceSplit) {
6273
- if (forceSplit)
6274
- { this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null; }
6275
- return this.history.generation
6276
- },
6277
- isClean: function (gen) {
6278
- return this.history.generation == (gen || this.cleanGeneration)
6279
- },
6280
-
6281
- getHistory: function() {
6282
- return {done: copyHistoryArray(this.history.done),
6283
- undone: copyHistoryArray(this.history.undone)}
6284
- },
6285
- setHistory: function(histData) {
6286
- var hist = this.history = new History(this.history.maxGeneration);
6287
- hist.done = copyHistoryArray(histData.done.slice(0), null, true);
6288
- hist.undone = copyHistoryArray(histData.undone.slice(0), null, true);
6289
- },
6290
-
6291
- setGutterMarker: docMethodOp(function(line, gutterID, value) {
6292
- return changeLine(this, line, "gutter", function (line) {
6293
- var markers = line.gutterMarkers || (line.gutterMarkers = {});
6294
- markers[gutterID] = value;
6295
- if (!value && isEmpty(markers)) { line.gutterMarkers = null; }
6296
- return true
6297
- })
6298
- }),
6299
-
6300
- clearGutter: docMethodOp(function(gutterID) {
6301
- var this$1 = this;
6302
-
6303
- this.iter(function (line) {
6304
- if (line.gutterMarkers && line.gutterMarkers[gutterID]) {
6305
- changeLine(this$1, line, "gutter", function () {
6306
- line.gutterMarkers[gutterID] = null;
6307
- if (isEmpty(line.gutterMarkers)) { line.gutterMarkers = null; }
6308
- return true
6309
- });
6310
- }
6311
- });
6312
- }),
6313
-
6314
- lineInfo: function(line) {
6315
- var n;
6316
- if (typeof line == "number") {
6317
- if (!isLine(this, line)) { return null }
6318
- n = line;
6319
- line = getLine(this, line);
6320
- if (!line) { return null }
6321
- } else {
6322
- n = lineNo(line);
6323
- if (n == null) { return null }
6324
- }
6325
- return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers,
6326
- textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass,
6327
- widgets: line.widgets}
6328
- },
6329
-
6330
- addLineClass: docMethodOp(function(handle, where, cls) {
6331
- return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) {
6332
- var prop = where == "text" ? "textClass"
6333
- : where == "background" ? "bgClass"
6334
- : where == "gutter" ? "gutterClass" : "wrapClass";
6335
- if (!line[prop]) { line[prop] = cls; }
6336
- else if (classTest(cls).test(line[prop])) { return false }
6337
- else { line[prop] += " " + cls; }
6338
- return true
6339
- })
6340
- }),
6341
- removeLineClass: docMethodOp(function(handle, where, cls) {
6342
- return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) {
6343
- var prop = where == "text" ? "textClass"
6344
- : where == "background" ? "bgClass"
6345
- : where == "gutter" ? "gutterClass" : "wrapClass";
6346
- var cur = line[prop];
6347
- if (!cur) { return false }
6348
- else if (cls == null) { line[prop] = null; }
6349
- else {
6350
- var found = cur.match(classTest(cls));
6351
- if (!found) { return false }
6352
- var end = found.index + found[0].length;
6353
- line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null;
6354
- }
6355
- return true
6356
- })
6357
- }),
6358
-
6359
- addLineWidget: docMethodOp(function(handle, node, options) {
6360
- return addLineWidget(this, handle, node, options)
6361
- }),
6362
- removeLineWidget: function(widget) { widget.clear(); },
6363
-
6364
- markText: function(from, to, options) {
6365
- return markText(this, clipPos(this, from), clipPos(this, to), options, options && options.type || "range")
6366
- },
6367
- setBookmark: function(pos, options) {
6368
- var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options),
6369
- insertLeft: options && options.insertLeft,
6370
- clearWhenEmpty: false, shared: options && options.shared,
6371
- handleMouseEvents: options && options.handleMouseEvents};
6372
- pos = clipPos(this, pos);
6373
- return markText(this, pos, pos, realOpts, "bookmark")
6374
- },
6375
- findMarksAt: function(pos) {
6376
- pos = clipPos(this, pos);
6377
- var markers = [], spans = getLine(this, pos.line).markedSpans;
6378
- if (spans) { for (var i = 0; i < spans.length; ++i) {
6379
- var span = spans[i];
6380
- if ((span.from == null || span.from <= pos.ch) &&
6381
- (span.to == null || span.to >= pos.ch))
6382
- { markers.push(span.marker.parent || span.marker); }
6383
- } }
6384
- return markers
6385
- },
6386
- findMarks: function(from, to, filter) {
6387
- from = clipPos(this, from); to = clipPos(this, to);
6388
- var found = [], lineNo$$1 = from.line;
6389
- this.iter(from.line, to.line + 1, function (line) {
6390
- var spans = line.markedSpans;
6391
- if (spans) { for (var i = 0; i < spans.length; i++) {
6392
- var span = spans[i];
6393
- if (!(span.to != null && lineNo$$1 == from.line && from.ch >= span.to ||
6394
- span.from == null && lineNo$$1 != from.line ||
6395
- span.from != null && lineNo$$1 == to.line && span.from >= to.ch) &&
6396
- (!filter || filter(span.marker)))
6397
- { found.push(span.marker.parent || span.marker); }
6398
- } }
6399
- ++lineNo$$1;
6400
- });
6401
- return found
6402
- },
6403
- getAllMarks: function() {
6404
- var markers = [];
6405
- this.iter(function (line) {
6406
- var sps = line.markedSpans;
6407
- if (sps) { for (var i = 0; i < sps.length; ++i)
6408
- { if (sps[i].from != null) { markers.push(sps[i].marker); } } }
6409
- });
6410
- return markers
6411
- },
6412
-
6413
- posFromIndex: function(off) {
6414
- var ch, lineNo$$1 = this.first, sepSize = this.lineSeparator().length;
6415
- this.iter(function (line) {
6416
- var sz = line.text.length + sepSize;
6417
- if (sz > off) { ch = off; return true }
6418
- off -= sz;
6419
- ++lineNo$$1;
6420
- });
6421
- return clipPos(this, Pos(lineNo$$1, ch))
6422
- },
6423
- indexFromPos: function (coords) {
6424
- coords = clipPos(this, coords);
6425
- var index = coords.ch;
6426
- if (coords.line < this.first || coords.ch < 0) { return 0 }
6427
- var sepSize = this.lineSeparator().length;
6428
- this.iter(this.first, coords.line, function (line) { // iter aborts when callback returns a truthy value
6429
- index += line.text.length + sepSize;
6430
- });
6431
- return index
6432
- },
6433
-
6434
- copy: function(copyHistory) {
6435
- var doc = new Doc(getLines(this, this.first, this.first + this.size),
6436
- this.modeOption, this.first, this.lineSep, this.direction);
6437
- doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft;
6438
- doc.sel = this.sel;
6439
- doc.extend = false;
6440
- if (copyHistory) {
6441
- doc.history.undoDepth = this.history.undoDepth;
6442
- doc.setHistory(this.getHistory());
6443
- }
6444
- return doc
6445
- },
6446
-
6447
- linkedDoc: function(options) {
6448
- if (!options) { options = {}; }
6449
- var from = this.first, to = this.first + this.size;
6450
- if (options.from != null && options.from > from) { from = options.from; }
6451
- if (options.to != null && options.to < to) { to = options.to; }
6452
- var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from, this.lineSep, this.direction);
6453
- if (options.sharedHist) { copy.history = this.history
6454
- ; }(this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist});
6455
- copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}];
6456
- copySharedMarkers(copy, findSharedMarkers(this));
6457
- return copy
6458
- },
6459
- unlinkDoc: function(other) {
6460
- var this$1 = this;
6461
-
6462
- if (other instanceof CodeMirror) { other = other.doc; }
6463
- if (this.linked) { for (var i = 0; i < this.linked.length; ++i) {
6464
- var link = this$1.linked[i];
6465
- if (link.doc != other) { continue }
6466
- this$1.linked.splice(i, 1);
6467
- other.unlinkDoc(this$1);
6468
- detachSharedMarkers(findSharedMarkers(this$1));
6469
- break
6470
- } }
6471
- // If the histories were shared, split them again
6472
- if (other.history == this.history) {
6473
- var splitIds = [other.id];
6474
- linkedDocs(other, function (doc) { return splitIds.push(doc.id); }, true);
6475
- other.history = new History(null);
6476
- other.history.done = copyHistoryArray(this.history.done, splitIds);
6477
- other.history.undone = copyHistoryArray(this.history.undone, splitIds);
6478
- }
6479
- },
6480
- iterLinkedDocs: function(f) {linkedDocs(this, f);},
6481
-
6482
- getMode: function() {return this.mode},
6483
- getEditor: function() {return this.cm},
6484
-
6485
- splitLines: function(str) {
6486
- if (this.lineSep) { return str.split(this.lineSep) }
6487
- return splitLinesAuto(str)
6488
- },
6489
- lineSeparator: function() { return this.lineSep || "\n" },
6490
-
6491
- setDirection: docMethodOp(function (dir) {
6492
- if (dir != "rtl") { dir = "ltr"; }
6493
- if (dir == this.direction) { return }
6494
- this.direction = dir;
6495
- this.iter(function (line) { return line.order = null; });
6496
- if (this.cm) { directionChanged(this.cm); }
6497
- })
6498
- });
6499
-
6500
- // Public alias.
6501
- Doc.prototype.eachLine = Doc.prototype.iter;
6502
-
6503
- // Kludge to work around strange IE behavior where it'll sometimes
6504
- // re-fire a series of drag-related events right after the drop (#1551)
6505
- var lastDrop = 0;
6506
-
6507
- function onDrop(e) {
6508
- var cm = this;
6509
- clearDragCursor(cm);
6510
- if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e))
6511
- { return }
6512
- e_preventDefault(e);
6513
- if (ie) { lastDrop = +new Date; }
6514
- var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files;
6515
- if (!pos || cm.isReadOnly()) { return }
6516
- // Might be a file drop, in which case we simply extract the text
6517
- // and insert it.
6518
- if (files && files.length && window.FileReader && window.File) {
6519
- var n = files.length, text = Array(n), read = 0;
6520
- var loadFile = function (file, i) {
6521
- if (cm.options.allowDropFileTypes &&
6522
- indexOf(cm.options.allowDropFileTypes, file.type) == -1)
6523
- { return }
6524
-
6525
- var reader = new FileReader;
6526
- reader.onload = operation(cm, function () {
6527
- var content = reader.result;
6528
- if (/[\x00-\x08\x0e-\x1f]{2}/.test(content)) { content = ""; }
6529
- text[i] = content;
6530
- if (++read == n) {
6531
- pos = clipPos(cm.doc, pos);
6532
- var change = {from: pos, to: pos,
6533
- text: cm.doc.splitLines(text.join(cm.doc.lineSeparator())),
6534
- origin: "paste"};
6535
- makeChange(cm.doc, change);
6536
- setSelectionReplaceHistory(cm.doc, simpleSelection(pos, changeEnd(change)));
6537
- }
6538
- });
6539
- reader.readAsText(file);
6540
- };
6541
- for (var i = 0; i < n; ++i) { loadFile(files[i], i); }
6542
- } else { // Normal drop
6543
- // Don't do a replace if the drop happened inside of the selected text.
6544
- if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) {
6545
- cm.state.draggingText(e);
6546
- // Ensure the editor is re-focused
6547
- setTimeout(function () { return cm.display.input.focus(); }, 20);
6548
- return
6549
- }
6550
- try {
6551
- var text$1 = e.dataTransfer.getData("Text");
6552
- if (text$1) {
6553
- var selected;
6554
- if (cm.state.draggingText && !cm.state.draggingText.copy)
6555
- { selected = cm.listSelections(); }
6556
- setSelectionNoUndo(cm.doc, simpleSelection(pos, pos));
6557
- if (selected) { for (var i$1 = 0; i$1 < selected.length; ++i$1)
6558
- { replaceRange(cm.doc, "", selected[i$1].anchor, selected[i$1].head, "drag"); } }
6559
- cm.replaceSelection(text$1, "around", "paste");
6560
- cm.display.input.focus();
6561
- }
6562
- }
6563
- catch(e){}
6564
- }
6565
- }
6566
-
6567
- function onDragStart(cm, e) {
6568
- if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return }
6569
- if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) { return }
6570
-
6571
- e.dataTransfer.setData("Text", cm.getSelection());
6572
- e.dataTransfer.effectAllowed = "copyMove";
6573
-
6574
- // Use dummy image instead of default browsers image.
6575
- // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
6576
- if (e.dataTransfer.setDragImage && !safari) {
6577
- var img = elt("img", null, null, "position: fixed; left: 0; top: 0;");
6578
- img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
6579
- if (presto) {
6580
- img.width = img.height = 1;
6581
- cm.display.wrapper.appendChild(img);
6582
- // Force a relayout, or Opera won't use our image for some obscure reason
6583
- img._top = img.offsetTop;
6584
- }
6585
- e.dataTransfer.setDragImage(img, 0, 0);
6586
- if (presto) { img.parentNode.removeChild(img); }
6587
- }
6588
- }
6589
-
6590
- function onDragOver(cm, e) {
6591
- var pos = posFromMouse(cm, e);
6592
- if (!pos) { return }
6593
- var frag = document.createDocumentFragment();
6594
- drawSelectionCursor(cm, pos, frag);
6595
- if (!cm.display.dragCursor) {
6596
- cm.display.dragCursor = elt("div", null, "CodeMirror-cursors CodeMirror-dragcursors");
6597
- cm.display.lineSpace.insertBefore(cm.display.dragCursor, cm.display.cursorDiv);
6598
- }
6599
- removeChildrenAndAdd(cm.display.dragCursor, frag);
6600
- }
6601
-
6602
- function clearDragCursor(cm) {
6603
- if (cm.display.dragCursor) {
6604
- cm.display.lineSpace.removeChild(cm.display.dragCursor);
6605
- cm.display.dragCursor = null;
6606
- }
6607
- }
6608
-
6609
- // These must be handled carefully, because naively registering a
6610
- // handler for each editor will cause the editors to never be
6611
- // garbage collected.
6612
-
6613
- function forEachCodeMirror(f) {
6614
- if (!document.getElementsByClassName) { return }
6615
- var byClass = document.getElementsByClassName("CodeMirror"), editors = [];
6616
- for (var i = 0; i < byClass.length; i++) {
6617
- var cm = byClass[i].CodeMirror;
6618
- if (cm) { editors.push(cm); }
6619
- }
6620
- if (editors.length) { editors[0].operation(function () {
6621
- for (var i = 0; i < editors.length; i++) { f(editors[i]); }
6622
- }); }
6623
- }
6624
-
6625
- var globalsRegistered = false;
6626
- function ensureGlobalHandlers() {
6627
- if (globalsRegistered) { return }
6628
- registerGlobalHandlers();
6629
- globalsRegistered = true;
6630
- }
6631
- function registerGlobalHandlers() {
6632
- // When the window resizes, we need to refresh active editors.
6633
- var resizeTimer;
6634
- on(window, "resize", function () {
6635
- if (resizeTimer == null) { resizeTimer = setTimeout(function () {
6636
- resizeTimer = null;
6637
- forEachCodeMirror(onResize);
6638
- }, 100); }
6639
- });
6640
- // When the window loses focus, we want to show the editor as blurred
6641
- on(window, "blur", function () { return forEachCodeMirror(onBlur); });
6642
- }
6643
- // Called when the window resizes
6644
- function onResize(cm) {
6645
- var d = cm.display;
6646
- // Might be a text scaling operation, clear size caches.
6647
- d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;
6648
- d.scrollbarsClipped = false;
6649
- cm.setSize();
6650
- }
6651
-
6652
- var keyNames = {
6653
- 3: "Pause", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
6654
- 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
6655
- 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
6656
- 46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod",
6657
- 106: "*", 107: "=", 109: "-", 110: ".", 111: "/", 145: "ScrollLock",
6658
- 173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\",
6659
- 221: "]", 222: "'", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete",
6660
- 63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Insert"
6661
- };
6662
-
6663
- // Number keys
6664
- for (var i = 0; i < 10; i++) { keyNames[i + 48] = keyNames[i + 96] = String(i); }
6665
- // Alphabetic keys
6666
- for (var i$1 = 65; i$1 <= 90; i$1++) { keyNames[i$1] = String.fromCharCode(i$1); }
6667
- // Function keys
6668
- for (var i$2 = 1; i$2 <= 12; i$2++) { keyNames[i$2 + 111] = keyNames[i$2 + 63235] = "F" + i$2; }
6669
-
6670
- var keyMap = {};
6671
-
6672
- keyMap.basic = {
6673
- "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
6674
- "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
6675
- "Delete": "delCharAfter", "Backspace": "delCharBefore", "Shift-Backspace": "delCharBefore",
6676
- "Tab": "defaultTab", "Shift-Tab": "indentAuto",
6677
- "Enter": "newlineAndIndent", "Insert": "toggleOverwrite",
6678
- "Esc": "singleSelection"
6679
- };
6680
- // Note that the save and find-related commands aren't defined by
6681
- // default. User code or addons can define them. Unknown commands
6682
- // are simply ignored.
6683
- keyMap.pcDefault = {
6684
- "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
6685
- "Ctrl-Home": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Up": "goLineUp", "Ctrl-Down": "goLineDown",
6686
- "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
6687
- "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find",
6688
- "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
6689
- "Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
6690
- "Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSelection",
6691
- "fallthrough": "basic"
6692
- };
6693
- // Very basic readline/emacs-style bindings, which are standard on Mac.
6694
- keyMap.emacsy = {
6695
- "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
6696
- "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
6697
- "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore",
6698
- "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars",
6699
- "Ctrl-O": "openLine"
6700
- };
6701
- keyMap.macDefault = {
6702
- "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
6703
- "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft",
6704
- "Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineRight", "Alt-Backspace": "delGroupBefore",
6705
- "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find",
6706
- "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
6707
- "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLineLeft", "Cmd-Delete": "delWrappedLineRight",
6708
- "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocStart", "Ctrl-Down": "goDocEnd",
6709
- "fallthrough": ["basic", "emacsy"]
6710
- };
6711
- keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault;
6712
-
6713
- // KEYMAP DISPATCH
6714
-
6715
- function normalizeKeyName(name) {
6716
- var parts = name.split(/-(?!$)/);
6717
- name = parts[parts.length - 1];
6718
- var alt, ctrl, shift, cmd;
6719
- for (var i = 0; i < parts.length - 1; i++) {
6720
- var mod = parts[i];
6721
- if (/^(cmd|meta|m)$/i.test(mod)) { cmd = true; }
6722
- else if (/^a(lt)?$/i.test(mod)) { alt = true; }
6723
- else if (/^(c|ctrl|control)$/i.test(mod)) { ctrl = true; }
6724
- else if (/^s(hift)?$/i.test(mod)) { shift = true; }
6725
- else { throw new Error("Unrecognized modifier name: " + mod) }
6726
- }
6727
- if (alt) { name = "Alt-" + name; }
6728
- if (ctrl) { name = "Ctrl-" + name; }
6729
- if (cmd) { name = "Cmd-" + name; }
6730
- if (shift) { name = "Shift-" + name; }
6731
- return name
6732
- }
6733
-
6734
- // This is a kludge to keep keymaps mostly working as raw objects
6735
- // (backwards compatibility) while at the same time support features
6736
- // like normalization and multi-stroke key bindings. It compiles a
6737
- // new normalized keymap, and then updates the old object to reflect
6738
- // this.
6739
- function normalizeKeyMap(keymap) {
6740
- var copy = {};
6741
- for (var keyname in keymap) { if (keymap.hasOwnProperty(keyname)) {
6742
- var value = keymap[keyname];
6743
- if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) { continue }
6744
- if (value == "...") { delete keymap[keyname]; continue }
6745
-
6746
- var keys = map(keyname.split(" "), normalizeKeyName);
6747
- for (var i = 0; i < keys.length; i++) {
6748
- var val = (void 0), name = (void 0);
6749
- if (i == keys.length - 1) {
6750
- name = keys.join(" ");
6751
- val = value;
6752
- } else {
6753
- name = keys.slice(0, i + 1).join(" ");
6754
- val = "...";
6755
- }
6756
- var prev = copy[name];
6757
- if (!prev) { copy[name] = val; }
6758
- else if (prev != val) { throw new Error("Inconsistent bindings for " + name) }
6759
- }
6760
- delete keymap[keyname];
6761
- } }
6762
- for (var prop in copy) { keymap[prop] = copy[prop]; }
6763
- return keymap
6764
- }
6765
-
6766
- function lookupKey(key, map$$1, handle, context) {
6767
- map$$1 = getKeyMap(map$$1);
6768
- var found = map$$1.call ? map$$1.call(key, context) : map$$1[key];
6769
- if (found === false) { return "nothing" }
6770
- if (found === "...") { return "multi" }
6771
- if (found != null && handle(found)) { return "handled" }
6772
-
6773
- if (map$$1.fallthrough) {
6774
- if (Object.prototype.toString.call(map$$1.fallthrough) != "[object Array]")
6775
- { return lookupKey(key, map$$1.fallthrough, handle, context) }
6776
- for (var i = 0; i < map$$1.fallthrough.length; i++) {
6777
- var result = lookupKey(key, map$$1.fallthrough[i], handle, context);
6778
- if (result) { return result }
6779
- }
6780
- }
6781
- }
6782
-
6783
- // Modifier key presses don't count as 'real' key presses for the
6784
- // purpose of keymap fallthrough.
6785
- function isModifierKey(value) {
6786
- var name = typeof value == "string" ? value : keyNames[value.keyCode];
6787
- return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod"
6788
- }
6789
-
6790
- function addModifierNames(name, event, noShift) {
6791
- var base = name;
6792
- if (event.altKey && base != "Alt") { name = "Alt-" + name; }
6793
- if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != "Ctrl") { name = "Ctrl-" + name; }
6794
- if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != "Cmd") { name = "Cmd-" + name; }
6795
- if (!noShift && event.shiftKey && base != "Shift") { name = "Shift-" + name; }
6796
- return name
6797
- }
6798
-
6799
- // Look up the name of a key as indicated by an event object.
6800
- function keyName(event, noShift) {
6801
- if (presto && event.keyCode == 34 && event["char"]) { return false }
6802
- var name = keyNames[event.keyCode];
6803
- if (name == null || event.altGraphKey) { return false }
6804
- // Ctrl-ScrollLock has keyCode 3, same as Ctrl-Pause,
6805
- // so we'll use event.code when available (Chrome 48+, FF 38+, Safari 10.1+)
6806
- if (event.keyCode == 3 && event.code) { name = event.code; }
6807
- return addModifierNames(name, event, noShift)
6808
- }
6809
-
6810
- function getKeyMap(val) {
6811
- return typeof val == "string" ? keyMap[val] : val
6812
- }
6813
-
6814
- // Helper for deleting text near the selection(s), used to implement
6815
- // backspace, delete, and similar functionality.
6816
- function deleteNearSelection(cm, compute) {
6817
- var ranges = cm.doc.sel.ranges, kill = [];
6818
- // Build up a set of ranges to kill first, merging overlapping
6819
- // ranges.
6820
- for (var i = 0; i < ranges.length; i++) {
6821
- var toKill = compute(ranges[i]);
6822
- while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) {
6823
- var replaced = kill.pop();
6824
- if (cmp(replaced.from, toKill.from) < 0) {
6825
- toKill.from = replaced.from;
6826
- break
6827
- }
6828
- }
6829
- kill.push(toKill);
6830
- }
6831
- // Next, remove those actual ranges.
6832
- runInOp(cm, function () {
6833
- for (var i = kill.length - 1; i >= 0; i--)
6834
- { replaceRange(cm.doc, "", kill[i].from, kill[i].to, "+delete"); }
6835
- ensureCursorVisible(cm);
6836
- });
6837
- }
6838
-
6839
- function moveCharLogically(line, ch, dir) {
6840
- var target = skipExtendingChars(line.text, ch + dir, dir);
6841
- return target < 0 || target > line.text.length ? null : target
6842
- }
6843
-
6844
- function moveLogically(line, start, dir) {
6845
- var ch = moveCharLogically(line, start.ch, dir);
6846
- return ch == null ? null : new Pos(start.line, ch, dir < 0 ? "after" : "before")
6847
- }
6848
-
6849
- function endOfLine(visually, cm, lineObj, lineNo, dir) {
6850
- if (visually) {
6851
- var order = getOrder(lineObj, cm.doc.direction);
6852
- if (order) {
6853
- var part = dir < 0 ? lst(order) : order[0];
6854
- var moveInStorageOrder = (dir < 0) == (part.level == 1);
6855
- var sticky = moveInStorageOrder ? "after" : "before";
6856
- var ch;
6857
- // With a wrapped rtl chunk (possibly spanning multiple bidi parts),
6858
- // it could be that the last bidi part is not on the last visual line,
6859
- // since visual lines contain content order-consecutive chunks.
6860
- // Thus, in rtl, we are looking for the first (content-order) character
6861
- // in the rtl chunk that is on the last line (that is, the same line
6862
- // as the last (content-order) character).
6863
- if (part.level > 0 || cm.doc.direction == "rtl") {
6864
- var prep = prepareMeasureForLine(cm, lineObj);
6865
- ch = dir < 0 ? lineObj.text.length - 1 : 0;
6866
- var targetTop = measureCharPrepared(cm, prep, ch).top;
6867
- ch = findFirst(function (ch) { return measureCharPrepared(cm, prep, ch).top == targetTop; }, (dir < 0) == (part.level == 1) ? part.from : part.to - 1, ch);
6868
- if (sticky == "before") { ch = moveCharLogically(lineObj, ch, 1); }
6869
- } else { ch = dir < 0 ? part.to : part.from; }
6870
- return new Pos(lineNo, ch, sticky)
6871
- }
6872
- }
6873
- return new Pos(lineNo, dir < 0 ? lineObj.text.length : 0, dir < 0 ? "before" : "after")
6874
- }
6875
-
6876
- function moveVisually(cm, line, start, dir) {
6877
- var bidi = getOrder(line, cm.doc.direction);
6878
- if (!bidi) { return moveLogically(line, start, dir) }
6879
- if (start.ch >= line.text.length) {
6880
- start.ch = line.text.length;
6881
- start.sticky = "before";
6882
- } else if (start.ch <= 0) {
6883
- start.ch = 0;
6884
- start.sticky = "after";
6885
- }
6886
- var partPos = getBidiPartAt(bidi, start.ch, start.sticky), part = bidi[partPos];
6887
- if (cm.doc.direction == "ltr" && part.level % 2 == 0 && (dir > 0 ? part.to > start.ch : part.from < start.ch)) {
6888
- // Case 1: We move within an ltr part in an ltr editor. Even with wrapped lines,
6889
- // nothing interesting happens.
6890
- return moveLogically(line, start, dir)
6891
- }
6892
-
6893
- var mv = function (pos, dir) { return moveCharLogically(line, pos instanceof Pos ? pos.ch : pos, dir); };
6894
- var prep;
6895
- var getWrappedLineExtent = function (ch) {
6896
- if (!cm.options.lineWrapping) { return {begin: 0, end: line.text.length} }
6897
- prep = prep || prepareMeasureForLine(cm, line);
6898
- return wrappedLineExtentChar(cm, line, prep, ch)
6899
- };
6900
- var wrappedLineExtent = getWrappedLineExtent(start.sticky == "before" ? mv(start, -1) : start.ch);
6901
-
6902
- if (cm.doc.direction == "rtl" || part.level == 1) {
6903
- var moveInStorageOrder = (part.level == 1) == (dir < 0);
6904
- var ch = mv(start, moveInStorageOrder ? 1 : -1);
6905
- if (ch != null && (!moveInStorageOrder ? ch >= part.from && ch >= wrappedLineExtent.begin : ch <= part.to && ch <= wrappedLineExtent.end)) {
6906
- // Case 2: We move within an rtl part or in an rtl editor on the same visual line
6907
- var sticky = moveInStorageOrder ? "before" : "after";
6908
- return new Pos(start.line, ch, sticky)
6909
- }
6910
- }
6911
-
6912
- // Case 3: Could not move within this bidi part in this visual line, so leave
6913
- // the current bidi part
6914
-
6915
- var searchInVisualLine = function (partPos, dir, wrappedLineExtent) {
6916
- var getRes = function (ch, moveInStorageOrder) { return moveInStorageOrder
6917
- ? new Pos(start.line, mv(ch, 1), "before")
6918
- : new Pos(start.line, ch, "after"); };
6919
-
6920
- for (; partPos >= 0 && partPos < bidi.length; partPos += dir) {
6921
- var part = bidi[partPos];
6922
- var moveInStorageOrder = (dir > 0) == (part.level != 1);
6923
- var ch = moveInStorageOrder ? wrappedLineExtent.begin : mv(wrappedLineExtent.end, -1);
6924
- if (part.from <= ch && ch < part.to) { return getRes(ch, moveInStorageOrder) }
6925
- ch = moveInStorageOrder ? part.from : mv(part.to, -1);
6926
- if (wrappedLineExtent.begin <= ch && ch < wrappedLineExtent.end) { return getRes(ch, moveInStorageOrder) }
6927
- }
6928
- };
6929
-
6930
- // Case 3a: Look for other bidi parts on the same visual line
6931
- var res = searchInVisualLine(partPos + dir, dir, wrappedLineExtent);
6932
- if (res) { return res }
6933
-
6934
- // Case 3b: Look for other bidi parts on the next visual line
6935
- var nextCh = dir > 0 ? wrappedLineExtent.end : mv(wrappedLineExtent.begin, -1);
6936
- if (nextCh != null && !(dir > 0 && nextCh == line.text.length)) {
6937
- res = searchInVisualLine(dir > 0 ? 0 : bidi.length - 1, dir, getWrappedLineExtent(nextCh));
6938
- if (res) { return res }
6939
- }
6940
-
6941
- // Case 4: Nowhere to move
6942
- return null
6943
- }
6944
-
6945
- // Commands are parameter-less actions that can be performed on an
6946
- // editor, mostly used for keybindings.
6947
- var commands = {
6948
- selectAll: selectAll,
6949
- singleSelection: function (cm) { return cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head"), sel_dontScroll); },
6950
- killLine: function (cm) { return deleteNearSelection(cm, function (range) {
6951
- if (range.empty()) {
6952
- var len = getLine(cm.doc, range.head.line).text.length;
6953
- if (range.head.ch == len && range.head.line < cm.lastLine())
6954
- { return {from: range.head, to: Pos(range.head.line + 1, 0)} }
6955
- else
6956
- { return {from: range.head, to: Pos(range.head.line, len)} }
6957
- } else {
6958
- return {from: range.from(), to: range.to()}
6959
- }
6960
- }); },
6961
- deleteLine: function (cm) { return deleteNearSelection(cm, function (range) { return ({
6962
- from: Pos(range.from().line, 0),
6963
- to: clipPos(cm.doc, Pos(range.to().line + 1, 0))
6964
- }); }); },
6965
- delLineLeft: function (cm) { return deleteNearSelection(cm, function (range) { return ({
6966
- from: Pos(range.from().line, 0), to: range.from()
6967
- }); }); },
6968
- delWrappedLineLeft: function (cm) { return deleteNearSelection(cm, function (range) {
6969
- var top = cm.charCoords(range.head, "div").top + 5;
6970
- var leftPos = cm.coordsChar({left: 0, top: top}, "div");
6971
- return {from: leftPos, to: range.from()}
6972
- }); },
6973
- delWrappedLineRight: function (cm) { return deleteNearSelection(cm, function (range) {
6974
- var top = cm.charCoords(range.head, "div").top + 5;
6975
- var rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div");
6976
- return {from: range.from(), to: rightPos }
6977
- }); },
6978
- undo: function (cm) { return cm.undo(); },
6979
- redo: function (cm) { return cm.redo(); },
6980
- undoSelection: function (cm) { return cm.undoSelection(); },
6981
- redoSelection: function (cm) { return cm.redoSelection(); },
6982
- goDocStart: function (cm) { return cm.extendSelection(Pos(cm.firstLine(), 0)); },
6983
- goDocEnd: function (cm) { return cm.extendSelection(Pos(cm.lastLine())); },
6984
- goLineStart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStart(cm, range.head.line); },
6985
- {origin: "+move", bias: 1}
6986
- ); },
6987
- goLineStartSmart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStartSmart(cm, range.head); },
6988
- {origin: "+move", bias: 1}
6989
- ); },
6990
- goLineEnd: function (cm) { return cm.extendSelectionsBy(function (range) { return lineEnd(cm, range.head.line); },
6991
- {origin: "+move", bias: -1}
6992
- ); },
6993
- goLineRight: function (cm) { return cm.extendSelectionsBy(function (range) {
6994
- var top = cm.cursorCoords(range.head, "div").top + 5;
6995
- return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div")
6996
- }, sel_move); },
6997
- goLineLeft: function (cm) { return cm.extendSelectionsBy(function (range) {
6998
- var top = cm.cursorCoords(range.head, "div").top + 5;
6999
- return cm.coordsChar({left: 0, top: top}, "div")
7000
- }, sel_move); },
7001
- goLineLeftSmart: function (cm) { return cm.extendSelectionsBy(function (range) {
7002
- var top = cm.cursorCoords(range.head, "div").top + 5;
7003
- var pos = cm.coordsChar({left: 0, top: top}, "div");
7004
- if (pos.ch < cm.getLine(pos.line).search(/\S/)) { return lineStartSmart(cm, range.head) }
7005
- return pos
7006
- }, sel_move); },
7007
- goLineUp: function (cm) { return cm.moveV(-1, "line"); },
7008
- goLineDown: function (cm) { return cm.moveV(1, "line"); },
7009
- goPageUp: function (cm) { return cm.moveV(-1, "page"); },
7010
- goPageDown: function (cm) { return cm.moveV(1, "page"); },
7011
- goCharLeft: function (cm) { return cm.moveH(-1, "char"); },
7012
- goCharRight: function (cm) { return cm.moveH(1, "char"); },
7013
- goColumnLeft: function (cm) { return cm.moveH(-1, "column"); },
7014
- goColumnRight: function (cm) { return cm.moveH(1, "column"); },
7015
- goWordLeft: function (cm) { return cm.moveH(-1, "word"); },
7016
- goGroupRight: function (cm) { return cm.moveH(1, "group"); },
7017
- goGroupLeft: function (cm) { return cm.moveH(-1, "group"); },
7018
- goWordRight: function (cm) { return cm.moveH(1, "word"); },
7019
- delCharBefore: function (cm) { return cm.deleteH(-1, "char"); },
7020
- delCharAfter: function (cm) { return cm.deleteH(1, "char"); },
7021
- delWordBefore: function (cm) { return cm.deleteH(-1, "word"); },
7022
- delWordAfter: function (cm) { return cm.deleteH(1, "word"); },
7023
- delGroupBefore: function (cm) { return cm.deleteH(-1, "group"); },
7024
- delGroupAfter: function (cm) { return cm.deleteH(1, "group"); },
7025
- indentAuto: function (cm) { return cm.indentSelection("smart"); },
7026
- indentMore: function (cm) { return cm.indentSelection("add"); },
7027
- indentLess: function (cm) { return cm.indentSelection("subtract"); },
7028
- insertTab: function (cm) { return cm.replaceSelection("\t"); },
7029
- insertSoftTab: function (cm) {
7030
- var spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize;
7031
- for (var i = 0; i < ranges.length; i++) {
7032
- var pos = ranges[i].from();
7033
- var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize);
7034
- spaces.push(spaceStr(tabSize - col % tabSize));
7035
- }
7036
- cm.replaceSelections(spaces);
7037
- },
7038
- defaultTab: function (cm) {
7039
- if (cm.somethingSelected()) { cm.indentSelection("add"); }
7040
- else { cm.execCommand("insertTab"); }
7041
- },
7042
- // Swap the two chars left and right of each selection's head.
7043
- // Move cursor behind the two swapped characters afterwards.
7044
- //
7045
- // Doesn't consider line feeds a character.
7046
- // Doesn't scan more than one line above to find a character.
7047
- // Doesn't do anything on an empty line.
7048
- // Doesn't do anything with non-empty selections.
7049
- transposeChars: function (cm) { return runInOp(cm, function () {
7050
- var ranges = cm.listSelections(), newSel = [];
7051
- for (var i = 0; i < ranges.length; i++) {
7052
- if (!ranges[i].empty()) { continue }
7053
- var cur = ranges[i].head, line = getLine(cm.doc, cur.line).text;
7054
- if (line) {
7055
- if (cur.ch == line.length) { cur = new Pos(cur.line, cur.ch - 1); }
7056
- if (cur.ch > 0) {
7057
- cur = new Pos(cur.line, cur.ch + 1);
7058
- cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2),
7059
- Pos(cur.line, cur.ch - 2), cur, "+transpose");
7060
- } else if (cur.line > cm.doc.first) {
7061
- var prev = getLine(cm.doc, cur.line - 1).text;
7062
- if (prev) {
7063
- cur = new Pos(cur.line, 1);
7064
- cm.replaceRange(line.charAt(0) + cm.doc.lineSeparator() +
7065
- prev.charAt(prev.length - 1),
7066
- Pos(cur.line - 1, prev.length - 1), cur, "+transpose");
7067
- }
7068
- }
7069
- }
7070
- newSel.push(new Range(cur, cur));
7071
- }
7072
- cm.setSelections(newSel);
7073
- }); },
7074
- newlineAndIndent: function (cm) { return runInOp(cm, function () {
7075
- var sels = cm.listSelections();
7076
- for (var i = sels.length - 1; i >= 0; i--)
7077
- { cm.replaceRange(cm.doc.lineSeparator(), sels[i].anchor, sels[i].head, "+input"); }
7078
- sels = cm.listSelections();
7079
- for (var i$1 = 0; i$1 < sels.length; i$1++)
7080
- { cm.indentLine(sels[i$1].from().line, null, true); }
7081
- ensureCursorVisible(cm);
7082
- }); },
7083
- openLine: function (cm) { return cm.replaceSelection("\n", "start"); },
7084
- toggleOverwrite: function (cm) { return cm.toggleOverwrite(); }
7085
- };
7086
-
7087
-
7088
- function lineStart(cm, lineN) {
7089
- var line = getLine(cm.doc, lineN);
7090
- var visual = visualLine(line);
7091
- if (visual != line) { lineN = lineNo(visual); }
7092
- return endOfLine(true, cm, visual, lineN, 1)
7093
- }
7094
- function lineEnd(cm, lineN) {
7095
- var line = getLine(cm.doc, lineN);
7096
- var visual = visualLineEnd(line);
7097
- if (visual != line) { lineN = lineNo(visual); }
7098
- return endOfLine(true, cm, line, lineN, -1)
7099
- }
7100
- function lineStartSmart(cm, pos) {
7101
- var start = lineStart(cm, pos.line);
7102
- var line = getLine(cm.doc, start.line);
7103
- var order = getOrder(line, cm.doc.direction);
7104
- if (!order || order[0].level == 0) {
7105
- var firstNonWS = Math.max(0, line.text.search(/\S/));
7106
- var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch;
7107
- return Pos(start.line, inWS ? 0 : firstNonWS, start.sticky)
7108
- }
7109
- return start
7110
- }
7111
-
7112
- // Run a handler that was bound to a key.
7113
- function doHandleBinding(cm, bound, dropShift) {
7114
- if (typeof bound == "string") {
7115
- bound = commands[bound];
7116
- if (!bound) { return false }
7117
- }
7118
- // Ensure previous input has been read, so that the handler sees a
7119
- // consistent view of the document
7120
- cm.display.input.ensurePolled();
7121
- var prevShift = cm.display.shift, done = false;
7122
- try {
7123
- if (cm.isReadOnly()) { cm.state.suppressEdits = true; }
7124
- if (dropShift) { cm.display.shift = false; }
7125
- done = bound(cm) != Pass;
7126
- } finally {
7127
- cm.display.shift = prevShift;
7128
- cm.state.suppressEdits = false;
7129
- }
7130
- return done
7131
- }
7132
-
7133
- function lookupKeyForEditor(cm, name, handle) {
7134
- for (var i = 0; i < cm.state.keyMaps.length; i++) {
7135
- var result = lookupKey(name, cm.state.keyMaps[i], handle, cm);
7136
- if (result) { return result }
7137
- }
7138
- return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle, cm))
7139
- || lookupKey(name, cm.options.keyMap, handle, cm)
7140
- }
7141
-
7142
- // Note that, despite the name, this function is also used to check
7143
- // for bound mouse clicks.
7144
-
7145
- var stopSeq = new Delayed;
7146
-
7147
- function dispatchKey(cm, name, e, handle) {
7148
- var seq = cm.state.keySeq;
7149
- if (seq) {
7150
- if (isModifierKey(name)) { return "handled" }
7151
- if (/\'$/.test(name))
7152
- { cm.state.keySeq = null; }
7153
- else
7154
- { stopSeq.set(50, function () {
7155
- if (cm.state.keySeq == seq) {
7156
- cm.state.keySeq = null;
7157
- cm.display.input.reset();
7158
- }
7159
- }); }
7160
- if (dispatchKeyInner(cm, seq + " " + name, e, handle)) { return true }
7161
- }
7162
- return dispatchKeyInner(cm, name, e, handle)
7163
- }
7164
-
7165
- function dispatchKeyInner(cm, name, e, handle) {
7166
- var result = lookupKeyForEditor(cm, name, handle);
7167
-
7168
- if (result == "multi")
7169
- { cm.state.keySeq = name; }
7170
- if (result == "handled")
7171
- { signalLater(cm, "keyHandled", cm, name, e); }
7172
-
7173
- if (result == "handled" || result == "multi") {
7174
- e_preventDefault(e);
7175
- restartBlink(cm);
7176
- }
7177
-
7178
- return !!result
7179
- }
7180
-
7181
- // Handle a key from the keydown event.
7182
- function handleKeyBinding(cm, e) {
7183
- var name = keyName(e, true);
7184
- if (!name) { return false }
7185
-
7186
- if (e.shiftKey && !cm.state.keySeq) {
7187
- // First try to resolve full name (including 'Shift-'). Failing
7188
- // that, see if there is a cursor-motion command (starting with
7189
- // 'go') bound to the keyname without 'Shift-'.
7190
- return dispatchKey(cm, "Shift-" + name, e, function (b) { return doHandleBinding(cm, b, true); })
7191
- || dispatchKey(cm, name, e, function (b) {
7192
- if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion)
7193
- { return doHandleBinding(cm, b) }
7194
- })
7195
- } else {
7196
- return dispatchKey(cm, name, e, function (b) { return doHandleBinding(cm, b); })
7197
- }
7198
- }
7199
-
7200
- // Handle a key from the keypress event
7201
- function handleCharBinding(cm, e, ch) {
7202
- return dispatchKey(cm, "'" + ch + "'", e, function (b) { return doHandleBinding(cm, b, true); })
7203
- }
7204
-
7205
- var lastStoppedKey = null;
7206
- function onKeyDown(e) {
7207
- var cm = this;
7208
- cm.curOp.focus = activeElt();
7209
- if (signalDOMEvent(cm, e)) { return }
7210
- // IE does strange things with escape.
7211
- if (ie && ie_version < 11 && e.keyCode == 27) { e.returnValue = false; }
7212
- var code = e.keyCode;
7213
- cm.display.shift = code == 16 || e.shiftKey;
7214
- var handled = handleKeyBinding(cm, e);
7215
- if (presto) {
7216
- lastStoppedKey = handled ? code : null;
7217
- // Opera has no cut event... we try to at least catch the key combo
7218
- if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey))
7219
- { cm.replaceSelection("", null, "cut"); }
7220
- }
7221
-
7222
- // Turn mouse into crosshair when Alt is held on Mac.
7223
- if (code == 18 && !/\bCodeMirror-crosshair\b/.test(cm.display.lineDiv.className))
7224
- { showCrossHair(cm); }
7225
- }
7226
-
7227
- function showCrossHair(cm) {
7228
- var lineDiv = cm.display.lineDiv;
7229
- addClass(lineDiv, "CodeMirror-crosshair");
7230
-
7231
- function up(e) {
7232
- if (e.keyCode == 18 || !e.altKey) {
7233
- rmClass(lineDiv, "CodeMirror-crosshair");
7234
- off(document, "keyup", up);
7235
- off(document, "mouseover", up);
7236
- }
7237
- }
7238
- on(document, "keyup", up);
7239
- on(document, "mouseover", up);
7240
- }
7241
-
7242
- function onKeyUp(e) {
7243
- if (e.keyCode == 16) { this.doc.sel.shift = false; }
7244
- signalDOMEvent(this, e);
7245
- }
7246
-
7247
- function onKeyPress(e) {
7248
- var cm = this;
7249
- if (eventInWidget(cm.display, e) || signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) { return }
7250
- var keyCode = e.keyCode, charCode = e.charCode;
7251
- if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return}
7252
- if ((presto && (!e.which || e.which < 10)) && handleKeyBinding(cm, e)) { return }
7253
- var ch = String.fromCharCode(charCode == null ? keyCode : charCode);
7254
- // Some browsers fire keypress events for backspace
7255
- if (ch == "\x08") { return }
7256
- if (handleCharBinding(cm, e, ch)) { return }
7257
- cm.display.input.onKeyPress(e);
7258
- }
7259
-
7260
- var DOUBLECLICK_DELAY = 400;
7261
-
7262
- var PastClick = function(time, pos, button) {
7263
- this.time = time;
7264
- this.pos = pos;
7265
- this.button = button;
7266
- };
7267
-
7268
- PastClick.prototype.compare = function (time, pos, button) {
7269
- return this.time + DOUBLECLICK_DELAY > time &&
7270
- cmp(pos, this.pos) == 0 && button == this.button
7271
- };
7272
-
7273
- var lastClick, lastDoubleClick;
7274
- function clickRepeat(pos, button) {
7275
- var now = +new Date;
7276
- if (lastDoubleClick && lastDoubleClick.compare(now, pos, button)) {
7277
- lastClick = lastDoubleClick = null;
7278
- return "triple"
7279
- } else if (lastClick && lastClick.compare(now, pos, button)) {
7280
- lastDoubleClick = new PastClick(now, pos, button);
7281
- lastClick = null;
7282
- return "double"
7283
- } else {
7284
- lastClick = new PastClick(now, pos, button);
7285
- lastDoubleClick = null;
7286
- return "single"
7287
- }
7288
- }
7289
-
7290
- // A mouse down can be a single click, double click, triple click,
7291
- // start of selection drag, start of text drag, new cursor
7292
- // (ctrl-click), rectangle drag (alt-drag), or xwin
7293
- // middle-click-paste. Or it might be a click on something we should
7294
- // not interfere with, such as a scrollbar or widget.
7295
- function onMouseDown(e) {
7296
- var cm = this, display = cm.display;
7297
- if (signalDOMEvent(cm, e) || display.activeTouch && display.input.supportsTouch()) { return }
7298
- display.input.ensurePolled();
7299
- display.shift = e.shiftKey;
7300
-
7301
- if (eventInWidget(display, e)) {
7302
- if (!webkit) {
7303
- // Briefly turn off draggability, to allow widgets to do
7304
- // normal dragging things.
7305
- display.scroller.draggable = false;
7306
- setTimeout(function () { return display.scroller.draggable = true; }, 100);
7307
- }
7308
- return
7309
- }
7310
- if (clickInGutter(cm, e)) { return }
7311
- var pos = posFromMouse(cm, e), button = e_button(e), repeat = pos ? clickRepeat(pos, button) : "single";
7312
- window.focus();
7313
-
7314
- // #3261: make sure, that we're not starting a second selection
7315
- if (button == 1 && cm.state.selectingText)
7316
- { cm.state.selectingText(e); }
7317
-
7318
- if (pos && handleMappedButton(cm, button, pos, repeat, e)) { return }
7319
-
7320
- if (button == 1) {
7321
- if (pos) { leftButtonDown(cm, pos, repeat, e); }
7322
- else if (e_target(e) == display.scroller) { e_preventDefault(e); }
7323
- } else if (button == 2) {
7324
- if (pos) { extendSelection(cm.doc, pos); }
7325
- setTimeout(function () { return display.input.focus(); }, 20);
7326
- } else if (button == 3) {
7327
- if (captureRightClick) { cm.display.input.onContextMenu(e); }
7328
- else { delayBlurEvent(cm); }
7329
- }
7330
- }
7331
-
7332
- function handleMappedButton(cm, button, pos, repeat, event) {
7333
- var name = "Click";
7334
- if (repeat == "double") { name = "Double" + name; }
7335
- else if (repeat == "triple") { name = "Triple" + name; }
7336
- name = (button == 1 ? "Left" : button == 2 ? "Middle" : "Right") + name;
7337
-
7338
- return dispatchKey(cm, addModifierNames(name, event), event, function (bound) {
7339
- if (typeof bound == "string") { bound = commands[bound]; }
7340
- if (!bound) { return false }
7341
- var done = false;
7342
- try {
7343
- if (cm.isReadOnly()) { cm.state.suppressEdits = true; }
7344
- done = bound(cm, pos) != Pass;
7345
- } finally {
7346
- cm.state.suppressEdits = false;
7347
- }
7348
- return done
7349
- })
7350
- }
7351
-
7352
- function configureMouse(cm, repeat, event) {
7353
- var option = cm.getOption("configureMouse");
7354
- var value = option ? option(cm, repeat, event) : {};
7355
- if (value.unit == null) {
7356
- var rect = chromeOS ? event.shiftKey && event.metaKey : event.altKey;
7357
- value.unit = rect ? "rectangle" : repeat == "single" ? "char" : repeat == "double" ? "word" : "line";
7358
- }
7359
- if (value.extend == null || cm.doc.extend) { value.extend = cm.doc.extend || event.shiftKey; }
7360
- if (value.addNew == null) { value.addNew = mac ? event.metaKey : event.ctrlKey; }
7361
- if (value.moveOnDrag == null) { value.moveOnDrag = !(mac ? event.altKey : event.ctrlKey); }
7362
- return value
7363
- }
7364
-
7365
- function leftButtonDown(cm, pos, repeat, event) {
7366
- if (ie) { setTimeout(bind(ensureFocus, cm), 0); }
7367
- else { cm.curOp.focus = activeElt(); }
7368
-
7369
- var behavior = configureMouse(cm, repeat, event);
7370
-
7371
- var sel = cm.doc.sel, contained;
7372
- if (cm.options.dragDrop && dragAndDrop && !cm.isReadOnly() &&
7373
- repeat == "single" && (contained = sel.contains(pos)) > -1 &&
7374
- (cmp((contained = sel.ranges[contained]).from(), pos) < 0 || pos.xRel > 0) &&
7375
- (cmp(contained.to(), pos) > 0 || pos.xRel < 0))
7376
- { leftButtonStartDrag(cm, event, pos, behavior); }
7377
- else
7378
- { leftButtonSelect(cm, event, pos, behavior); }
7379
- }
7380
-
7381
- // Start a text drag. When it ends, see if any dragging actually
7382
- // happen, and treat as a click if it didn't.
7383
- function leftButtonStartDrag(cm, event, pos, behavior) {
7384
- var display = cm.display, moved = false;
7385
- var dragEnd = operation(cm, function (e) {
7386
- if (webkit) { display.scroller.draggable = false; }
7387
- cm.state.draggingText = false;
7388
- off(display.wrapper.ownerDocument, "mouseup", dragEnd);
7389
- off(display.wrapper.ownerDocument, "mousemove", mouseMove);
7390
- off(display.scroller, "dragstart", dragStart);
7391
- off(display.scroller, "drop", dragEnd);
7392
- if (!moved) {
7393
- e_preventDefault(e);
7394
- if (!behavior.addNew)
7395
- { extendSelection(cm.doc, pos, null, null, behavior.extend); }
7396
- // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081)
7397
- if (webkit || ie && ie_version == 9)
7398
- { setTimeout(function () {display.wrapper.ownerDocument.body.focus(); display.input.focus();}, 20); }
7399
- else
7400
- { display.input.focus(); }
7401
- }
7402
- });
7403
- var mouseMove = function(e2) {
7404
- moved = moved || Math.abs(event.clientX - e2.clientX) + Math.abs(event.clientY - e2.clientY) >= 10;
7405
- };
7406
- var dragStart = function () { return moved = true; };
7407
- // Let the drag handler handle this.
7408
- if (webkit) { display.scroller.draggable = true; }
7409
- cm.state.draggingText = dragEnd;
7410
- dragEnd.copy = !behavior.moveOnDrag;
7411
- // IE's approach to draggable
7412
- if (display.scroller.dragDrop) { display.scroller.dragDrop(); }
7413
- on(display.wrapper.ownerDocument, "mouseup", dragEnd);
7414
- on(display.wrapper.ownerDocument, "mousemove", mouseMove);
7415
- on(display.scroller, "dragstart", dragStart);
7416
- on(display.scroller, "drop", dragEnd);
7417
-
7418
- delayBlurEvent(cm);
7419
- setTimeout(function () { return display.input.focus(); }, 20);
7420
- }
7421
-
7422
- function rangeForUnit(cm, pos, unit) {
7423
- if (unit == "char") { return new Range(pos, pos) }
7424
- if (unit == "word") { return cm.findWordAt(pos) }
7425
- if (unit == "line") { return new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))) }
7426
- var result = unit(cm, pos);
7427
- return new Range(result.from, result.to)
7428
- }
7429
-
7430
- // Normal selection, as opposed to text dragging.
7431
- function leftButtonSelect(cm, event, start, behavior) {
7432
- var display = cm.display, doc = cm.doc;
7433
- e_preventDefault(event);
7434
-
7435
- var ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges;
7436
- if (behavior.addNew && !behavior.extend) {
7437
- ourIndex = doc.sel.contains(start);
7438
- if (ourIndex > -1)
7439
- { ourRange = ranges[ourIndex]; }
7440
- else
7441
- { ourRange = new Range(start, start); }
7442
- } else {
7443
- ourRange = doc.sel.primary();
7444
- ourIndex = doc.sel.primIndex;
7445
- }
7446
-
7447
- if (behavior.unit == "rectangle") {
7448
- if (!behavior.addNew) { ourRange = new Range(start, start); }
7449
- start = posFromMouse(cm, event, true, true);
7450
- ourIndex = -1;
7451
- } else {
7452
- var range$$1 = rangeForUnit(cm, start, behavior.unit);
7453
- if (behavior.extend)
7454
- { ourRange = extendRange(ourRange, range$$1.anchor, range$$1.head, behavior.extend); }
7455
- else
7456
- { ourRange = range$$1; }
7457
- }
7458
-
7459
- if (!behavior.addNew) {
7460
- ourIndex = 0;
7461
- setSelection(doc, new Selection([ourRange], 0), sel_mouse);
7462
- startSel = doc.sel;
7463
- } else if (ourIndex == -1) {
7464
- ourIndex = ranges.length;
7465
- setSelection(doc, normalizeSelection(cm, ranges.concat([ourRange]), ourIndex),
7466
- {scroll: false, origin: "*mouse"});
7467
- } else if (ranges.length > 1 && ranges[ourIndex].empty() && behavior.unit == "char" && !behavior.extend) {
7468
- setSelection(doc, normalizeSelection(cm, ranges.slice(0, ourIndex).concat(ranges.slice(ourIndex + 1)), 0),
7469
- {scroll: false, origin: "*mouse"});
7470
- startSel = doc.sel;
7471
- } else {
7472
- replaceOneSelection(doc, ourIndex, ourRange, sel_mouse);
7473
- }
7474
-
7475
- var lastPos = start;
7476
- function extendTo(pos) {
7477
- if (cmp(lastPos, pos) == 0) { return }
7478
- lastPos = pos;
7479
-
7480
- if (behavior.unit == "rectangle") {
7481
- var ranges = [], tabSize = cm.options.tabSize;
7482
- var startCol = countColumn(getLine(doc, start.line).text, start.ch, tabSize);
7483
- var posCol = countColumn(getLine(doc, pos.line).text, pos.ch, tabSize);
7484
- var left = Math.min(startCol, posCol), right = Math.max(startCol, posCol);
7485
- for (var line = Math.min(start.line, pos.line), end = Math.min(cm.lastLine(), Math.max(start.line, pos.line));
7486
- line <= end; line++) {
7487
- var text = getLine(doc, line).text, leftPos = findColumn(text, left, tabSize);
7488
- if (left == right)
7489
- { ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos))); }
7490
- else if (text.length > leftPos)
7491
- { ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize)))); }
7492
- }
7493
- if (!ranges.length) { ranges.push(new Range(start, start)); }
7494
- setSelection(doc, normalizeSelection(cm, startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex),
7495
- {origin: "*mouse", scroll: false});
7496
- cm.scrollIntoView(pos);
7497
- } else {
7498
- var oldRange = ourRange;
7499
- var range$$1 = rangeForUnit(cm, pos, behavior.unit);
7500
- var anchor = oldRange.anchor, head;
7501
- if (cmp(range$$1.anchor, anchor) > 0) {
7502
- head = range$$1.head;
7503
- anchor = minPos(oldRange.from(), range$$1.anchor);
7504
- } else {
7505
- head = range$$1.anchor;
7506
- anchor = maxPos(oldRange.to(), range$$1.head);
7507
- }
7508
- var ranges$1 = startSel.ranges.slice(0);
7509
- ranges$1[ourIndex] = bidiSimplify(cm, new Range(clipPos(doc, anchor), head));
7510
- setSelection(doc, normalizeSelection(cm, ranges$1, ourIndex), sel_mouse);
7511
- }
7512
- }
7513
-
7514
- var editorSize = display.wrapper.getBoundingClientRect();
7515
- // Used to ensure timeout re-tries don't fire when another extend
7516
- // happened in the meantime (clearTimeout isn't reliable -- at
7517
- // least on Chrome, the timeouts still happen even when cleared,
7518
- // if the clear happens after their scheduled firing time).
7519
- var counter = 0;
7520
-
7521
- function extend(e) {
7522
- var curCount = ++counter;
7523
- var cur = posFromMouse(cm, e, true, behavior.unit == "rectangle");
7524
- if (!cur) { return }
7525
- if (cmp(cur, lastPos) != 0) {
7526
- cm.curOp.focus = activeElt();
7527
- extendTo(cur);
7528
- var visible = visibleLines(display, doc);
7529
- if (cur.line >= visible.to || cur.line < visible.from)
7530
- { setTimeout(operation(cm, function () {if (counter == curCount) { extend(e); }}), 150); }
7531
- } else {
7532
- var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0;
7533
- if (outside) { setTimeout(operation(cm, function () {
7534
- if (counter != curCount) { return }
7535
- display.scroller.scrollTop += outside;
7536
- extend(e);
7537
- }), 50); }
7538
- }
7539
- }
7540
-
7541
- function done(e) {
7542
- cm.state.selectingText = false;
7543
- counter = Infinity;
7544
- // If e is null or undefined we interpret this as someone trying
7545
- // to explicitly cancel the selection rather than the user
7546
- // letting go of the mouse button.
7547
- if (e) {
7548
- e_preventDefault(e);
7549
- display.input.focus();
7550
- }
7551
- off(display.wrapper.ownerDocument, "mousemove", move);
7552
- off(display.wrapper.ownerDocument, "mouseup", up);
7553
- doc.history.lastSelOrigin = null;
7554
- }
7555
-
7556
- var move = operation(cm, function (e) {
7557
- if (e.buttons === 0 || !e_button(e)) { done(e); }
7558
- else { extend(e); }
7559
- });
7560
- var up = operation(cm, done);
7561
- cm.state.selectingText = up;
7562
- on(display.wrapper.ownerDocument, "mousemove", move);
7563
- on(display.wrapper.ownerDocument, "mouseup", up);
7564
- }
7565
-
7566
- // Used when mouse-selecting to adjust the anchor to the proper side
7567
- // of a bidi jump depending on the visual position of the head.
7568
- function bidiSimplify(cm, range$$1) {
7569
- var anchor = range$$1.anchor;
7570
- var head = range$$1.head;
7571
- var anchorLine = getLine(cm.doc, anchor.line);
7572
- if (cmp(anchor, head) == 0 && anchor.sticky == head.sticky) { return range$$1 }
7573
- var order = getOrder(anchorLine);
7574
- if (!order) { return range$$1 }
7575
- var index = getBidiPartAt(order, anchor.ch, anchor.sticky), part = order[index];
7576
- if (part.from != anchor.ch && part.to != anchor.ch) { return range$$1 }
7577
- var boundary = index + ((part.from == anchor.ch) == (part.level != 1) ? 0 : 1);
7578
- if (boundary == 0 || boundary == order.length) { return range$$1 }
7579
-
7580
- // Compute the relative visual position of the head compared to the
7581
- // anchor (<0 is to the left, >0 to the right)
7582
- var leftSide;
7583
- if (head.line != anchor.line) {
7584
- leftSide = (head.line - anchor.line) * (cm.doc.direction == "ltr" ? 1 : -1) > 0;
7585
- } else {
7586
- var headIndex = getBidiPartAt(order, head.ch, head.sticky);
7587
- var dir = headIndex - index || (head.ch - anchor.ch) * (part.level == 1 ? -1 : 1);
7588
- if (headIndex == boundary - 1 || headIndex == boundary)
7589
- { leftSide = dir < 0; }
7590
- else
7591
- { leftSide = dir > 0; }
7592
- }
7593
-
7594
- var usePart = order[boundary + (leftSide ? -1 : 0)];
7595
- var from = leftSide == (usePart.level == 1);
7596
- var ch = from ? usePart.from : usePart.to, sticky = from ? "after" : "before";
7597
- return anchor.ch == ch && anchor.sticky == sticky ? range$$1 : new Range(new Pos(anchor.line, ch, sticky), head)
7598
- }
7599
-
7600
-
7601
- // Determines whether an event happened in the gutter, and fires the
7602
- // handlers for the corresponding event.
7603
- function gutterEvent(cm, e, type, prevent) {
7604
- var mX, mY;
7605
- if (e.touches) {
7606
- mX = e.touches[0].clientX;
7607
- mY = e.touches[0].clientY;
7608
- } else {
7609
- try { mX = e.clientX; mY = e.clientY; }
7610
- catch(e) { return false }
7611
- }
7612
- if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) { return false }
7613
- if (prevent) { e_preventDefault(e); }
7614
-
7615
- var display = cm.display;
7616
- var lineBox = display.lineDiv.getBoundingClientRect();
7617
-
7618
- if (mY > lineBox.bottom || !hasHandler(cm, type)) { return e_defaultPrevented(e) }
7619
- mY -= lineBox.top - display.viewOffset;
7620
-
7621
- for (var i = 0; i < cm.display.gutterSpecs.length; ++i) {
7622
- var g = display.gutters.childNodes[i];
7623
- if (g && g.getBoundingClientRect().right >= mX) {
7624
- var line = lineAtHeight(cm.doc, mY);
7625
- var gutter = cm.display.gutterSpecs[i];
7626
- signal(cm, type, cm, line, gutter.className, e);
7627
- return e_defaultPrevented(e)
7628
- }
7629
- }
7630
- }
7631
-
7632
- function clickInGutter(cm, e) {
7633
- return gutterEvent(cm, e, "gutterClick", true)
7634
- }
7635
-
7636
- // CONTEXT MENU HANDLING
7637
-
7638
- // To make the context menu work, we need to briefly unhide the
7639
- // textarea (making it as unobtrusive as possible) to let the
7640
- // right-click take effect on it.
7641
- function onContextMenu(cm, e) {
7642
- if (eventInWidget(cm.display, e) || contextMenuInGutter(cm, e)) { return }
7643
- if (signalDOMEvent(cm, e, "contextmenu")) { return }
7644
- if (!captureRightClick) { cm.display.input.onContextMenu(e); }
7645
- }
7646
-
7647
- function contextMenuInGutter(cm, e) {
7648
- if (!hasHandler(cm, "gutterContextMenu")) { return false }
7649
- return gutterEvent(cm, e, "gutterContextMenu", false)
7650
- }
7651
-
7652
- function themeChanged(cm) {
7653
- cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") +
7654
- cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-");
7655
- clearCaches(cm);
7656
- }
7657
-
7658
- var Init = {toString: function(){return "CodeMirror.Init"}};
7659
-
7660
- var defaults = {};
7661
- var optionHandlers = {};
7662
-
7663
- function defineOptions(CodeMirror) {
7664
- var optionHandlers = CodeMirror.optionHandlers;
7665
-
7666
- function option(name, deflt, handle, notOnInit) {
7667
- CodeMirror.defaults[name] = deflt;
7668
- if (handle) { optionHandlers[name] =
7669
- notOnInit ? function (cm, val, old) {if (old != Init) { handle(cm, val, old); }} : handle; }
7670
- }
7671
-
7672
- CodeMirror.defineOption = option;
7673
-
7674
- // Passed to option handlers when there is no old value.
7675
- CodeMirror.Init = Init;
7676
-
7677
- // These two are, on init, called from the constructor because they
7678
- // have to be initialized before the editor can start at all.
7679
- option("value", "", function (cm, val) { return cm.setValue(val); }, true);
7680
- option("mode", null, function (cm, val) {
7681
- cm.doc.modeOption = val;
7682
- loadMode(cm);
7683
- }, true);
7684
-
7685
- option("indentUnit", 2, loadMode, true);
7686
- option("indentWithTabs", false);
7687
- option("smartIndent", true);
7688
- option("tabSize", 4, function (cm) {
7689
- resetModeState(cm);
7690
- clearCaches(cm);
7691
- regChange(cm);
7692
- }, true);
7693
-
7694
- option("lineSeparator", null, function (cm, val) {
7695
- cm.doc.lineSep = val;
7696
- if (!val) { return }
7697
- var newBreaks = [], lineNo = cm.doc.first;
7698
- cm.doc.iter(function (line) {
7699
- for (var pos = 0;;) {
7700
- var found = line.text.indexOf(val, pos);
7701
- if (found == -1) { break }
7702
- pos = found + val.length;
7703
- newBreaks.push(Pos(lineNo, found));
7704
- }
7705
- lineNo++;
7706
- });
7707
- for (var i = newBreaks.length - 1; i >= 0; i--)
7708
- { replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i].ch + val.length)); }
7709
- });
7710
- option("specialChars", /[\u0000-\u001f\u007f-\u009f\u00ad\u061c\u200b-\u200f\u2028\u2029\ufeff\ufff9-\ufffc]/g, function (cm, val, old) {
7711
- cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g");
7712
- if (old != Init) { cm.refresh(); }
7713
- });
7714
- option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function (cm) { return cm.refresh(); }, true);
7715
- option("electricChars", true);
7716
- option("inputStyle", mobile ? "contenteditable" : "textarea", function () {
7717
- throw new Error("inputStyle can not (yet) be changed in a running editor") // FIXME
7718
- }, true);
7719
- option("spellcheck", false, function (cm, val) { return cm.getInputField().spellcheck = val; }, true);
7720
- option("autocorrect", false, function (cm, val) { return cm.getInputField().autocorrect = val; }, true);
7721
- option("autocapitalize", false, function (cm, val) { return cm.getInputField().autocapitalize = val; }, true);
7722
- option("rtlMoveVisually", !windows);
7723
- option("wholeLineUpdateBefore", true);
7724
-
7725
- option("theme", "default", function (cm) {
7726
- themeChanged(cm);
7727
- updateGutters(cm);
7728
- }, true);
7729
- option("keyMap", "default", function (cm, val, old) {
7730
- var next = getKeyMap(val);
7731
- var prev = old != Init && getKeyMap(old);
7732
- if (prev && prev.detach) { prev.detach(cm, next); }
7733
- if (next.attach) { next.attach(cm, prev || null); }
7734
- });
7735
- option("extraKeys", null);
7736
- option("configureMouse", null);
7737
-
7738
- option("lineWrapping", false, wrappingChanged, true);
7739
- option("gutters", [], function (cm, val) {
7740
- cm.display.gutterSpecs = getGutters(val, cm.options.lineNumbers);
7741
- updateGutters(cm);
7742
- }, true);
7743
- option("fixedGutter", true, function (cm, val) {
7744
- cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0";
7745
- cm.refresh();
7746
- }, true);
7747
- option("coverGutterNextToScrollbar", false, function (cm) { return updateScrollbars(cm); }, true);
7748
- option("scrollbarStyle", "native", function (cm) {
7749
- initScrollbars(cm);
7750
- updateScrollbars(cm);
7751
- cm.display.scrollbars.setScrollTop(cm.doc.scrollTop);
7752
- cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft);
7753
- }, true);
7754
- option("lineNumbers", false, function (cm, val) {
7755
- cm.display.gutterSpecs = getGutters(cm.options.gutters, val);
7756
- updateGutters(cm);
7757
- }, true);
7758
- option("firstLineNumber", 1, updateGutters, true);
7759
- option("lineNumberFormatter", function (integer) { return integer; }, updateGutters, true);
7760
- option("showCursorWhenSelecting", false, updateSelection, true);
7761
-
7762
- option("resetSelectionOnContextMenu", true);
7763
- option("lineWiseCopyCut", true);
7764
- option("pasteLinesPerSelection", true);
7765
- option("selectionsMayTouch", false);
7766
-
7767
- option("readOnly", false, function (cm, val) {
7768
- if (val == "nocursor") {
7769
- onBlur(cm);
7770
- cm.display.input.blur();
7771
- }
7772
- cm.display.input.readOnlyChanged(val);
7773
- });
7774
- option("disableInput", false, function (cm, val) {if (!val) { cm.display.input.reset(); }}, true);
7775
- option("dragDrop", true, dragDropChanged);
7776
- option("allowDropFileTypes", null);
7777
-
7778
- option("cursorBlinkRate", 530);
7779
- option("cursorScrollMargin", 0);
7780
- option("cursorHeight", 1, updateSelection, true);
7781
- option("singleCursorHeightPerLine", true, updateSelection, true);
7782
- option("workTime", 100);
7783
- option("workDelay", 100);
7784
- option("flattenSpans", true, resetModeState, true);
7785
- option("addModeClass", false, resetModeState, true);
7786
- option("pollInterval", 100);
7787
- option("undoDepth", 200, function (cm, val) { return cm.doc.history.undoDepth = val; });
7788
- option("historyEventDelay", 1250);
7789
- option("viewportMargin", 10, function (cm) { return cm.refresh(); }, true);
7790
- option("maxHighlightLength", 10000, resetModeState, true);
7791
- option("moveInputWithCursor", true, function (cm, val) {
7792
- if (!val) { cm.display.input.resetPosition(); }
7793
- });
7794
-
7795
- option("tabindex", null, function (cm, val) { return cm.display.input.getField().tabIndex = val || ""; });
7796
- option("autofocus", null);
7797
- option("direction", "ltr", function (cm, val) { return cm.doc.setDirection(val); }, true);
7798
- option("phrases", null);
7799
- }
7800
-
7801
- function dragDropChanged(cm, value, old) {
7802
- var wasOn = old && old != Init;
7803
- if (!value != !wasOn) {
7804
- var funcs = cm.display.dragFunctions;
7805
- var toggle = value ? on : off;
7806
- toggle(cm.display.scroller, "dragstart", funcs.start);
7807
- toggle(cm.display.scroller, "dragenter", funcs.enter);
7808
- toggle(cm.display.scroller, "dragover", funcs.over);
7809
- toggle(cm.display.scroller, "dragleave", funcs.leave);
7810
- toggle(cm.display.scroller, "drop", funcs.drop);
7811
- }
7812
- }
7813
-
7814
- function wrappingChanged(cm) {
7815
- if (cm.options.lineWrapping) {
7816
- addClass(cm.display.wrapper, "CodeMirror-wrap");
7817
- cm.display.sizer.style.minWidth = "";
7818
- cm.display.sizerWidth = null;
7819
- } else {
7820
- rmClass(cm.display.wrapper, "CodeMirror-wrap");
7821
- findMaxLine(cm);
7822
- }
7823
- estimateLineHeights(cm);
7824
- regChange(cm);
7825
- clearCaches(cm);
7826
- setTimeout(function () { return updateScrollbars(cm); }, 100);
7827
- }
7828
-
7829
- // A CodeMirror instance represents an editor. This is the object
7830
- // that user code is usually dealing with.
7831
-
7832
- function CodeMirror(place, options) {
7833
- var this$1 = this;
7834
-
7835
- if (!(this instanceof CodeMirror)) { return new CodeMirror(place, options) }
7836
-
7837
- this.options = options = options ? copyObj(options) : {};
7838
- // Determine effective options based on given values and defaults.
7839
- copyObj(defaults, options, false);
7840
-
7841
- var doc = options.value;
7842
- if (typeof doc == "string") { doc = new Doc(doc, options.mode, null, options.lineSeparator, options.direction); }
7843
- else if (options.mode) { doc.modeOption = options.mode; }
7844
- this.doc = doc;
7845
-
7846
- var input = new CodeMirror.inputStyles[options.inputStyle](this);
7847
- var display = this.display = new Display(place, doc, input, options);
7848
- display.wrapper.CodeMirror = this;
7849
- themeChanged(this);
7850
- if (options.lineWrapping)
7851
- { this.display.wrapper.className += " CodeMirror-wrap"; }
7852
- initScrollbars(this);
7853
-
7854
- this.state = {
7855
- keyMaps: [], // stores maps added by addKeyMap
7856
- overlays: [], // highlighting overlays, as added by addOverlay
7857
- modeGen: 0, // bumped when mode/overlay changes, used to invalidate highlighting info
7858
- overwrite: false,
7859
- delayingBlurEvent: false,
7860
- focused: false,
7861
- suppressEdits: false, // used to disable editing during key handlers when in readOnly mode
7862
- pasteIncoming: -1, cutIncoming: -1, // help recognize paste/cut edits in input.poll
7863
- selectingText: false,
7864
- draggingText: false,
7865
- highlight: new Delayed(), // stores highlight worker timeout
7866
- keySeq: null, // Unfinished key sequence
7867
- specialChars: null
7868
- };
7869
-
7870
- if (options.autofocus && !mobile) { display.input.focus(); }
7871
-
7872
- // Override magic textarea content restore that IE sometimes does
7873
- // on our hidden textarea on reload
7874
- if (ie && ie_version < 11) { setTimeout(function () { return this$1.display.input.reset(true); }, 20); }
7875
-
7876
- registerEventHandlers(this);
7877
- ensureGlobalHandlers();
7878
-
7879
- startOperation(this);
7880
- this.curOp.forceUpdate = true;
7881
- attachDoc(this, doc);
7882
-
7883
- if ((options.autofocus && !mobile) || this.hasFocus())
7884
- { setTimeout(bind(onFocus, this), 20); }
7885
- else
7886
- { onBlur(this); }
7887
-
7888
- for (var opt in optionHandlers) { if (optionHandlers.hasOwnProperty(opt))
7889
- { optionHandlers[opt](this$1, options[opt], Init); } }
7890
- maybeUpdateLineNumberWidth(this);
7891
- if (options.finishInit) { options.finishInit(this); }
7892
- for (var i = 0; i < initHooks.length; ++i) { initHooks[i](this$1); }
7893
- endOperation(this);
7894
- // Suppress optimizelegibility in Webkit, since it breaks text
7895
- // measuring on line wrapping boundaries.
7896
- if (webkit && options.lineWrapping &&
7897
- getComputedStyle(display.lineDiv).textRendering == "optimizelegibility")
7898
- { display.lineDiv.style.textRendering = "auto"; }
7899
- }
7900
-
7901
- // The default configuration options.
7902
- CodeMirror.defaults = defaults;
7903
- // Functions to run when options are changed.
7904
- CodeMirror.optionHandlers = optionHandlers;
7905
-
7906
- // Attach the necessary event handlers when initializing the editor
7907
- function registerEventHandlers(cm) {
7908
- var d = cm.display;
7909
- on(d.scroller, "mousedown", operation(cm, onMouseDown));
7910
- // Older IE's will not fire a second mousedown for a double click
7911
- if (ie && ie_version < 11)
7912
- { on(d.scroller, "dblclick", operation(cm, function (e) {
7913
- if (signalDOMEvent(cm, e)) { return }
7914
- var pos = posFromMouse(cm, e);
7915
- if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) { return }
7916
- e_preventDefault(e);
7917
- var word = cm.findWordAt(pos);
7918
- extendSelection(cm.doc, word.anchor, word.head);
7919
- })); }
7920
- else
7921
- { on(d.scroller, "dblclick", function (e) { return signalDOMEvent(cm, e) || e_preventDefault(e); }); }
7922
- // Some browsers fire contextmenu *after* opening the menu, at
7923
- // which point we can't mess with it anymore. Context menu is
7924
- // handled in onMouseDown for these browsers.
7925
- on(d.scroller, "contextmenu", function (e) { return onContextMenu(cm, e); });
7926
-
7927
- // Used to suppress mouse event handling when a touch happens
7928
- var touchFinished, prevTouch = {end: 0};
7929
- function finishTouch() {
7930
- if (d.activeTouch) {
7931
- touchFinished = setTimeout(function () { return d.activeTouch = null; }, 1000);
7932
- prevTouch = d.activeTouch;
7933
- prevTouch.end = +new Date;
7934
- }
7935
- }
7936
- function isMouseLikeTouchEvent(e) {
7937
- if (e.touches.length != 1) { return false }
7938
- var touch = e.touches[0];
7939
- return touch.radiusX <= 1 && touch.radiusY <= 1
7940
- }
7941
- function farAway(touch, other) {
7942
- if (other.left == null) { return true }
7943
- var dx = other.left - touch.left, dy = other.top - touch.top;
7944
- return dx * dx + dy * dy > 20 * 20
7945
- }
7946
- on(d.scroller, "touchstart", function (e) {
7947
- if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e) && !clickInGutter(cm, e)) {
7948
- d.input.ensurePolled();
7949
- clearTimeout(touchFinished);
7950
- var now = +new Date;
7951
- d.activeTouch = {start: now, moved: false,
7952
- prev: now - prevTouch.end <= 300 ? prevTouch : null};
7953
- if (e.touches.length == 1) {
7954
- d.activeTouch.left = e.touches[0].pageX;
7955
- d.activeTouch.top = e.touches[0].pageY;
7956
- }
7957
- }
7958
- });
7959
- on(d.scroller, "touchmove", function () {
7960
- if (d.activeTouch) { d.activeTouch.moved = true; }
7961
- });
7962
- on(d.scroller, "touchend", function (e) {
7963
- var touch = d.activeTouch;
7964
- if (touch && !eventInWidget(d, e) && touch.left != null &&
7965
- !touch.moved && new Date - touch.start < 300) {
7966
- var pos = cm.coordsChar(d.activeTouch, "page"), range;
7967
- if (!touch.prev || farAway(touch, touch.prev)) // Single tap
7968
- { range = new Range(pos, pos); }
7969
- else if (!touch.prev.prev || farAway(touch, touch.prev.prev)) // Double tap
7970
- { range = cm.findWordAt(pos); }
7971
- else // Triple tap
7972
- { range = new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))); }
7973
- cm.setSelection(range.anchor, range.head);
7974
- cm.focus();
7975
- e_preventDefault(e);
7976
- }
7977
- finishTouch();
7978
- });
7979
- on(d.scroller, "touchcancel", finishTouch);
7980
-
7981
- // Sync scrolling between fake scrollbars and real scrollable
7982
- // area, ensure viewport is updated when scrolling.
7983
- on(d.scroller, "scroll", function () {
7984
- if (d.scroller.clientHeight) {
7985
- updateScrollTop(cm, d.scroller.scrollTop);
7986
- setScrollLeft(cm, d.scroller.scrollLeft, true);
7987
- signal(cm, "scroll", cm);
7988
- }
7989
- });
7990
-
7991
- // Listen to wheel events in order to try and update the viewport on time.
7992
- on(d.scroller, "mousewheel", function (e) { return onScrollWheel(cm, e); });
7993
- on(d.scroller, "DOMMouseScroll", function (e) { return onScrollWheel(cm, e); });
7994
-
7995
- // Prevent wrapper from ever scrolling
7996
- on(d.wrapper, "scroll", function () { return d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; });
7997
-
7998
- d.dragFunctions = {
7999
- enter: function (e) {if (!signalDOMEvent(cm, e)) { e_stop(e); }},
8000
- over: function (e) {if (!signalDOMEvent(cm, e)) { onDragOver(cm, e); e_stop(e); }},
8001
- start: function (e) { return onDragStart(cm, e); },
8002
- drop: operation(cm, onDrop),
8003
- leave: function (e) {if (!signalDOMEvent(cm, e)) { clearDragCursor(cm); }}
8004
- };
8005
-
8006
- var inp = d.input.getField();
8007
- on(inp, "keyup", function (e) { return onKeyUp.call(cm, e); });
8008
- on(inp, "keydown", operation(cm, onKeyDown));
8009
- on(inp, "keypress", operation(cm, onKeyPress));
8010
- on(inp, "focus", function (e) { return onFocus(cm, e); });
8011
- on(inp, "blur", function (e) { return onBlur(cm, e); });
8012
- }
8013
-
8014
- var initHooks = [];
8015
- CodeMirror.defineInitHook = function (f) { return initHooks.push(f); };
8016
-
8017
- // Indent the given line. The how parameter can be "smart",
8018
- // "add"/null, "subtract", or "prev". When aggressive is false
8019
- // (typically set to true for forced single-line indents), empty
8020
- // lines are not indented, and places where the mode returns Pass
8021
- // are left alone.
8022
- function indentLine(cm, n, how, aggressive) {
8023
- var doc = cm.doc, state;
8024
- if (how == null) { how = "add"; }
8025
- if (how == "smart") {
8026
- // Fall back to "prev" when the mode doesn't have an indentation
8027
- // method.
8028
- if (!doc.mode.indent) { how = "prev"; }
8029
- else { state = getContextBefore(cm, n).state; }
8030
- }
8031
-
8032
- var tabSize = cm.options.tabSize;
8033
- var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize);
8034
- if (line.stateAfter) { line.stateAfter = null; }
8035
- var curSpaceString = line.text.match(/^\s*/)[0], indentation;
8036
- if (!aggressive && !/\S/.test(line.text)) {
8037
- indentation = 0;
8038
- how = "not";
8039
- } else if (how == "smart") {
8040
- indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text);
8041
- if (indentation == Pass || indentation > 150) {
8042
- if (!aggressive) { return }
8043
- how = "prev";
8044
- }
8045
- }
8046
- if (how == "prev") {
8047
- if (n > doc.first) { indentation = countColumn(getLine(doc, n-1).text, null, tabSize); }
8048
- else { indentation = 0; }
8049
- } else if (how == "add") {
8050
- indentation = curSpace + cm.options.indentUnit;
8051
- } else if (how == "subtract") {
8052
- indentation = curSpace - cm.options.indentUnit;
8053
- } else if (typeof how == "number") {
8054
- indentation = curSpace + how;
8055
- }
8056
- indentation = Math.max(0, indentation);
8057
-
8058
- var indentString = "", pos = 0;
8059
- if (cm.options.indentWithTabs)
8060
- { for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";} }
8061
- if (pos < indentation) { indentString += spaceStr(indentation - pos); }
8062
-
8063
- if (indentString != curSpaceString) {
8064
- replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input");
8065
- line.stateAfter = null;
8066
- return true
8067
- } else {
8068
- // Ensure that, if the cursor was in the whitespace at the start
8069
- // of the line, it is moved to the end of that space.
8070
- for (var i$1 = 0; i$1 < doc.sel.ranges.length; i$1++) {
8071
- var range = doc.sel.ranges[i$1];
8072
- if (range.head.line == n && range.head.ch < curSpaceString.length) {
8073
- var pos$1 = Pos(n, curSpaceString.length);
8074
- replaceOneSelection(doc, i$1, new Range(pos$1, pos$1));
8075
- break
8076
- }
8077
- }
8078
- }
8079
- }
8080
-
8081
- // This will be set to a {lineWise: bool, text: [string]} object, so
8082
- // that, when pasting, we know what kind of selections the copied
8083
- // text was made out of.
8084
- var lastCopied = null;
8085
-
8086
- function setLastCopied(newLastCopied) {
8087
- lastCopied = newLastCopied;
8088
- }
8089
-
8090
- function applyTextInput(cm, inserted, deleted, sel, origin) {
8091
- var doc = cm.doc;
8092
- cm.display.shift = false;
8093
- if (!sel) { sel = doc.sel; }
8094
-
8095
- var recent = +new Date - 200;
8096
- var paste = origin == "paste" || cm.state.pasteIncoming > recent;
8097
- var textLines = splitLinesAuto(inserted), multiPaste = null;
8098
- // When pasting N lines into N selections, insert one line per selection
8099
- if (paste && sel.ranges.length > 1) {
8100
- if (lastCopied && lastCopied.text.join("\n") == inserted) {
8101
- if (sel.ranges.length % lastCopied.text.length == 0) {
8102
- multiPaste = [];
8103
- for (var i = 0; i < lastCopied.text.length; i++)
8104
- { multiPaste.push(doc.splitLines(lastCopied.text[i])); }
8105
- }
8106
- } else if (textLines.length == sel.ranges.length && cm.options.pasteLinesPerSelection) {
8107
- multiPaste = map(textLines, function (l) { return [l]; });
8108
- }
8109
- }
8110
-
8111
- var updateInput = cm.curOp.updateInput;
8112
- // Normal behavior is to insert the new text into every selection
8113
- for (var i$1 = sel.ranges.length - 1; i$1 >= 0; i$1--) {
8114
- var range$$1 = sel.ranges[i$1];
8115
- var from = range$$1.from(), to = range$$1.to();
8116
- if (range$$1.empty()) {
8117
- if (deleted && deleted > 0) // Handle deletion
8118
- { from = Pos(from.line, from.ch - deleted); }
8119
- else if (cm.state.overwrite && !paste) // Handle overwrite
8120
- { to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length)); }
8121
- else if (paste && lastCopied && lastCopied.lineWise && lastCopied.text.join("\n") == inserted)
8122
- { from = to = Pos(from.line, 0); }
8123
- }
8124
- var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i$1 % multiPaste.length] : textLines,
8125
- origin: origin || (paste ? "paste" : cm.state.cutIncoming > recent ? "cut" : "+input")};
8126
- makeChange(cm.doc, changeEvent);
8127
- signalLater(cm, "inputRead", cm, changeEvent);
8128
- }
8129
- if (inserted && !paste)
8130
- { triggerElectric(cm, inserted); }
8131
-
8132
- ensureCursorVisible(cm);
8133
- if (cm.curOp.updateInput < 2) { cm.curOp.updateInput = updateInput; }
8134
- cm.curOp.typing = true;
8135
- cm.state.pasteIncoming = cm.state.cutIncoming = -1;
8136
- }
8137
-
8138
- function handlePaste(e, cm) {
8139
- var pasted = e.clipboardData && e.clipboardData.getData("Text");
8140
- if (pasted) {
8141
- e.preventDefault();
8142
- if (!cm.isReadOnly() && !cm.options.disableInput)
8143
- { runInOp(cm, function () { return applyTextInput(cm, pasted, 0, null, "paste"); }); }
8144
- return true
8145
- }
8146
- }
8147
-
8148
- function triggerElectric(cm, inserted) {
8149
- // When an 'electric' character is inserted, immediately trigger a reindent
8150
- if (!cm.options.electricChars || !cm.options.smartIndent) { return }
8151
- var sel = cm.doc.sel;
8152
-
8153
- for (var i = sel.ranges.length - 1; i >= 0; i--) {
8154
- var range$$1 = sel.ranges[i];
8155
- if (range$$1.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range$$1.head.line)) { continue }
8156
- var mode = cm.getModeAt(range$$1.head);
8157
- var indented = false;
8158
- if (mode.electricChars) {
8159
- for (var j = 0; j < mode.electricChars.length; j++)
8160
- { if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) {
8161
- indented = indentLine(cm, range$$1.head.line, "smart");
8162
- break
8163
- } }
8164
- } else if (mode.electricInput) {
8165
- if (mode.electricInput.test(getLine(cm.doc, range$$1.head.line).text.slice(0, range$$1.head.ch)))
8166
- { indented = indentLine(cm, range$$1.head.line, "smart"); }
8167
- }
8168
- if (indented) { signalLater(cm, "electricInput", cm, range$$1.head.line); }
8169
- }
8170
- }
8171
-
8172
- function copyableRanges(cm) {
8173
- var text = [], ranges = [];
8174
- for (var i = 0; i < cm.doc.sel.ranges.length; i++) {
8175
- var line = cm.doc.sel.ranges[i].head.line;
8176
- var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)};
8177
- ranges.push(lineRange);
8178
- text.push(cm.getRange(lineRange.anchor, lineRange.head));
8179
- }
8180
- return {text: text, ranges: ranges}
8181
- }
8182
-
8183
- function disableBrowserMagic(field, spellcheck, autocorrect, autocapitalize) {
8184
- field.setAttribute("autocorrect", autocorrect ? "" : "off");
8185
- field.setAttribute("autocapitalize", autocapitalize ? "" : "off");
8186
- field.setAttribute("spellcheck", !!spellcheck);
8187
- }
8188
-
8189
- function hiddenTextarea() {
8190
- var te = elt("textarea", null, null, "position: absolute; bottom: -1em; padding: 0; width: 1px; height: 1em; outline: none");
8191
- var div = elt("div", [te], null, "overflow: hidden; position: relative; width: 3px; height: 0px;");
8192
- // The textarea is kept positioned near the cursor to prevent the
8193
- // fact that it'll be scrolled into view on input from scrolling
8194
- // our fake cursor out of view. On webkit, when wrap=off, paste is
8195
- // very slow. So make the area wide instead.
8196
- if (webkit) { te.style.width = "1000px"; }
8197
- else { te.setAttribute("wrap", "off"); }
8198
- // If border: 0; -- iOS fails to open keyboard (issue #1287)
8199
- if (ios) { te.style.border = "1px solid black"; }
8200
- disableBrowserMagic(te);
8201
- return div
8202
- }
8203
-
8204
- // The publicly visible API. Note that methodOp(f) means
8205
- // 'wrap f in an operation, performed on its `this` parameter'.
8206
-
8207
- // This is not the complete set of editor methods. Most of the
8208
- // methods defined on the Doc type are also injected into
8209
- // CodeMirror.prototype, for backwards compatibility and
8210
- // convenience.
8211
-
8212
- function addEditorMethods(CodeMirror) {
8213
- var optionHandlers = CodeMirror.optionHandlers;
8214
-
8215
- var helpers = CodeMirror.helpers = {};
8216
-
8217
- CodeMirror.prototype = {
8218
- constructor: CodeMirror,
8219
- focus: function(){window.focus(); this.display.input.focus();},
8220
-
8221
- setOption: function(option, value) {
8222
- var options = this.options, old = options[option];
8223
- if (options[option] == value && option != "mode") { return }
8224
- options[option] = value;
8225
- if (optionHandlers.hasOwnProperty(option))
8226
- { operation(this, optionHandlers[option])(this, value, old); }
8227
- signal(this, "optionChange", this, option);
8228
- },
8229
-
8230
- getOption: function(option) {return this.options[option]},
8231
- getDoc: function() {return this.doc},
8232
-
8233
- addKeyMap: function(map$$1, bottom) {
8234
- this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map$$1));
8235
- },
8236
- removeKeyMap: function(map$$1) {
8237
- var maps = this.state.keyMaps;
8238
- for (var i = 0; i < maps.length; ++i)
8239
- { if (maps[i] == map$$1 || maps[i].name == map$$1) {
8240
- maps.splice(i, 1);
8241
- return true
8242
- } }
8243
- },
8244
-
8245
- addOverlay: methodOp(function(spec, options) {
8246
- var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec);
8247
- if (mode.startState) { throw new Error("Overlays may not be stateful.") }
8248
- insertSorted(this.state.overlays,
8249
- {mode: mode, modeSpec: spec, opaque: options && options.opaque,
8250
- priority: (options && options.priority) || 0},
8251
- function (overlay) { return overlay.priority; });
8252
- this.state.modeGen++;
8253
- regChange(this);
8254
- }),
8255
- removeOverlay: methodOp(function(spec) {
8256
- var this$1 = this;
8257
-
8258
- var overlays = this.state.overlays;
8259
- for (var i = 0; i < overlays.length; ++i) {
8260
- var cur = overlays[i].modeSpec;
8261
- if (cur == spec || typeof spec == "string" && cur.name == spec) {
8262
- overlays.splice(i, 1);
8263
- this$1.state.modeGen++;
8264
- regChange(this$1);
8265
- return
8266
- }
8267
- }
8268
- }),
8269
-
8270
- indentLine: methodOp(function(n, dir, aggressive) {
8271
- if (typeof dir != "string" && typeof dir != "number") {
8272
- if (dir == null) { dir = this.options.smartIndent ? "smart" : "prev"; }
8273
- else { dir = dir ? "add" : "subtract"; }
8274
- }
8275
- if (isLine(this.doc, n)) { indentLine(this, n, dir, aggressive); }
8276
- }),
8277
- indentSelection: methodOp(function(how) {
8278
- var this$1 = this;
8279
-
8280
- var ranges = this.doc.sel.ranges, end = -1;
8281
- for (var i = 0; i < ranges.length; i++) {
8282
- var range$$1 = ranges[i];
8283
- if (!range$$1.empty()) {
8284
- var from = range$$1.from(), to = range$$1.to();
8285
- var start = Math.max(end, from.line);
8286
- end = Math.min(this$1.lastLine(), to.line - (to.ch ? 0 : 1)) + 1;
8287
- for (var j = start; j < end; ++j)
8288
- { indentLine(this$1, j, how); }
8289
- var newRanges = this$1.doc.sel.ranges;
8290
- if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].from().ch > 0)
8291
- { replaceOneSelection(this$1.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll); }
8292
- } else if (range$$1.head.line > end) {
8293
- indentLine(this$1, range$$1.head.line, how, true);
8294
- end = range$$1.head.line;
8295
- if (i == this$1.doc.sel.primIndex) { ensureCursorVisible(this$1); }
8296
- }
8297
- }
8298
- }),
8299
-
8300
- // Fetch the parser token for a given character. Useful for hacks
8301
- // that want to inspect the mode state (say, for completion).
8302
- getTokenAt: function(pos, precise) {
8303
- return takeToken(this, pos, precise)
8304
- },
8305
-
8306
- getLineTokens: function(line, precise) {
8307
- return takeToken(this, Pos(line), precise, true)
8308
- },
8309
-
8310
- getTokenTypeAt: function(pos) {
8311
- pos = clipPos(this.doc, pos);
8312
- var styles = getLineStyles(this, getLine(this.doc, pos.line));
8313
- var before = 0, after = (styles.length - 1) / 2, ch = pos.ch;
8314
- var type;
8315
- if (ch == 0) { type = styles[2]; }
8316
- else { for (;;) {
8317
- var mid = (before + after) >> 1;
8318
- if ((mid ? styles[mid * 2 - 1] : 0) >= ch) { after = mid; }
8319
- else if (styles[mid * 2 + 1] < ch) { before = mid + 1; }
8320
- else { type = styles[mid * 2 + 2]; break }
8321
- } }
8322
- var cut = type ? type.indexOf("overlay ") : -1;
8323
- return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1)
8324
- },
8325
-
8326
- getModeAt: function(pos) {
8327
- var mode = this.doc.mode;
8328
- if (!mode.innerMode) { return mode }
8329
- return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode
8330
- },
8331
-
8332
- getHelper: function(pos, type) {
8333
- return this.getHelpers(pos, type)[0]
8334
- },
8335
-
8336
- getHelpers: function(pos, type) {
8337
- var this$1 = this;
8338
-
8339
- var found = [];
8340
- if (!helpers.hasOwnProperty(type)) { return found }
8341
- var help = helpers[type], mode = this.getModeAt(pos);
8342
- if (typeof mode[type] == "string") {
8343
- if (help[mode[type]]) { found.push(help[mode[type]]); }
8344
- } else if (mode[type]) {
8345
- for (var i = 0; i < mode[type].length; i++) {
8346
- var val = help[mode[type][i]];
8347
- if (val) { found.push(val); }
8348
- }
8349
- } else if (mode.helperType && help[mode.helperType]) {
8350
- found.push(help[mode.helperType]);
8351
- } else if (help[mode.name]) {
8352
- found.push(help[mode.name]);
8353
- }
8354
- for (var i$1 = 0; i$1 < help._global.length; i$1++) {
8355
- var cur = help._global[i$1];
8356
- if (cur.pred(mode, this$1) && indexOf(found, cur.val) == -1)
8357
- { found.push(cur.val); }
8358
- }
8359
- return found
8360
- },
8361
-
8362
- getStateAfter: function(line, precise) {
8363
- var doc = this.doc;
8364
- line = clipLine(doc, line == null ? doc.first + doc.size - 1: line);
8365
- return getContextBefore(this, line + 1, precise).state
8366
- },
8367
-
8368
- cursorCoords: function(start, mode) {
8369
- var pos, range$$1 = this.doc.sel.primary();
8370
- if (start == null) { pos = range$$1.head; }
8371
- else if (typeof start == "object") { pos = clipPos(this.doc, start); }
8372
- else { pos = start ? range$$1.from() : range$$1.to(); }
8373
- return cursorCoords(this, pos, mode || "page")
8374
- },
8375
-
8376
- charCoords: function(pos, mode) {
8377
- return charCoords(this, clipPos(this.doc, pos), mode || "page")
8378
- },
8379
-
8380
- coordsChar: function(coords, mode) {
8381
- coords = fromCoordSystem(this, coords, mode || "page");
8382
- return coordsChar(this, coords.left, coords.top)
8383
- },
8384
-
8385
- lineAtHeight: function(height, mode) {
8386
- height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top;
8387
- return lineAtHeight(this.doc, height + this.display.viewOffset)
8388
- },
8389
- heightAtLine: function(line, mode, includeWidgets) {
8390
- var end = false, lineObj;
8391
- if (typeof line == "number") {
8392
- var last = this.doc.first + this.doc.size - 1;
8393
- if (line < this.doc.first) { line = this.doc.first; }
8394
- else if (line > last) { line = last; end = true; }
8395
- lineObj = getLine(this.doc, line);
8396
- } else {
8397
- lineObj = line;
8398
- }
8399
- return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || "page", includeWidgets || end).top +
8400
- (end ? this.doc.height - heightAtLine(lineObj) : 0)
8401
- },
8402
-
8403
- defaultTextHeight: function() { return textHeight(this.display) },
8404
- defaultCharWidth: function() { return charWidth(this.display) },
8405
-
8406
- getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo}},
8407
-
8408
- addWidget: function(pos, node, scroll, vert, horiz) {
8409
- var display = this.display;
8410
- pos = cursorCoords(this, clipPos(this.doc, pos));
8411
- var top = pos.bottom, left = pos.left;
8412
- node.style.position = "absolute";
8413
- node.setAttribute("cm-ignore-events", "true");
8414
- this.display.input.setUneditable(node);
8415
- display.sizer.appendChild(node);
8416
- if (vert == "over") {
8417
- top = pos.top;
8418
- } else if (vert == "above" || vert == "near") {
8419
- var vspace = Math.max(display.wrapper.clientHeight, this.doc.height),
8420
- hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth);
8421
- // Default to positioning above (if specified and possible); otherwise default to positioning below
8422
- if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight)
8423
- { top = pos.top - node.offsetHeight; }
8424
- else if (pos.bottom + node.offsetHeight <= vspace)
8425
- { top = pos.bottom; }
8426
- if (left + node.offsetWidth > hspace)
8427
- { left = hspace - node.offsetWidth; }
8428
- }
8429
- node.style.top = top + "px";
8430
- node.style.left = node.style.right = "";
8431
- if (horiz == "right") {
8432
- left = display.sizer.clientWidth - node.offsetWidth;
8433
- node.style.right = "0px";
8434
- } else {
8435
- if (horiz == "left") { left = 0; }
8436
- else if (horiz == "middle") { left = (display.sizer.clientWidth - node.offsetWidth) / 2; }
8437
- node.style.left = left + "px";
8438
- }
8439
- if (scroll)
8440
- { scrollIntoView(this, {left: left, top: top, right: left + node.offsetWidth, bottom: top + node.offsetHeight}); }
8441
- },
8442
-
8443
- triggerOnKeyDown: methodOp(onKeyDown),
8444
- triggerOnKeyPress: methodOp(onKeyPress),
8445
- triggerOnKeyUp: onKeyUp,
8446
- triggerOnMouseDown: methodOp(onMouseDown),
8447
-
8448
- execCommand: function(cmd) {
8449
- if (commands.hasOwnProperty(cmd))
8450
- { return commands[cmd].call(null, this) }
8451
- },
8452
-
8453
- triggerElectric: methodOp(function(text) { triggerElectric(this, text); }),
8454
-
8455
- findPosH: function(from, amount, unit, visually) {
8456
- var this$1 = this;
8457
-
8458
- var dir = 1;
8459
- if (amount < 0) { dir = -1; amount = -amount; }
8460
- var cur = clipPos(this.doc, from);
8461
- for (var i = 0; i < amount; ++i) {
8462
- cur = findPosH(this$1.doc, cur, dir, unit, visually);
8463
- if (cur.hitSide) { break }
8464
- }
8465
- return cur
8466
- },
8467
-
8468
- moveH: methodOp(function(dir, unit) {
8469
- var this$1 = this;
8470
-
8471
- this.extendSelectionsBy(function (range$$1) {
8472
- if (this$1.display.shift || this$1.doc.extend || range$$1.empty())
8473
- { return findPosH(this$1.doc, range$$1.head, dir, unit, this$1.options.rtlMoveVisually) }
8474
- else
8475
- { return dir < 0 ? range$$1.from() : range$$1.to() }
8476
- }, sel_move);
8477
- }),
8478
-
8479
- deleteH: methodOp(function(dir, unit) {
8480
- var sel = this.doc.sel, doc = this.doc;
8481
- if (sel.somethingSelected())
8482
- { doc.replaceSelection("", null, "+delete"); }
8483
- else
8484
- { deleteNearSelection(this, function (range$$1) {
8485
- var other = findPosH(doc, range$$1.head, dir, unit, false);
8486
- return dir < 0 ? {from: other, to: range$$1.head} : {from: range$$1.head, to: other}
8487
- }); }
8488
- }),
8489
-
8490
- findPosV: function(from, amount, unit, goalColumn) {
8491
- var this$1 = this;
8492
-
8493
- var dir = 1, x = goalColumn;
8494
- if (amount < 0) { dir = -1; amount = -amount; }
8495
- var cur = clipPos(this.doc, from);
8496
- for (var i = 0; i < amount; ++i) {
8497
- var coords = cursorCoords(this$1, cur, "div");
8498
- if (x == null) { x = coords.left; }
8499
- else { coords.left = x; }
8500
- cur = findPosV(this$1, coords, dir, unit);
8501
- if (cur.hitSide) { break }
8502
- }
8503
- return cur
8504
- },
8505
-
8506
- moveV: methodOp(function(dir, unit) {
8507
- var this$1 = this;
8508
-
8509
- var doc = this.doc, goals = [];
8510
- var collapse = !this.display.shift && !doc.extend && doc.sel.somethingSelected();
8511
- doc.extendSelectionsBy(function (range$$1) {
8512
- if (collapse)
8513
- { return dir < 0 ? range$$1.from() : range$$1.to() }
8514
- var headPos = cursorCoords(this$1, range$$1.head, "div");
8515
- if (range$$1.goalColumn != null) { headPos.left = range$$1.goalColumn; }
8516
- goals.push(headPos.left);
8517
- var pos = findPosV(this$1, headPos, dir, unit);
8518
- if (unit == "page" && range$$1 == doc.sel.primary())
8519
- { addToScrollTop(this$1, charCoords(this$1, pos, "div").top - headPos.top); }
8520
- return pos
8521
- }, sel_move);
8522
- if (goals.length) { for (var i = 0; i < doc.sel.ranges.length; i++)
8523
- { doc.sel.ranges[i].goalColumn = goals[i]; } }
8524
- }),
8525
-
8526
- // Find the word at the given position (as returned by coordsChar).
8527
- findWordAt: function(pos) {
8528
- var doc = this.doc, line = getLine(doc, pos.line).text;
8529
- var start = pos.ch, end = pos.ch;
8530
- if (line) {
8531
- var helper = this.getHelper(pos, "wordChars");
8532
- if ((pos.sticky == "before" || end == line.length) && start) { --start; } else { ++end; }
8533
- var startChar = line.charAt(start);
8534
- var check = isWordChar(startChar, helper)
8535
- ? function (ch) { return isWordChar(ch, helper); }
8536
- : /\s/.test(startChar) ? function (ch) { return /\s/.test(ch); }
8537
- : function (ch) { return (!/\s/.test(ch) && !isWordChar(ch)); };
8538
- while (start > 0 && check(line.charAt(start - 1))) { --start; }
8539
- while (end < line.length && check(line.charAt(end))) { ++end; }
8540
- }
8541
- return new Range(Pos(pos.line, start), Pos(pos.line, end))
8542
- },
8543
-
8544
- toggleOverwrite: function(value) {
8545
- if (value != null && value == this.state.overwrite) { return }
8546
- if (this.state.overwrite = !this.state.overwrite)
8547
- { addClass(this.display.cursorDiv, "CodeMirror-overwrite"); }
8548
- else
8549
- { rmClass(this.display.cursorDiv, "CodeMirror-overwrite"); }
8550
-
8551
- signal(this, "overwriteToggle", this, this.state.overwrite);
8552
- },
8553
- hasFocus: function() { return this.display.input.getField() == activeElt() },
8554
- isReadOnly: function() { return !!(this.options.readOnly || this.doc.cantEdit) },
8555
-
8556
- scrollTo: methodOp(function (x, y) { scrollToCoords(this, x, y); }),
8557
- getScrollInfo: function() {
8558
- var scroller = this.display.scroller;
8559
- return {left: scroller.scrollLeft, top: scroller.scrollTop,
8560
- height: scroller.scrollHeight - scrollGap(this) - this.display.barHeight,
8561
- width: scroller.scrollWidth - scrollGap(this) - this.display.barWidth,
8562
- clientHeight: displayHeight(this), clientWidth: displayWidth(this)}
8563
- },
8564
-
8565
- scrollIntoView: methodOp(function(range$$1, margin) {
8566
- if (range$$1 == null) {
8567
- range$$1 = {from: this.doc.sel.primary().head, to: null};
8568
- if (margin == null) { margin = this.options.cursorScrollMargin; }
8569
- } else if (typeof range$$1 == "number") {
8570
- range$$1 = {from: Pos(range$$1, 0), to: null};
8571
- } else if (range$$1.from == null) {
8572
- range$$1 = {from: range$$1, to: null};
8573
- }
8574
- if (!range$$1.to) { range$$1.to = range$$1.from; }
8575
- range$$1.margin = margin || 0;
8576
-
8577
- if (range$$1.from.line != null) {
8578
- scrollToRange(this, range$$1);
8579
- } else {
8580
- scrollToCoordsRange(this, range$$1.from, range$$1.to, range$$1.margin);
8581
- }
8582
- }),
8583
-
8584
- setSize: methodOp(function(width, height) {
8585
- var this$1 = this;
8586
-
8587
- var interpret = function (val) { return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val; };
8588
- if (width != null) { this.display.wrapper.style.width = interpret(width); }
8589
- if (height != null) { this.display.wrapper.style.height = interpret(height); }
8590
- if (this.options.lineWrapping) { clearLineMeasurementCache(this); }
8591
- var lineNo$$1 = this.display.viewFrom;
8592
- this.doc.iter(lineNo$$1, this.display.viewTo, function (line) {
8593
- if (line.widgets) { for (var i = 0; i < line.widgets.length; i++)
8594
- { if (line.widgets[i].noHScroll) { regLineChange(this$1, lineNo$$1, "widget"); break } } }
8595
- ++lineNo$$1;
8596
- });
8597
- this.curOp.forceUpdate = true;
8598
- signal(this, "refresh", this);
8599
- }),
8600
-
8601
- operation: function(f){return runInOp(this, f)},
8602
- startOperation: function(){return startOperation(this)},
8603
- endOperation: function(){return endOperation(this)},
8604
-
8605
- refresh: methodOp(function() {
8606
- var oldHeight = this.display.cachedTextHeight;
8607
- regChange(this);
8608
- this.curOp.forceUpdate = true;
8609
- clearCaches(this);
8610
- scrollToCoords(this, this.doc.scrollLeft, this.doc.scrollTop);
8611
- updateGutterSpace(this.display);
8612
- if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5)
8613
- { estimateLineHeights(this); }
8614
- signal(this, "refresh", this);
8615
- }),
8616
-
8617
- swapDoc: methodOp(function(doc) {
8618
- var old = this.doc;
8619
- old.cm = null;
8620
- // Cancel the current text selection if any (#5821)
8621
- if (this.state.selectingText) { this.state.selectingText(); }
8622
- attachDoc(this, doc);
8623
- clearCaches(this);
8624
- this.display.input.reset();
8625
- scrollToCoords(this, doc.scrollLeft, doc.scrollTop);
8626
- this.curOp.forceScroll = true;
8627
- signalLater(this, "swapDoc", this, old);
8628
- return old
8629
- }),
8630
-
8631
- phrase: function(phraseText) {
8632
- var phrases = this.options.phrases;
8633
- return phrases && Object.prototype.hasOwnProperty.call(phrases, phraseText) ? phrases[phraseText] : phraseText
8634
- },
8635
-
8636
- getInputField: function(){return this.display.input.getField()},
8637
- getWrapperElement: function(){return this.display.wrapper},
8638
- getScrollerElement: function(){return this.display.scroller},
8639
- getGutterElement: function(){return this.display.gutters}
8640
- };
8641
- eventMixin(CodeMirror);
8642
-
8643
- CodeMirror.registerHelper = function(type, name, value) {
8644
- if (!helpers.hasOwnProperty(type)) { helpers[type] = CodeMirror[type] = {_global: []}; }
8645
- helpers[type][name] = value;
8646
- };
8647
- CodeMirror.registerGlobalHelper = function(type, name, predicate, value) {
8648
- CodeMirror.registerHelper(type, name, value);
8649
- helpers[type]._global.push({pred: predicate, val: value});
8650
- };
8651
- }
8652
-
8653
- // Used for horizontal relative motion. Dir is -1 or 1 (left or
8654
- // right), unit can be "char", "column" (like char, but doesn't
8655
- // cross line boundaries), "word" (across next word), or "group" (to
8656
- // the start of next group of word or non-word-non-whitespace
8657
- // chars). The visually param controls whether, in right-to-left
8658
- // text, direction 1 means to move towards the next index in the
8659
- // string, or towards the character to the right of the current
8660
- // position. The resulting position will have a hitSide=true
8661
- // property if it reached the end of the document.
8662
- function findPosH(doc, pos, dir, unit, visually) {
8663
- var oldPos = pos;
8664
- var origDir = dir;
8665
- var lineObj = getLine(doc, pos.line);
8666
- function findNextLine() {
8667
- var l = pos.line + dir;
8668
- if (l < doc.first || l >= doc.first + doc.size) { return false }
8669
- pos = new Pos(l, pos.ch, pos.sticky);
8670
- return lineObj = getLine(doc, l)
8671
- }
8672
- function moveOnce(boundToLine) {
8673
- var next;
8674
- if (visually) {
8675
- next = moveVisually(doc.cm, lineObj, pos, dir);
8676
- } else {
8677
- next = moveLogically(lineObj, pos, dir);
8678
- }
8679
- if (next == null) {
8680
- if (!boundToLine && findNextLine())
8681
- { pos = endOfLine(visually, doc.cm, lineObj, pos.line, dir); }
8682
- else
8683
- { return false }
8684
- } else {
8685
- pos = next;
8686
- }
8687
- return true
8688
- }
8689
-
8690
- if (unit == "char") {
8691
- moveOnce();
8692
- } else if (unit == "column") {
8693
- moveOnce(true);
8694
- } else if (unit == "word" || unit == "group") {
8695
- var sawType = null, group = unit == "group";
8696
- var helper = doc.cm && doc.cm.getHelper(pos, "wordChars");
8697
- for (var first = true;; first = false) {
8698
- if (dir < 0 && !moveOnce(!first)) { break }
8699
- var cur = lineObj.text.charAt(pos.ch) || "\n";
8700
- var type = isWordChar(cur, helper) ? "w"
8701
- : group && cur == "\n" ? "n"
8702
- : !group || /\s/.test(cur) ? null
8703
- : "p";
8704
- if (group && !first && !type) { type = "s"; }
8705
- if (sawType && sawType != type) {
8706
- if (dir < 0) {dir = 1; moveOnce(); pos.sticky = "after";}
8707
- break
8708
- }
8709
-
8710
- if (type) { sawType = type; }
8711
- if (dir > 0 && !moveOnce(!first)) { break }
8712
- }
8713
- }
8714
- var result = skipAtomic(doc, pos, oldPos, origDir, true);
8715
- if (equalCursorPos(oldPos, result)) { result.hitSide = true; }
8716
- return result
8717
- }
8718
-
8719
- // For relative vertical movement. Dir may be -1 or 1. Unit can be
8720
- // "page" or "line". The resulting position will have a hitSide=true
8721
- // property if it reached the end of the document.
8722
- function findPosV(cm, pos, dir, unit) {
8723
- var doc = cm.doc, x = pos.left, y;
8724
- if (unit == "page") {
8725
- var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight);
8726
- var moveAmount = Math.max(pageSize - .5 * textHeight(cm.display), 3);
8727
- y = (dir > 0 ? pos.bottom : pos.top) + dir * moveAmount;
8728
-
8729
- } else if (unit == "line") {
8730
- y = dir > 0 ? pos.bottom + 3 : pos.top - 3;
8731
- }
8732
- var target;
8733
- for (;;) {
8734
- target = coordsChar(cm, x, y);
8735
- if (!target.outside) { break }
8736
- if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break }
8737
- y += dir * 5;
8738
- }
8739
- return target
8740
- }
8741
-
8742
- // CONTENTEDITABLE INPUT STYLE
8743
-
8744
- var ContentEditableInput = function(cm) {
8745
- this.cm = cm;
8746
- this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null;
8747
- this.polling = new Delayed();
8748
- this.composing = null;
8749
- this.gracePeriod = false;
8750
- this.readDOMTimeout = null;
8751
- };
8752
-
8753
- ContentEditableInput.prototype.init = function (display) {
8754
- var this$1 = this;
8755
-
8756
- var input = this, cm = input.cm;
8757
- var div = input.div = display.lineDiv;
8758
- disableBrowserMagic(div, cm.options.spellcheck, cm.options.autocorrect, cm.options.autocapitalize);
8759
-
8760
- on(div, "paste", function (e) {
8761
- if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return }
8762
- // IE doesn't fire input events, so we schedule a read for the pasted content in this way
8763
- if (ie_version <= 11) { setTimeout(operation(cm, function () { return this$1.updateFromDOM(); }), 20); }
8764
- });
8765
-
8766
- on(div, "compositionstart", function (e) {
8767
- this$1.composing = {data: e.data, done: false};
8768
- });
8769
- on(div, "compositionupdate", function (e) {
8770
- if (!this$1.composing) { this$1.composing = {data: e.data, done: false}; }
8771
- });
8772
- on(div, "compositionend", function (e) {
8773
- if (this$1.composing) {
8774
- if (e.data != this$1.composing.data) { this$1.readFromDOMSoon(); }
8775
- this$1.composing.done = true;
8776
- }
8777
- });
8778
-
8779
- on(div, "touchstart", function () { return input.forceCompositionEnd(); });
8780
-
8781
- on(div, "input", function () {
8782
- if (!this$1.composing) { this$1.readFromDOMSoon(); }
8783
- });
8784
-
8785
- function onCopyCut(e) {
8786
- if (signalDOMEvent(cm, e)) { return }
8787
- if (cm.somethingSelected()) {
8788
- setLastCopied({lineWise: false, text: cm.getSelections()});
8789
- if (e.type == "cut") { cm.replaceSelection("", null, "cut"); }
8790
- } else if (!cm.options.lineWiseCopyCut) {
8791
- return
8792
- } else {
8793
- var ranges = copyableRanges(cm);
8794
- setLastCopied({lineWise: true, text: ranges.text});
8795
- if (e.type == "cut") {
8796
- cm.operation(function () {
8797
- cm.setSelections(ranges.ranges, 0, sel_dontScroll);
8798
- cm.replaceSelection("", null, "cut");
8799
- });
8800
- }
8801
- }
8802
- if (e.clipboardData) {
8803
- e.clipboardData.clearData();
8804
- var content = lastCopied.text.join("\n");
8805
- // iOS exposes the clipboard API, but seems to discard content inserted into it
8806
- e.clipboardData.setData("Text", content);
8807
- if (e.clipboardData.getData("Text") == content) {
8808
- e.preventDefault();
8809
- return
8810
- }
8811
- }
8812
- // Old-fashioned briefly-focus-a-textarea hack
8813
- var kludge = hiddenTextarea(), te = kludge.firstChild;
8814
- cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild);
8815
- te.value = lastCopied.text.join("\n");
8816
- var hadFocus = document.activeElement;
8817
- selectInput(te);
8818
- setTimeout(function () {
8819
- cm.display.lineSpace.removeChild(kludge);
8820
- hadFocus.focus();
8821
- if (hadFocus == div) { input.showPrimarySelection(); }
8822
- }, 50);
8823
- }
8824
- on(div, "copy", onCopyCut);
8825
- on(div, "cut", onCopyCut);
8826
- };
8827
-
8828
- ContentEditableInput.prototype.prepareSelection = function () {
8829
- var result = prepareSelection(this.cm, false);
8830
- result.focus = this.cm.state.focused;
8831
- return result
8832
- };
8833
-
8834
- ContentEditableInput.prototype.showSelection = function (info, takeFocus) {
8835
- if (!info || !this.cm.display.view.length) { return }
8836
- if (info.focus || takeFocus) { this.showPrimarySelection(); }
8837
- this.showMultipleSelections(info);
8838
- };
8839
-
8840
- ContentEditableInput.prototype.getSelection = function () {
8841
- return this.cm.display.wrapper.ownerDocument.getSelection()
8842
- };
8843
-
8844
- ContentEditableInput.prototype.showPrimarySelection = function () {
8845
- var sel = this.getSelection(), cm = this.cm, prim = cm.doc.sel.primary();
8846
- var from = prim.from(), to = prim.to();
8847
-
8848
- if (cm.display.viewTo == cm.display.viewFrom || from.line >= cm.display.viewTo || to.line < cm.display.viewFrom) {
8849
- sel.removeAllRanges();
8850
- return
8851
- }
8852
-
8853
- var curAnchor = domToPos(cm, sel.anchorNode, sel.anchorOffset);
8854
- var curFocus = domToPos(cm, sel.focusNode, sel.focusOffset);
8855
- if (curAnchor && !curAnchor.bad && curFocus && !curFocus.bad &&
8856
- cmp(minPos(curAnchor, curFocus), from) == 0 &&
8857
- cmp(maxPos(curAnchor, curFocus), to) == 0)
8858
- { return }
8859
-
8860
- var view = cm.display.view;
8861
- var start = (from.line >= cm.display.viewFrom && posToDOM(cm, from)) ||
8862
- {node: view[0].measure.map[2], offset: 0};
8863
- var end = to.line < cm.display.viewTo && posToDOM(cm, to);
8864
- if (!end) {
8865
- var measure = view[view.length - 1].measure;
8866
- var map$$1 = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map;
8867
- end = {node: map$$1[map$$1.length - 1], offset: map$$1[map$$1.length - 2] - map$$1[map$$1.length - 3]};
8868
- }
8869
-
8870
- if (!start || !end) {
8871
- sel.removeAllRanges();
8872
- return
8873
- }
8874
-
8875
- var old = sel.rangeCount && sel.getRangeAt(0), rng;
8876
- try { rng = range(start.node, start.offset, end.offset, end.node); }
8877
- catch(e) {} // Our model of the DOM might be outdated, in which case the range we try to set can be impossible
8878
- if (rng) {
8879
- if (!gecko && cm.state.focused) {
8880
- sel.collapse(start.node, start.offset);
8881
- if (!rng.collapsed) {
8882
- sel.removeAllRanges();
8883
- sel.addRange(rng);
8884
- }
8885
- } else {
8886
- sel.removeAllRanges();
8887
- sel.addRange(rng);
8888
- }
8889
- if (old && sel.anchorNode == null) { sel.addRange(old); }
8890
- else if (gecko) { this.startGracePeriod(); }
8891
- }
8892
- this.rememberSelection();
8893
- };
8894
-
8895
- ContentEditableInput.prototype.startGracePeriod = function () {
8896
- var this$1 = this;
8897
-
8898
- clearTimeout(this.gracePeriod);
8899
- this.gracePeriod = setTimeout(function () {
8900
- this$1.gracePeriod = false;
8901
- if (this$1.selectionChanged())
8902
- { this$1.cm.operation(function () { return this$1.cm.curOp.selectionChanged = true; }); }
8903
- }, 20);
8904
- };
8905
-
8906
- ContentEditableInput.prototype.showMultipleSelections = function (info) {
8907
- removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors);
8908
- removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection);
8909
- };
8910
-
8911
- ContentEditableInput.prototype.rememberSelection = function () {
8912
- var sel = this.getSelection();
8913
- this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset;
8914
- this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset;
8915
- };
8916
-
8917
- ContentEditableInput.prototype.selectionInEditor = function () {
8918
- var sel = this.getSelection();
8919
- if (!sel.rangeCount) { return false }
8920
- var node = sel.getRangeAt(0).commonAncestorContainer;
8921
- return contains(this.div, node)
8922
- };
8923
-
8924
- ContentEditableInput.prototype.focus = function () {
8925
- if (this.cm.options.readOnly != "nocursor") {
8926
- if (!this.selectionInEditor())
8927
- { this.showSelection(this.prepareSelection(), true); }
8928
- this.div.focus();
8929
- }
8930
- };
8931
- ContentEditableInput.prototype.blur = function () { this.div.blur(); };
8932
- ContentEditableInput.prototype.getField = function () { return this.div };
8933
-
8934
- ContentEditableInput.prototype.supportsTouch = function () { return true };
8935
-
8936
- ContentEditableInput.prototype.receivedFocus = function () {
8937
- var input = this;
8938
- if (this.selectionInEditor())
8939
- { this.pollSelection(); }
8940
- else
8941
- { runInOp(this.cm, function () { return input.cm.curOp.selectionChanged = true; }); }
8942
-
8943
- function poll() {
8944
- if (input.cm.state.focused) {
8945
- input.pollSelection();
8946
- input.polling.set(input.cm.options.pollInterval, poll);
8947
- }
8948
- }
8949
- this.polling.set(this.cm.options.pollInterval, poll);
8950
- };
8951
-
8952
- ContentEditableInput.prototype.selectionChanged = function () {
8953
- var sel = this.getSelection();
8954
- return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset ||
8955
- sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset
8956
- };
8957
-
8958
- ContentEditableInput.prototype.pollSelection = function () {
8959
- if (this.readDOMTimeout != null || this.gracePeriod || !this.selectionChanged()) { return }
8960
- var sel = this.getSelection(), cm = this.cm;
8961
- // On Android Chrome (version 56, at least), backspacing into an
8962
- // uneditable block element will put the cursor in that element,
8963
- // and then, because it's not editable, hide the virtual keyboard.
8964
- // Because Android doesn't allow us to actually detect backspace
8965
- // presses in a sane way, this code checks for when that happens
8966
- // and simulates a backspace press in this case.
8967
- if (android && chrome && this.cm.display.gutterSpecs.length && isInGutter(sel.anchorNode)) {
8968
- this.cm.triggerOnKeyDown({type: "keydown", keyCode: 8, preventDefault: Math.abs});
8969
- this.blur();
8970
- this.focus();
8971
- return
8972
- }
8973
- if (this.composing) { return }
8974
- this.rememberSelection();
8975
- var anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset);
8976
- var head = domToPos(cm, sel.focusNode, sel.focusOffset);
8977
- if (anchor && head) { runInOp(cm, function () {
8978
- setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll);
8979
- if (anchor.bad || head.bad) { cm.curOp.selectionChanged = true; }
8980
- }); }
8981
- };
8982
-
8983
- ContentEditableInput.prototype.pollContent = function () {
8984
- if (this.readDOMTimeout != null) {
8985
- clearTimeout(this.readDOMTimeout);
8986
- this.readDOMTimeout = null;
8987
- }
8988
-
8989
- var cm = this.cm, display = cm.display, sel = cm.doc.sel.primary();
8990
- var from = sel.from(), to = sel.to();
8991
- if (from.ch == 0 && from.line > cm.firstLine())
8992
- { from = Pos(from.line - 1, getLine(cm.doc, from.line - 1).length); }
8993
- if (to.ch == getLine(cm.doc, to.line).text.length && to.line < cm.lastLine())
8994
- { to = Pos(to.line + 1, 0); }
8995
- if (from.line < display.viewFrom || to.line > display.viewTo - 1) { return false }
8996
-
8997
- var fromIndex, fromLine, fromNode;
8998
- if (from.line == display.viewFrom || (fromIndex = findViewIndex(cm, from.line)) == 0) {
8999
- fromLine = lineNo(display.view[0].line);
9000
- fromNode = display.view[0].node;
9001
- } else {
9002
- fromLine = lineNo(display.view[fromIndex].line);
9003
- fromNode = display.view[fromIndex - 1].node.nextSibling;
9004
- }
9005
- var toIndex = findViewIndex(cm, to.line);
9006
- var toLine, toNode;
9007
- if (toIndex == display.view.length - 1) {
9008
- toLine = display.viewTo - 1;
9009
- toNode = display.lineDiv.lastChild;
9010
- } else {
9011
- toLine = lineNo(display.view[toIndex + 1].line) - 1;
9012
- toNode = display.view[toIndex + 1].node.previousSibling;
9013
- }
9014
-
9015
- if (!fromNode) { return false }
9016
- var newText = cm.doc.splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine));
9017
- var oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm.doc, toLine).text.length));
9018
- while (newText.length > 1 && oldText.length > 1) {
9019
- if (lst(newText) == lst(oldText)) { newText.pop(); oldText.pop(); toLine--; }
9020
- else if (newText[0] == oldText[0]) { newText.shift(); oldText.shift(); fromLine++; }
9021
- else { break }
9022
- }
9023
-
9024
- var cutFront = 0, cutEnd = 0;
9025
- var newTop = newText[0], oldTop = oldText[0], maxCutFront = Math.min(newTop.length, oldTop.length);
9026
- while (cutFront < maxCutFront && newTop.charCodeAt(cutFront) == oldTop.charCodeAt(cutFront))
9027
- { ++cutFront; }
9028
- var newBot = lst(newText), oldBot = lst(oldText);
9029
- var maxCutEnd = Math.min(newBot.length - (newText.length == 1 ? cutFront : 0),
9030
- oldBot.length - (oldText.length == 1 ? cutFront : 0));
9031
- while (cutEnd < maxCutEnd &&
9032
- newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1))
9033
- { ++cutEnd; }
9034
- // Try to move start of change to start of selection if ambiguous
9035
- if (newText.length == 1 && oldText.length == 1 && fromLine == from.line) {
9036
- while (cutFront && cutFront > from.ch &&
9037
- newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1)) {
9038
- cutFront--;
9039
- cutEnd++;
9040
- }
9041
- }
9042
-
9043
- newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd).replace(/^\u200b+/, "");
9044
- newText[0] = newText[0].slice(cutFront).replace(/\u200b+$/, "");
9045
-
9046
- var chFrom = Pos(fromLine, cutFront);
9047
- var chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0);
9048
- if (newText.length > 1 || newText[0] || cmp(chFrom, chTo)) {
9049
- replaceRange(cm.doc, newText, chFrom, chTo, "+input");
9050
- return true
9051
- }
9052
- };
9053
-
9054
- ContentEditableInput.prototype.ensurePolled = function () {
9055
- this.forceCompositionEnd();
9056
- };
9057
- ContentEditableInput.prototype.reset = function () {
9058
- this.forceCompositionEnd();
9059
- };
9060
- ContentEditableInput.prototype.forceCompositionEnd = function () {
9061
- if (!this.composing) { return }
9062
- clearTimeout(this.readDOMTimeout);
9063
- this.composing = null;
9064
- this.updateFromDOM();
9065
- this.div.blur();
9066
- this.div.focus();
9067
- };
9068
- ContentEditableInput.prototype.readFromDOMSoon = function () {
9069
- var this$1 = this;
9070
-
9071
- if (this.readDOMTimeout != null) { return }
9072
- this.readDOMTimeout = setTimeout(function () {
9073
- this$1.readDOMTimeout = null;
9074
- if (this$1.composing) {
9075
- if (this$1.composing.done) { this$1.composing = null; }
9076
- else { return }
9077
- }
9078
- this$1.updateFromDOM();
9079
- }, 80);
9080
- };
9081
-
9082
- ContentEditableInput.prototype.updateFromDOM = function () {
9083
- var this$1 = this;
9084
-
9085
- if (this.cm.isReadOnly() || !this.pollContent())
9086
- { runInOp(this.cm, function () { return regChange(this$1.cm); }); }
9087
- };
9088
-
9089
- ContentEditableInput.prototype.setUneditable = function (node) {
9090
- node.contentEditable = "false";
9091
- };
9092
-
9093
- ContentEditableInput.prototype.onKeyPress = function (e) {
9094
- if (e.charCode == 0 || this.composing) { return }
9095
- e.preventDefault();
9096
- if (!this.cm.isReadOnly())
9097
- { operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode), 0); }
9098
- };
9099
-
9100
- ContentEditableInput.prototype.readOnlyChanged = function (val) {
9101
- this.div.contentEditable = String(val != "nocursor");
9102
- };
9103
-
9104
- ContentEditableInput.prototype.onContextMenu = function () {};
9105
- ContentEditableInput.prototype.resetPosition = function () {};
9106
-
9107
- ContentEditableInput.prototype.needsContentAttribute = true;
9108
-
9109
- function posToDOM(cm, pos) {
9110
- var view = findViewForLine(cm, pos.line);
9111
- if (!view || view.hidden) { return null }
9112
- var line = getLine(cm.doc, pos.line);
9113
- var info = mapFromLineView(view, line, pos.line);
9114
-
9115
- var order = getOrder(line, cm.doc.direction), side = "left";
9116
- if (order) {
9117
- var partPos = getBidiPartAt(order, pos.ch);
9118
- side = partPos % 2 ? "right" : "left";
9119
- }
9120
- var result = nodeAndOffsetInLineMap(info.map, pos.ch, side);
9121
- result.offset = result.collapse == "right" ? result.end : result.start;
9122
- return result
9123
- }
9124
-
9125
- function isInGutter(node) {
9126
- for (var scan = node; scan; scan = scan.parentNode)
9127
- { if (/CodeMirror-gutter-wrapper/.test(scan.className)) { return true } }
9128
- return false
9129
- }
9130
-
9131
- function badPos(pos, bad) { if (bad) { pos.bad = true; } return pos }
9132
-
9133
- function domTextBetween(cm, from, to, fromLine, toLine) {
9134
- var text = "", closing = false, lineSep = cm.doc.lineSeparator(), extraLinebreak = false;
9135
- function recognizeMarker(id) { return function (marker) { return marker.id == id; } }
9136
- function close() {
9137
- if (closing) {
9138
- text += lineSep;
9139
- if (extraLinebreak) { text += lineSep; }
9140
- closing = extraLinebreak = false;
9141
- }
9142
- }
9143
- function addText(str) {
9144
- if (str) {
9145
- close();
9146
- text += str;
9147
- }
9148
- }
9149
- function walk(node) {
9150
- if (node.nodeType == 1) {
9151
- var cmText = node.getAttribute("cm-text");
9152
- if (cmText) {
9153
- addText(cmText);
9154
- return
9155
- }
9156
- var markerID = node.getAttribute("cm-marker"), range$$1;
9157
- if (markerID) {
9158
- var found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recognizeMarker(+markerID));
9159
- if (found.length && (range$$1 = found[0].find(0)))
9160
- { addText(getBetween(cm.doc, range$$1.from, range$$1.to).join(lineSep)); }
9161
- return
9162
- }
9163
- if (node.getAttribute("contenteditable") == "false") { return }
9164
- var isBlock = /^(pre|div|p|li|table|br)$/i.test(node.nodeName);
9165
- if (!/^br$/i.test(node.nodeName) && node.textContent.length == 0) { return }
9166
-
9167
- if (isBlock) { close(); }
9168
- for (var i = 0; i < node.childNodes.length; i++)
9169
- { walk(node.childNodes[i]); }
9170
-
9171
- if (/^(pre|p)$/i.test(node.nodeName)) { extraLinebreak = true; }
9172
- if (isBlock) { closing = true; }
9173
- } else if (node.nodeType == 3) {
9174
- addText(node.nodeValue.replace(/\u200b/g, "").replace(/\u00a0/g, " "));
9175
- }
9176
- }
9177
- for (;;) {
9178
- walk(from);
9179
- if (from == to) { break }
9180
- from = from.nextSibling;
9181
- extraLinebreak = false;
9182
- }
9183
- return text
9184
- }
9185
-
9186
- function domToPos(cm, node, offset) {
9187
- var lineNode;
9188
- if (node == cm.display.lineDiv) {
9189
- lineNode = cm.display.lineDiv.childNodes[offset];
9190
- if (!lineNode) { return badPos(cm.clipPos(Pos(cm.display.viewTo - 1)), true) }
9191
- node = null; offset = 0;
9192
- } else {
9193
- for (lineNode = node;; lineNode = lineNode.parentNode) {
9194
- if (!lineNode || lineNode == cm.display.lineDiv) { return null }
9195
- if (lineNode.parentNode && lineNode.parentNode == cm.display.lineDiv) { break }
9196
- }
9197
- }
9198
- for (var i = 0; i < cm.display.view.length; i++) {
9199
- var lineView = cm.display.view[i];
9200
- if (lineView.node == lineNode)
9201
- { return locateNodeInLineView(lineView, node, offset) }
9202
- }
9203
- }
9204
-
9205
- function locateNodeInLineView(lineView, node, offset) {
9206
- var wrapper = lineView.text.firstChild, bad = false;
9207
- if (!node || !contains(wrapper, node)) { return badPos(Pos(lineNo(lineView.line), 0), true) }
9208
- if (node == wrapper) {
9209
- bad = true;
9210
- node = wrapper.childNodes[offset];
9211
- offset = 0;
9212
- if (!node) {
9213
- var line = lineView.rest ? lst(lineView.rest) : lineView.line;
9214
- return badPos(Pos(lineNo(line), line.text.length), bad)
9215
- }
9216
- }
9217
-
9218
- var textNode = node.nodeType == 3 ? node : null, topNode = node;
9219
- if (!textNode && node.childNodes.length == 1 && node.firstChild.nodeType == 3) {
9220
- textNode = node.firstChild;
9221
- if (offset) { offset = textNode.nodeValue.length; }
9222
- }
9223
- while (topNode.parentNode != wrapper) { topNode = topNode.parentNode; }
9224
- var measure = lineView.measure, maps = measure.maps;
9225
-
9226
- function find(textNode, topNode, offset) {
9227
- for (var i = -1; i < (maps ? maps.length : 0); i++) {
9228
- var map$$1 = i < 0 ? measure.map : maps[i];
9229
- for (var j = 0; j < map$$1.length; j += 3) {
9230
- var curNode = map$$1[j + 2];
9231
- if (curNode == textNode || curNode == topNode) {
9232
- var line = lineNo(i < 0 ? lineView.line : lineView.rest[i]);
9233
- var ch = map$$1[j] + offset;
9234
- if (offset < 0 || curNode != textNode) { ch = map$$1[j + (offset ? 1 : 0)]; }
9235
- return Pos(line, ch)
9236
- }
9237
- }
9238
- }
9239
- }
9240
- var found = find(textNode, topNode, offset);
9241
- if (found) { return badPos(found, bad) }
9242
-
9243
- // FIXME this is all really shaky. might handle the few cases it needs to handle, but likely to cause problems
9244
- for (var after = topNode.nextSibling, dist = textNode ? textNode.nodeValue.length - offset : 0; after; after = after.nextSibling) {
9245
- found = find(after, after.firstChild, 0);
9246
- if (found)
9247
- { return badPos(Pos(found.line, found.ch - dist), bad) }
9248
- else
9249
- { dist += after.textContent.length; }
9250
- }
9251
- for (var before = topNode.previousSibling, dist$1 = offset; before; before = before.previousSibling) {
9252
- found = find(before, before.firstChild, -1);
9253
- if (found)
9254
- { return badPos(Pos(found.line, found.ch + dist$1), bad) }
9255
- else
9256
- { dist$1 += before.textContent.length; }
9257
- }
9258
- }
9259
-
9260
- // TEXTAREA INPUT STYLE
9261
-
9262
- var TextareaInput = function(cm) {
9263
- this.cm = cm;
9264
- // See input.poll and input.reset
9265
- this.prevInput = "";
9266
-
9267
- // Flag that indicates whether we expect input to appear real soon
9268
- // now (after some event like 'keypress' or 'input') and are
9269
- // polling intensively.
9270
- this.pollingFast = false;
9271
- // Self-resetting timeout for the poller
9272
- this.polling = new Delayed();
9273
- // Used to work around IE issue with selection being forgotten when focus moves away from textarea
9274
- this.hasSelection = false;
9275
- this.composing = null;
9276
- };
9277
-
9278
- TextareaInput.prototype.init = function (display) {
9279
- var this$1 = this;
9280
-
9281
- var input = this, cm = this.cm;
9282
- this.createField(display);
9283
- var te = this.textarea;
9284
-
9285
- display.wrapper.insertBefore(this.wrapper, display.wrapper.firstChild);
9286
-
9287
- // Needed to hide big blue blinking cursor on Mobile Safari (doesn't seem to work in iOS 8 anymore)
9288
- if (ios) { te.style.width = "0px"; }
9289
-
9290
- on(te, "input", function () {
9291
- if (ie && ie_version >= 9 && this$1.hasSelection) { this$1.hasSelection = null; }
9292
- input.poll();
9293
- });
9294
-
9295
- on(te, "paste", function (e) {
9296
- if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return }
9297
-
9298
- cm.state.pasteIncoming = +new Date;
9299
- input.fastPoll();
9300
- });
9301
-
9302
- function prepareCopyCut(e) {
9303
- if (signalDOMEvent(cm, e)) { return }
9304
- if (cm.somethingSelected()) {
9305
- setLastCopied({lineWise: false, text: cm.getSelections()});
9306
- } else if (!cm.options.lineWiseCopyCut) {
9307
- return
9308
- } else {
9309
- var ranges = copyableRanges(cm);
9310
- setLastCopied({lineWise: true, text: ranges.text});
9311
- if (e.type == "cut") {
9312
- cm.setSelections(ranges.ranges, null, sel_dontScroll);
9313
- } else {
9314
- input.prevInput = "";
9315
- te.value = ranges.text.join("\n");
9316
- selectInput(te);
9317
- }
9318
- }
9319
- if (e.type == "cut") { cm.state.cutIncoming = +new Date; }
9320
- }
9321
- on(te, "cut", prepareCopyCut);
9322
- on(te, "copy", prepareCopyCut);
9323
-
9324
- on(display.scroller, "paste", function (e) {
9325
- if (eventInWidget(display, e) || signalDOMEvent(cm, e)) { return }
9326
- if (!te.dispatchEvent) {
9327
- cm.state.pasteIncoming = +new Date;
9328
- input.focus();
9329
- return
9330
- }
9331
-
9332
- // Pass the `paste` event to the textarea so it's handled by its event listener.
9333
- var event = new Event("paste");
9334
- event.clipboardData = e.clipboardData;
9335
- te.dispatchEvent(event);
9336
- });
9337
-
9338
- // Prevent normal selection in the editor (we handle our own)
9339
- on(display.lineSpace, "selectstart", function (e) {
9340
- if (!eventInWidget(display, e)) { e_preventDefault(e); }
9341
- });
9342
-
9343
- on(te, "compositionstart", function () {
9344
- var start = cm.getCursor("from");
9345
- if (input.composing) { input.composing.range.clear(); }
9346
- input.composing = {
9347
- start: start,
9348
- range: cm.markText(start, cm.getCursor("to"), {className: "CodeMirror-composing"})
9349
- };
9350
- });
9351
- on(te, "compositionend", function () {
9352
- if (input.composing) {
9353
- input.poll();
9354
- input.composing.range.clear();
9355
- input.composing = null;
9356
- }
9357
- });
9358
- };
9359
-
9360
- TextareaInput.prototype.createField = function (_display) {
9361
- // Wraps and hides input textarea
9362
- this.wrapper = hiddenTextarea();
9363
- // The semihidden textarea that is focused when the editor is
9364
- // focused, and receives input.
9365
- this.textarea = this.wrapper.firstChild;
9366
- };
9367
-
9368
- TextareaInput.prototype.prepareSelection = function () {
9369
- // Redraw the selection and/or cursor
9370
- var cm = this.cm, display = cm.display, doc = cm.doc;
9371
- var result = prepareSelection(cm);
9372
-
9373
- // Move the hidden textarea near the cursor to prevent scrolling artifacts
9374
- if (cm.options.moveInputWithCursor) {
9375
- var headPos = cursorCoords(cm, doc.sel.primary().head, "div");
9376
- var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect();
9377
- result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
9378
- headPos.top + lineOff.top - wrapOff.top));
9379
- result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
9380
- headPos.left + lineOff.left - wrapOff.left));
9381
- }
9382
-
9383
- return result
9384
- };
9385
-
9386
- TextareaInput.prototype.showSelection = function (drawn) {
9387
- var cm = this.cm, display = cm.display;
9388
- removeChildrenAndAdd(display.cursorDiv, drawn.cursors);
9389
- removeChildrenAndAdd(display.selectionDiv, drawn.selection);
9390
- if (drawn.teTop != null) {
9391
- this.wrapper.style.top = drawn.teTop + "px";
9392
- this.wrapper.style.left = drawn.teLeft + "px";
9393
- }
9394
- };
9395
-
9396
- // Reset the input to correspond to the selection (or to be empty,
9397
- // when not typing and nothing is selected)
9398
- TextareaInput.prototype.reset = function (typing) {
9399
- if (this.contextMenuPending || this.composing) { return }
9400
- var cm = this.cm;
9401
- if (cm.somethingSelected()) {
9402
- this.prevInput = "";
9403
- var content = cm.getSelection();
9404
- this.textarea.value = content;
9405
- if (cm.state.focused) { selectInput(this.textarea); }
9406
- if (ie && ie_version >= 9) { this.hasSelection = content; }
9407
- } else if (!typing) {
9408
- this.prevInput = this.textarea.value = "";
9409
- if (ie && ie_version >= 9) { this.hasSelection = null; }
9410
- }
9411
- };
9412
-
9413
- TextareaInput.prototype.getField = function () { return this.textarea };
9414
-
9415
- TextareaInput.prototype.supportsTouch = function () { return false };
9416
-
9417
- TextareaInput.prototype.focus = function () {
9418
- if (this.cm.options.readOnly != "nocursor" && (!mobile || activeElt() != this.textarea)) {
9419
- try { this.textarea.focus(); }
9420
- catch (e) {} // IE8 will throw if the textarea is display: none or not in DOM
9421
- }
9422
- };
9423
-
9424
- TextareaInput.prototype.blur = function () { this.textarea.blur(); };
9425
-
9426
- TextareaInput.prototype.resetPosition = function () {
9427
- this.wrapper.style.top = this.wrapper.style.left = 0;
9428
- };
9429
-
9430
- TextareaInput.prototype.receivedFocus = function () { this.slowPoll(); };
9431
-
9432
- // Poll for input changes, using the normal rate of polling. This
9433
- // runs as long as the editor is focused.
9434
- TextareaInput.prototype.slowPoll = function () {
9435
- var this$1 = this;
9436
-
9437
- if (this.pollingFast) { return }
9438
- this.polling.set(this.cm.options.pollInterval, function () {
9439
- this$1.poll();
9440
- if (this$1.cm.state.focused) { this$1.slowPoll(); }
9441
- });
9442
- };
9443
-
9444
- // When an event has just come in that is likely to add or change
9445
- // something in the input textarea, we poll faster, to ensure that
9446
- // the change appears on the screen quickly.
9447
- TextareaInput.prototype.fastPoll = function () {
9448
- var missed = false, input = this;
9449
- input.pollingFast = true;
9450
- function p() {
9451
- var changed = input.poll();
9452
- if (!changed && !missed) {missed = true; input.polling.set(60, p);}
9453
- else {input.pollingFast = false; input.slowPoll();}
9454
- }
9455
- input.polling.set(20, p);
9456
- };
9457
-
9458
- // Read input from the textarea, and update the document to match.
9459
- // When something is selected, it is present in the textarea, and
9460
- // selected (unless it is huge, in which case a placeholder is
9461
- // used). When nothing is selected, the cursor sits after previously
9462
- // seen text (can be empty), which is stored in prevInput (we must
9463
- // not reset the textarea when typing, because that breaks IME).
9464
- TextareaInput.prototype.poll = function () {
9465
- var this$1 = this;
9466
-
9467
- var cm = this.cm, input = this.textarea, prevInput = this.prevInput;
9468
- // Since this is called a *lot*, try to bail out as cheaply as
9469
- // possible when it is clear that nothing happened. hasSelection
9470
- // will be the case when there is a lot of text in the textarea,
9471
- // in which case reading its value would be expensive.
9472
- if (this.contextMenuPending || !cm.state.focused ||
9473
- (hasSelection(input) && !prevInput && !this.composing) ||
9474
- cm.isReadOnly() || cm.options.disableInput || cm.state.keySeq)
9475
- { return false }
9476
-
9477
- var text = input.value;
9478
- // If nothing changed, bail.
9479
- if (text == prevInput && !cm.somethingSelected()) { return false }
9480
- // Work around nonsensical selection resetting in IE9/10, and
9481
- // inexplicable appearance of private area unicode characters on
9482
- // some key combos in Mac (#2689).
9483
- if (ie && ie_version >= 9 && this.hasSelection === text ||
9484
- mac && /[\uf700-\uf7ff]/.test(text)) {
9485
- cm.display.input.reset();
9486
- return false
9487
- }
9488
-
9489
- if (cm.doc.sel == cm.display.selForContextMenu) {
9490
- var first = text.charCodeAt(0);
9491
- if (first == 0x200b && !prevInput) { prevInput = "\u200b"; }
9492
- if (first == 0x21da) { this.reset(); return this.cm.execCommand("undo") }
9493
- }
9494
- // Find the part of the input that is actually new
9495
- var same = 0, l = Math.min(prevInput.length, text.length);
9496
- while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) { ++same; }
9497
-
9498
- runInOp(cm, function () {
9499
- applyTextInput(cm, text.slice(same), prevInput.length - same,
9500
- null, this$1.composing ? "*compose" : null);
9501
-
9502
- // Don't leave long text in the textarea, since it makes further polling slow
9503
- if (text.length > 1000 || text.indexOf("\n") > -1) { input.value = this$1.prevInput = ""; }
9504
- else { this$1.prevInput = text; }
9505
-
9506
- if (this$1.composing) {
9507
- this$1.composing.range.clear();
9508
- this$1.composing.range = cm.markText(this$1.composing.start, cm.getCursor("to"),
9509
- {className: "CodeMirror-composing"});
9510
- }
9511
- });
9512
- return true
9513
- };
9514
-
9515
- TextareaInput.prototype.ensurePolled = function () {
9516
- if (this.pollingFast && this.poll()) { this.pollingFast = false; }
9517
- };
9518
-
9519
- TextareaInput.prototype.onKeyPress = function () {
9520
- if (ie && ie_version >= 9) { this.hasSelection = null; }
9521
- this.fastPoll();
9522
- };
9523
-
9524
- TextareaInput.prototype.onContextMenu = function (e) {
9525
- var input = this, cm = input.cm, display = cm.display, te = input.textarea;
9526
- if (input.contextMenuPending) { input.contextMenuPending(); }
9527
- var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop;
9528
- if (!pos || presto) { return } // Opera is difficult.
9529
-
9530
- // Reset the current text selection only if the click is done outside of the selection
9531
- // and 'resetSelectionOnContextMenu' option is true.
9532
- var reset = cm.options.resetSelectionOnContextMenu;
9533
- if (reset && cm.doc.sel.contains(pos) == -1)
9534
- { operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll); }
9535
-
9536
- var oldCSS = te.style.cssText, oldWrapperCSS = input.wrapper.style.cssText;
9537
- var wrapperBox = input.wrapper.offsetParent.getBoundingClientRect();
9538
- input.wrapper.style.cssText = "position: static";
9539
- te.style.cssText = "position: absolute; width: 30px; height: 30px;\n top: " + (e.clientY - wrapperBox.top - 5) + "px; left: " + (e.clientX - wrapperBox.left - 5) + "px;\n z-index: 1000; background: " + (ie ? "rgba(255, 255, 255, .05)" : "transparent") + ";\n outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);";
9540
- var oldScrollY;
9541
- if (webkit) { oldScrollY = window.scrollY; } // Work around Chrome issue (#2712)
9542
- display.input.focus();
9543
- if (webkit) { window.scrollTo(null, oldScrollY); }
9544
- display.input.reset();
9545
- // Adds "Select all" to context menu in FF
9546
- if (!cm.somethingSelected()) { te.value = input.prevInput = " "; }
9547
- input.contextMenuPending = rehide;
9548
- display.selForContextMenu = cm.doc.sel;
9549
- clearTimeout(display.detectingSelectAll);
9550
-
9551
- // Select-all will be greyed out if there's nothing to select, so
9552
- // this adds a zero-width space so that we can later check whether
9553
- // it got selected.
9554
- function prepareSelectAllHack() {
9555
- if (te.selectionStart != null) {
9556
- var selected = cm.somethingSelected();
9557
- var extval = "\u200b" + (selected ? te.value : "");
9558
- te.value = "\u21da"; // Used to catch context-menu undo
9559
- te.value = extval;
9560
- input.prevInput = selected ? "" : "\u200b";
9561
- te.selectionStart = 1; te.selectionEnd = extval.length;
9562
- // Re-set this, in case some other handler touched the
9563
- // selection in the meantime.
9564
- display.selForContextMenu = cm.doc.sel;
9565
- }
9566
- }
9567
- function rehide() {
9568
- if (input.contextMenuPending != rehide) { return }
9569
- input.contextMenuPending = false;
9570
- input.wrapper.style.cssText = oldWrapperCSS;
9571
- te.style.cssText = oldCSS;
9572
- if (ie && ie_version < 9) { display.scrollbars.setScrollTop(display.scroller.scrollTop = scrollPos); }
9573
-
9574
- // Try to detect the user choosing select-all
9575
- if (te.selectionStart != null) {
9576
- if (!ie || (ie && ie_version < 9)) { prepareSelectAllHack(); }
9577
- var i = 0, poll = function () {
9578
- if (display.selForContextMenu == cm.doc.sel && te.selectionStart == 0 &&
9579
- te.selectionEnd > 0 && input.prevInput == "\u200b") {
9580
- operation(cm, selectAll)(cm);
9581
- } else if (i++ < 10) {
9582
- display.detectingSelectAll = setTimeout(poll, 500);
9583
- } else {
9584
- display.selForContextMenu = null;
9585
- display.input.reset();
9586
- }
9587
- };
9588
- display.detectingSelectAll = setTimeout(poll, 200);
9589
- }
9590
- }
9591
-
9592
- if (ie && ie_version >= 9) { prepareSelectAllHack(); }
9593
- if (captureRightClick) {
9594
- e_stop(e);
9595
- var mouseup = function () {
9596
- off(window, "mouseup", mouseup);
9597
- setTimeout(rehide, 20);
9598
- };
9599
- on(window, "mouseup", mouseup);
9600
- } else {
9601
- setTimeout(rehide, 50);
9602
- }
9603
- };
9604
-
9605
- TextareaInput.prototype.readOnlyChanged = function (val) {
9606
- if (!val) { this.reset(); }
9607
- this.textarea.disabled = val == "nocursor";
9608
- };
9609
-
9610
- TextareaInput.prototype.setUneditable = function () {};
9611
-
9612
- TextareaInput.prototype.needsContentAttribute = false;
9613
-
9614
- function fromTextArea(textarea, options) {
9615
- options = options ? copyObj(options) : {};
9616
- options.value = textarea.value;
9617
- if (!options.tabindex && textarea.tabIndex)
9618
- { options.tabindex = textarea.tabIndex; }
9619
- if (!options.placeholder && textarea.placeholder)
9620
- { options.placeholder = textarea.placeholder; }
9621
- // Set autofocus to true if this textarea is focused, or if it has
9622
- // autofocus and no other element is focused.
9623
- if (options.autofocus == null) {
9624
- var hasFocus = activeElt();
9625
- options.autofocus = hasFocus == textarea ||
9626
- textarea.getAttribute("autofocus") != null && hasFocus == document.body;
9627
- }
9628
-
9629
- function save() {textarea.value = cm.getValue();}
9630
-
9631
- var realSubmit;
9632
- if (textarea.form) {
9633
- on(textarea.form, "submit", save);
9634
- // Deplorable hack to make the submit method do the right thing.
9635
- if (!options.leaveSubmitMethodAlone) {
9636
- var form = textarea.form;
9637
- realSubmit = form.submit;
9638
- try {
9639
- var wrappedSubmit = form.submit = function () {
9640
- save();
9641
- form.submit = realSubmit;
9642
- form.submit();
9643
- form.submit = wrappedSubmit;
9644
- };
9645
- } catch(e) {}
9646
- }
9647
- }
9648
-
9649
- options.finishInit = function (cm) {
9650
- cm.save = save;
9651
- cm.getTextArea = function () { return textarea; };
9652
- cm.toTextArea = function () {
9653
- cm.toTextArea = isNaN; // Prevent this from being ran twice
9654
- save();
9655
- textarea.parentNode.removeChild(cm.getWrapperElement());
9656
- textarea.style.display = "";
9657
- if (textarea.form) {
9658
- off(textarea.form, "submit", save);
9659
- if (typeof textarea.form.submit == "function")
9660
- { textarea.form.submit = realSubmit; }
9661
- }
9662
- };
9663
- };
9664
-
9665
- textarea.style.display = "none";
9666
- var cm = CodeMirror(function (node) { return textarea.parentNode.insertBefore(node, textarea.nextSibling); },
9667
- options);
9668
- return cm
9669
- }
9670
-
9671
- function addLegacyProps(CodeMirror) {
9672
- CodeMirror.off = off;
9673
- CodeMirror.on = on;
9674
- CodeMirror.wheelEventPixels = wheelEventPixels;
9675
- CodeMirror.Doc = Doc;
9676
- CodeMirror.splitLines = splitLinesAuto;
9677
- CodeMirror.countColumn = countColumn;
9678
- CodeMirror.findColumn = findColumn;
9679
- CodeMirror.isWordChar = isWordCharBasic;
9680
- CodeMirror.Pass = Pass;
9681
- CodeMirror.signal = signal;
9682
- CodeMirror.Line = Line;
9683
- CodeMirror.changeEnd = changeEnd;
9684
- CodeMirror.scrollbarModel = scrollbarModel;
9685
- CodeMirror.Pos = Pos;
9686
- CodeMirror.cmpPos = cmp;
9687
- CodeMirror.modes = modes;
9688
- CodeMirror.mimeModes = mimeModes;
9689
- CodeMirror.resolveMode = resolveMode;
9690
- CodeMirror.getMode = getMode;
9691
- CodeMirror.modeExtensions = modeExtensions;
9692
- CodeMirror.extendMode = extendMode;
9693
- CodeMirror.copyState = copyState;
9694
- CodeMirror.startState = startState;
9695
- CodeMirror.innerMode = innerMode;
9696
- CodeMirror.commands = commands;
9697
- CodeMirror.keyMap = keyMap;
9698
- CodeMirror.keyName = keyName;
9699
- CodeMirror.isModifierKey = isModifierKey;
9700
- CodeMirror.lookupKey = lookupKey;
9701
- CodeMirror.normalizeKeyMap = normalizeKeyMap;
9702
- CodeMirror.StringStream = StringStream;
9703
- CodeMirror.SharedTextMarker = SharedTextMarker;
9704
- CodeMirror.TextMarker = TextMarker;
9705
- CodeMirror.LineWidget = LineWidget;
9706
- CodeMirror.e_preventDefault = e_preventDefault;
9707
- CodeMirror.e_stopPropagation = e_stopPropagation;
9708
- CodeMirror.e_stop = e_stop;
9709
- CodeMirror.addClass = addClass;
9710
- CodeMirror.contains = contains;
9711
- CodeMirror.rmClass = rmClass;
9712
- CodeMirror.keyNames = keyNames;
9713
- }
9714
-
9715
- // EDITOR CONSTRUCTOR
9716
-
9717
- defineOptions(CodeMirror);
9718
-
9719
- addEditorMethods(CodeMirror);
9720
-
9721
- // Set up methods on CodeMirror's prototype to redirect to the editor's document.
9722
- var dontDelegate = "iter insert remove copy getEditor constructor".split(" ");
9723
- for (var prop in Doc.prototype) { if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0)
9724
- { CodeMirror.prototype[prop] = (function(method) {
9725
- return function() {return method.apply(this.doc, arguments)}
9726
- })(Doc.prototype[prop]); } }
9727
-
9728
- eventMixin(Doc);
9729
- CodeMirror.inputStyles = {"textarea": TextareaInput, "contenteditable": ContentEditableInput};
9730
-
9731
- // Extra arguments are stored as the mode's dependencies, which is
9732
- // used by (legacy) mechanisms like loadmode.js to automatically
9733
- // load a mode. (Preferred mechanism is the require/define calls.)
9734
- CodeMirror.defineMode = function(name/*, mode, …*/) {
9735
- if (!CodeMirror.defaults.mode && name != "null") { CodeMirror.defaults.mode = name; }
9736
- defineMode.apply(this, arguments);
9737
- };
9738
-
9739
- CodeMirror.defineMIME = defineMIME;
9740
-
9741
- // Minimal default mode.
9742
- CodeMirror.defineMode("null", function () { return ({token: function (stream) { return stream.skipToEnd(); }}); });
9743
- CodeMirror.defineMIME("text/plain", "null");
9744
-
9745
- // EXTENSIONS
9746
-
9747
- CodeMirror.defineExtension = function (name, func) {
9748
- CodeMirror.prototype[name] = func;
9749
- };
9750
- CodeMirror.defineDocExtension = function (name, func) {
9751
- Doc.prototype[name] = func;
9752
- };
9753
-
9754
- CodeMirror.fromTextArea = fromTextArea;
9755
-
9756
- addLegacyProps(CodeMirror);
9757
-
9758
- CodeMirror.version = "5.48.0";
9759
-
9760
- return CodeMirror;
9761
-
9762
- })));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
readme.txt CHANGED
@@ -5,7 +5,7 @@ Tags: google maps, maps, map, map markers, google map, google maps plugin, wp go
5
  Requires at least: 3.5
6
  Tested up to: 5.8
7
  Requires PHP: 5.3
8
- Stable tag: trunk
9
  License: GPLv2
10
 
11
  The easiest to use Google maps plugin! Create a custom Google map, store locator or map widget with high quality markers containing categories, descriptions, images and links.
@@ -199,6 +199,9 @@ To add your map to your widgets area, simply go to Appearance->Widgets and drag
199
 
200
  == Upgrade Notice ==
201
 
 
 
 
202
  = 8.1.16 =
203
  Please update to 8.1.16 or above to ensure plugin stability.
204
 
@@ -222,6 +225,15 @@ Please update your WP Google Maps version to 6.3.15 to ensure you are using the
222
 
223
  == Changelog ==
224
 
 
 
 
 
 
 
 
 
 
225
  = 8.1.17 - 2021-10-13 =
226
  * Improved marker editor geocode usage to only geocode when an address has changed, or is being added for the first time. (Reduced API calls due to usage)
227
  * Fixed issue where editing a marker which has already been position adusted would trigger a geocode on the original address, moving the marker back to the original placement
@@ -1709,23 +1721,3 @@ For more, please view the WP Google Maps site
1709
 
1710
 
1711
 
1712
-
1713
-
1714
-
1715
-
1716
-
1717
-
1718
-
1719
-
1720
-
1721
-
1722
-
1723
-
1724
-
1725
-
1726
-
1727
-
1728
-
1729
-
1730
-
1731
-
5
  Requires at least: 3.5
6
  Tested up to: 5.8
7
  Requires PHP: 5.3
8
+ Stable tag: 8.1.18
9
  License: GPLv2
10
 
11
  The easiest to use Google maps plugin! Create a custom Google map, store locator or map widget with high quality markers containing categories, descriptions, images and links.
199
 
200
  == Upgrade Notice ==
201
 
202
+ = 8.1.18 =
203
+ Please update to 8.1.18 or above to ensure you are using the latest security enhancements.
204
+
205
  = 8.1.16 =
206
  Please update to 8.1.16 or above to ensure plugin stability.
207
 
225
 
226
  == Changelog ==
227
 
228
+ = 8.1.18 - 2021-11-02 =
229
+ * Improved sanitization, validation and escaping. Improving user editable content management, trace calls, and temporary variables
230
+ * Improved sanitization, validation and escaping on legacy code base. Code largely unused but has been addressed for additional security
231
+ * Removed polygons labels settings placeholder when in OpenLayers engine, this is not supported by OpenLayers presently
232
+ * Removed legacy code which loaded internal version of CodeMirror
233
+ * Removed legacy code which allowed manual jQuery version to be loaded. This has been disabled for some time, however, code is now fully deprecated
234
+ * Deprecated some legacy functionality
235
+ * Updated stable tag to reflect version number correctly
236
+
237
  = 8.1.17 - 2021-10-13 =
238
  * Improved marker editor geocode usage to only geocode when an address has changed, or is being added for the first time. (Reduced API calls due to usage)
239
  * Fixed issue where editing a marker which has already been position adusted would trigger a geocode on the original address, moving the marker back to the original placement
1721
 
1722
 
1723
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
wpGoogleMaps.php CHANGED
@@ -3,7 +3,7 @@
3
  Plugin Name: WP Google Maps
4
  Plugin URI: https://www.wpgmaps.com
5
  Description: The easiest to use Google Maps plugin! Create custom Google Maps with high quality markers containing locations, descriptions, images and links. Add your customized map to your WordPress posts and/or pages quickly and easily with the supplied shortcode. No fuss.
6
- Version: 8.1.17
7
  Author: WP Google Maps
8
  Author URI: https://www.wpgmaps.com
9
  Text Domain: wp-google-maps
@@ -12,6 +12,15 @@ Domain Path: /languages
12
 
13
 
14
  /*
 
 
 
 
 
 
 
 
 
15
  * 8.1.17 - 2021-10-18
16
  * Improved marker editor geocode usage to only geocode when an address has changed, or is being added for the first time. (Reduced API calls due to usage)
17
  * Fixed issue where editing a marker which has already been position adusted would trigger a geocode on the original address, moving the marker back to the original placement
3
  Plugin Name: WP Google Maps
4
  Plugin URI: https://www.wpgmaps.com
5
  Description: The easiest to use Google Maps plugin! Create custom Google Maps with high quality markers containing locations, descriptions, images and links. Add your customized map to your WordPress posts and/or pages quickly and easily with the supplied shortcode. No fuss.
6
+ Version: 8.1.18
7
  Author: WP Google Maps
8
  Author URI: https://www.wpgmaps.com
9
  Text Domain: wp-google-maps
12
 
13
 
14
  /*
15
+ * 8.1.18 - 2021-11-02
16
+ * Improved sanitization, validation and escaping. Improving user editable content management, trace calls, and temporary variables
17
+ * Improved sanitization, validation and escaping on legacy code base. Code largely unused but has been addressed for additional security
18
+ * Removed polygons labels settings placeholder when in OpenLayers engine, this is not supported by OpenLayers presently
19
+ * Removed legacy code which loaded internal version of CodeMirror
20
+ * Removed legacy code which allowed manual jQuery version to be loaded. This has been disabled for some time, however, code is now fully deprecated
21
+ * Deprecated some legacy functionality
22
+ * Updated stable tag to reflect version number correctly
23
+ *
24
  * 8.1.17 - 2021-10-18
25
  * Improved marker editor geocode usage to only geocode when an address has changed, or is being added for the first time. (Reduced API calls due to usage)
26
  * Fixed issue where editing a marker which has already been position adusted would trigger a geocode on the original address, moving the marker back to the original placement